Files
imgui-luau/build/ImGui.luau
T

80917 lines
4.6 MiB
Plaintext

--!optimize 2
-- GENERATED by tools/bundle.py from *.lua, backends/, fonts/ - do not edit
local ImGui, ImVec2, ImVec4, ImRect, HelpMarker, ImVec2_Copy, IMGUI_DEMO_MARKER, ImGuiTableFlags, ImStd, ImVector, ImDrawFlags, ImGuiInputFlags, ImRect_Copy, ImStb, ImGuiDataType, IM_TRUNC, ImGuiKey, ImGuiStyleVarInfo, ImVec2_CopyV, IM_ASSERT_USER_ERROR, ImGuiHoveredFlags, ImFormatString, ImHashStr, IM_ASSERT_USER_ERROR_RET, ImIsPowerOfTwo, ImS32, ImGuiColorEditFlags, ImVec4_Copy, ImAbs, ExampleAppConsoleMT, IM_ROUND, ImGuiTableColumnFlags, ImGuiTreeNodeFlags, PopStyleCompact, PushStyleCompact, IM_ASSERT_PARANOID, ImU16, ExampleNames, ImGuiWindowFlags, ImGuiItemFlags, ImGui_ImplRoblox_GetBackendData, stbte, ImColor, ImCos, ImGuiButtonFlags, ImGuiDebugLogFlags, ImGuiListClipper, ImGuiLocKey, ImU32, ExampleTreeFuncs, ImS16, _PathArcToFastExXY, ImGuiMultiSelectFlags, ImSin, ImGuiTabBarFlags, ImLengthSqr, ImUpperPowerOfTwo, IM_F32_TO_INT8_SAT, ImFontAtlasPackGetRect, DockSettingsRenameNodeReferences, ExampleDualListBoxMT, IM_DRAWLIST_TEX_LINES_WIDTH_MAX, ImCeil, ImFabs, ImGuiCol, ImGuiSelectionBasicStorage, ImS8, ImU8, ExampleAppPropertyEditorMT, ImGuiChildFlags, ImGuiDockNodeFlags, ImGuiLastItemData_Copy, IMGUI_DEBUG_LOG_IO, ImGuiScrollFlags, ImLog, ImMemchr, ImStrByte, ExampleSelectionWithDeletionMT, IMGUI_DEBUG_LOG_VIEWPORT, ImFontCalcWordWrapPositionEx, ImGuiInputEvent, ImGuiPackedDate, ImGuiTextBuffer, ImHashData, ImPow, ImTrunc64, CustomConstraints, ExampleAppLogMT, IMGUI_DEBUG_LOG_FONT, IMGUI_DEBUG_LOG_SELECTION, IMGUI_DISABLE_DEBUG_TOOLS, ImAddClampOverflow, ImFontAtlasBuildInit, ImFontAtlasFontDestroyOutput, ImGuiComboFlags, ImGuiLastItemData, ImGuiPayload_IsDataType, ImGuiPopupFlags, ImGuiSliderFlags, ImGuiTextFilter, ImRotate, ImSqrt, ImSubClampOverflow, ImTextClassifierSetCharClass, ImTextureRef, MyDocument, ShowExampleMenuFile, CalcCornerArcStepAndCount, CalcNextScrollFromScrollTargetAndClamp, IMGUI_PAYLOAD_TYPE_WINDOW, IMGUI_VERSION, ImCharIsBlankW, ImCharIsSeparatorW, ImFmod, ImFontAtlasBakedAddFontGlyph, ImFontAtlasPackAddRect, ImFontBaked_BuildLoadGlyph, ImFontConfig, ImGuiInputTextFlags, ImGuiLogFlags, ImGuiNavMoveFlags, ImGuiStyle_Copy, ImVec1_Copy, ExampleAsset_s_current_sort_specs, ExampleMemberInfo, ExampleTree_CreateNode, IMGUI_PAYLOAD_TYPE_COLOR_4F, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC, IM_F32_TO_INT8_UNBOUND, IM_PI, ImCharIsBlankA, ImFontAtlasBuildNotifySetFont, ImFontAtlasBuildRenderBitmapFromString, ImFontAtlasFontDestroySourceData, ImFontAtlasFontDiscardBakes, ImFontAtlasFontSourceAddToFont, ImFontAtlasFontSourceInit, ImFontAtlasRectId_GetIndex, ImFontAtlasTextureAdd, ImFontAtlasTextureBlockQueueUpload, ImFontAtlasTextureGrow, ImFontBaked_BuildGrowIndex, ImFontGlyph, ImGuiTableColumn, ImGuiWindowClass, ImGui_ImplRoblox_RenderDrawData, ImParseFormatFindStart, ImRect_CopyFromV4, ImTextureRect, MyItem_s_current_sort_specs, stb_textedit_key, stbtt_GetGlyphBitmapBoxSubpixel, DockSettingsHandler_ClearAll, EditTableSizingFlags, ExampleSelectionWithDeletion, IMGUI_DEBUG_LOG_TABLE, IMGUI_PAYLOAD_TYPE_COLOR_3F, IM_FREE, ImDrawCmd, ImDrawList
local type, mathMin, mathMax, mathFloor, mathCeil, bitOr, bitLShift = type, math.min, math.max, math.floor, math.ceil, bit32.bor, bit32.lshift
local function IM_COL32(R, G, B, A) return (bitOr(bitLShift(A, 24 --[[IM_COL32_A_SHIFT]]), bitLShift(B, 16 --[[IM_COL32_B_SHIFT]]), bitLShift(G, 8 --[[IM_COL32_G_SHIFT]]), bitLShift(R, 0 --[[IM_COL32_R_SHIFT]]))) end
@native
local function ImMin(lhs, rhs)
if type(lhs) == "number" then return mathMin(lhs, rhs) end
return ImVec2(mathMin(lhs.x, rhs.x), mathMin(lhs.y, rhs.y))
end
@native
local function ImMax(lhs, rhs)
if type(lhs) == "number" then return mathMax(lhs, rhs) end
return ImVec2(mathMax(lhs.x, rhs.x), mathMax(lhs.y, rhs.y))
end
@native
local function ImClamp(v, mn, mx)
if type(v) == "number" and type(mn) == "number" and type(mx) == "number" then return mathMin(mathMax(v, mn), mx)
elseif type(v) == "table" and type(mn) == "table" and type(mx) == "table" then return ImVec2(mathMax(mn.x, mathMin(v.x, mx.x)), mathMax(mn.y, mathMin(v.y, mx.y)))
end
end
@native
local function ImLerp(a, b, t)
if type(a) == "number" and type(b) == "number" and type(t) == "number" then return ((a) + ((b) - (a)) * (t))
elseif type(a) == "table" and type(b) == "table" and type(t) == "number" then return ImVec2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t)
elseif type(a) == "table" and type(b) == "table" and type(t) == "table" then return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y)
end
end
local function _Trunc(val) if val >= 0 then return mathFloor(val) else return mathCeil(val) end end
local function ImTrunc(v)
if type(v) == "number" then return _Trunc(v)
elseif type(v) == "table" then return ImVec2(_Trunc(v.x), _Trunc(v.y))
end
end
local function ImFloor(f) return math.floor(f) end
local function ImSaturate(f) return ((f < 0.0 and 0.0) or (f > 1.0 and 1.0) or f) end
local __FILES, __LOADED = {}, {}
function IM_INCLUDE(name)
local r = __LOADED[name]
if r == nil then r = __FILES[name](); if r == nil then r = true end; __LOADED[name] = r end
return r
end
__FILES['imgui.lua'] = function()
-- dear imgui, v1.93 WIP (Luau port)
-- (main code and documentation)
-- This file is a Lua/Luau port of Dear ImGui's imgui.cpp (docking branch, 1.93 WIP), following imgui.cpp function by function.
-- It runs on Roblox (Luau, primary target) and LÖVE (LuaJIT). Upstream documentation, examples and the demo apply almost directly.
-- Help:
-- - Call and read ImGui.ShowDemoWindow() in imgui_demo.lua. All applications in examples/ are doing that.
-- - Read top of imgui.lua for more details, links and comments.
-- - ImVec2 and ImVec4 have arithmetic metamethods (a + b, a * 2.0, etc.); fields are .x, .y (.z, .w).
-- - See README.md "Differences from C++ Dear ImGui" for the Luau conventions (out-params become return values, flags enums, text buffers).
-- Resources:
-- - FAQ ........................ https://dearimgui.com/faq (in upstream repository as docs/FAQ.md)
-- - Homepage ................... https://github.com/ocornut/imgui
-- - Releases & Changelog ....... https://github.com/ocornut/imgui/releases
-- - Gallery .................... https://github.com/ocornut/imgui/issues?q=label%3Agallery (please post your screenshots/video there!)
-- - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there)
-- - Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code)
-- - Third-party Extensions https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more)
-- - Bindings/Backends https://github.com/ocornut/imgui/wiki/Bindings (language bindings + backends for various tech/engines)
-- - Debug Tools https://github.com/ocornut/imgui/wiki/Debug-Tools
-- - Glossary https://github.com/ocornut/imgui/wiki/Glossary
-- - Software using Dear ImGui https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui
-- - Issues & support ........... https://github.com/ocornut/imgui/issues (upstream; for issues specific to this port, use this port's repository)
-- - Test Engine & Automation ... https://github.com/ocornut/imgui_test_engine (test suite, test engine to automate your apps)
-- - Web version of the Demo .... https://pthom.github.io/imgui_explorer (w/ source code browser)
-- Copyright (c) 2014-2026 Omar Cornut
-- Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
-- See LICENSE.txt for copyright and licensing details (standard MIT License).
-- This library is free but needs your support to sustain development and maintenance.
-- Businesses: you can support continued development via B2B invoiced technical support, maintenance and sponsoring contracts.
-- PLEASE reach out at omar AT discohello DOT com. See https://github.com/ocornut/imgui/wiki/Funding
-- Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine.
-- This port is unofficial and not affiliated with the Dear ImGui project; it is distributed under the same license.
-- It is recommended that you don't modify imgui.lua! It will become difficult for you to update the library.
-- Note that 'ImGui' being a plain table, you can add functions into it from your own source files, without
-- modifying imgui.lua. You may use the internal functions and data structures (imgui_internal.lua), but they don't
-- come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
-- to a better solution or official support for them.
--
-- Index of this file:
--
-- DOCUMENTATION
--
-- - MISSION STATEMENT
-- - CONTROLS GUIDE
-- - PROGRAMMER GUIDE
-- - READ FIRST
-- - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
-- - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
-- - HOW A SIMPLE APPLICATION MAY LOOK LIKE
-- - USING CUSTOM BACKEND / CUSTOM ENGINE
-- - API BREAKING CHANGES (read me when you update!)
-- - FREQUENTLY ASKED QUESTIONS (FAQ)
-- - Read all answers online: https://www.dearimgui.com/faq, or in docs/FAQ.md (with a Markdown viewer)
--
-- CODE
-- (search for "[SECTION]" in the code to find them)
--
-- [SECTION] INCLUDES
-- [SECTION] FORWARD DECLARATIONS
-- [SECTION] CONTEXT AND MEMORY ALLOCATORS
-- [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO, ImGuiPlatformIO)
-- [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
-- [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
-- [SECTION] MISC HELPERS/UTILITIES (File functions)
-- [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
-- [SECTION] MISC HELPERS/UTILITIES (Color functions)
-- [SECTION] ImGuiStorage
-- [SECTION] ImGuiTextFilter
-- [SECTION] ImGuiTextBuffer, ImGuiTextIndex
-- [SECTION] ImGuiListClipper
-- [SECTION] STYLING
-- [SECTION] RENDER HELPERS
-- [SECTION] INITIALIZATION, SHUTDOWN
-- [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
-- [SECTION] FONTS, TEXTURES
-- [SECTION] ID STACK
-- [SECTION] INPUTS
-- [SECTION] ERROR CHECKING, STATE RECOVERY
-- [SECTION] ITEM SUBMISSION
-- [SECTION] LAYOUT
-- [SECTION] SCROLLING
-- [SECTION] TOOLTIPS
-- [SECTION] POPUPS
-- [SECTION] WINDOW FOCUS
-- [SECTION] KEYBOARD/GAMEPAD NAVIGATION
-- [SECTION] DRAG AND DROP
-- [SECTION] LOGGING/CAPTURING
-- [SECTION] SETTINGS
-- [SECTION] LOCALIZATION
-- [SECTION] VIEWPORTS, PLATFORM WINDOWS
-- [SECTION] DOCKING
-- [SECTION] PLATFORM DEPENDENT HELPERS
-- [SECTION] METRICS/DEBUGGER WINDOW
-- [SECTION] DEBUG LOG WINDOW
-- [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL)
--
-- (In this port, the sections keep upstream's names but may appear in a different order:
-- e.g. DRAG AND DROP and DOCKING are near the top of imgui.lua. Some are partial or empty, see docs/TODO.txt.)
--
-------------------------------------------------------------------------------
-- DOCUMENTATION
-------------------------------------------------------------------------------
--
-- MISSION STATEMENT
-- =================
--
-- - Easy to use to create code-driven and data-driven tools.
-- - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
-- - Easy to hack and improve.
-- - Minimize setup and maintenance.
-- - Minimize state storage on user side.
-- - Minimize state synchronization.
-- - Portable, minimize dependencies, run on target (consoles, phones, etc.).
-- - Efficient runtime and memory consumption.
--
-- Designed primarily for developers and content-creators, not the typical end-user!
-- Some of the current weaknesses (which we aim to address in the future) includes:
--
-- - Doesn't look fancy by default.
-- - Limited layout features, intricate layouts are typically crafted in code.
--
--
-- CONTROLS GUIDE
-- ==============
--
-- - MOUSE CONTROLS
-- - Mouse wheel: Scroll vertically.
-- - Shift+Mouse wheel: Scroll horizontally.
-- - Click [X]: Close a window, available when an 'open' value is passed to ImGui.Begin() (open = ImGui.Begin(name, open)).
-- - Click ^, Double-Click title: Collapse window.
-- - Drag on corner/border: Resize window (double-click to auto fit window to its contents).
-- - Drag on any empty space: Move window (unless io.ConfigWindowsMoveFromTitleBarOnly = true).
-- - Left-click outside popup: Close popup stack (right-click over underlying popup: Partially close popup stack).
--
-- - TEXT EDITOR
-- - Hold Shift or Drag Mouse: Select text.
-- - Ctrl+Left/Right: Word jump.
-- - Ctrl+Shift+Left/Right: Select words.
-- - Ctrl+A or Double-Click: Select All.
-- - Ctrl+X, Ctrl+C, Ctrl+V: Use OS clipboard (on Roblox, requires ImGui_ImplRoblox.SetTextProxy(true)).
-- - Ctrl+Z Undo.
-- - Ctrl+Y or Ctrl+Shift+Z: Redo.
-- - ESCAPE: Revert text to its original value.
-- - On macOS, controls are automatically adjusted to match standard macOS text editing and behaviors.
-- (for 99% of shortcuts, Ctrl is replaced by Cmd on macOS).
--
-- - KEYBOARD CONTROLS
-- - Basic:
-- - Tab, Shift+Tab Cycle through text editable fields.
-- - Ctrl+Tab, Ctrl+Shift+Tab Cycle through windows.
-- - Ctrl+Click Input text into a Slider or Drag widget.
-- - Extended features with `io.ConfigFlags = bit32.bor(io.ConfigFlags, ImGuiConfigFlags.NavEnableKeyboard)`:
-- - Tab, Shift+Tab: Cycle through every items.
-- - Arrow keys Move through items using directional navigation. Tweak value.
-- - Arrow keys + Alt, Shift Tweak slower, tweak faster (when using arrow keys).
-- - Enter Activate item (prefer text input when possible).
-- - Space Activate item (prefer tweaking with arrows when possible).
-- - Escape Deactivate item, leave child window, close popup.
-- - Page Up, Page Down Previous page, next page.
-- - Home, End Scroll to top, scroll to bottom.
-- - Alt Toggle between scrolling layer and menu layer.
-- - Ctrl+Tab then Ctrl+Arrows Move window. Hold Shift to resize instead of moving.
-- - Menu or Shift+F10 Open context menu.
-- - Output when ImGuiConfigFlags.NavEnableKeyboard set,
-- - io.WantCaptureKeyboard flag is set when keyboard is claimed.
-- - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags.NoNavInputs flag set.
-- - io.NavVisible: true when the navigation cursor is visible (usually goes to back false when mouse is used).
--
-- - GAMEPAD CONTROLS
-- - Enable with 'io.ConfigFlags = bit32.bor(io.ConfigFlags, ImGuiConfigFlags.NavEnableGamepad)'.
-- - Particularly useful to use Dear ImGui on a console system (e.g. PlayStation, Switch, Xbox) without a mouse!
-- - Download controller mapping PNG/PSD at http://dearimgui.com/controls_sheets
-- - Backend support: backend needs to:
-- - Set 'io.BackendFlags = bit32.bor(io.BackendFlags, ImGuiBackendFlags.HasGamepad)' + call io:AddKeyEvent()/io:AddKeyAnalogEvent() with ImGuiKey.GamepadXXX keys.
-- - For analog values (0.0 to 1.0), backend is responsible to handling a dead-zone and rescaling inputs accordingly.
-- Backend code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.).
-- - (The Roblox and LÖVE backends of this port currently handle mouse and keyboard only: gamepad and touch are not wired up yet.)
-- - If you need to share inputs between your game and the Dear ImGui interface, the easiest approach is to go all-or-nothing,
-- with a buttons combo to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
--
-- - REMOTE INPUTS SHARING & MOUSE EMULATION
-- - See https://github.com/ocornut/imgui/wiki/Useful-Extensions#remoting for other remoting solutions.
-- - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the io.ConfigNavMoveSetMousePos flag.
-- Enabling io.ConfigNavMoveSetMousePos + ImGuiBackendFlags.HasSetMousePos instructs Dear ImGui to move your mouse cursor along with navigation movements.
-- When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantSetMousePos' to notify you that it wants the mouse cursor to be moved.
-- When that happens your backend NEEDS to move the OS or underlying mouse cursor on the next frame.
-- (If you set the io.ConfigNavMoveSetMousePos flag but don't honor 'io.WantSetMousePos' properly, Dear ImGui will misbehave as it will see your mouse moving back & forth!)
--
--
-- PROGRAMMER GUIDE
-- ================
--
-- READ FIRST
-- ----------
-- - Remember to check the wonderful Wiki: https://github.com/ocornut/imgui/wiki
-- - Your code creates the UI every frame of your application loop, if your code doesn't run the UI is gone!
-- The UI can be highly dynamic, there are no construction or destruction steps, less superfluous
-- data retention on your side, less state duplication, less state synchronization, fewer bugs.
-- - Call and read ImGui.ShowDemoWindow() for demo code demonstrating most features (imgui_demo.lua, a port of imgui_demo.cpp).
-- Or browse pthom's online imgui_explorer: https://pthom.github.io/imgui_explorer for a web version w/ source code browser.
-- - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
-- You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in Wiki.
-- - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
-- For every application frame, your UI code will be called only once. This is in contrast to e.g. Unity's implementation of an IMGUI,
-- where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
-- - Our origin is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
-- - IM_ASSERT() is used throughout the code, like upstream.
-- If you get an assert, read the messages and comments around the assert.
-- - This codebase aims to be highly optimized:
-- - A typical idle frame should allocate as little as possible (in Luau: avoid creating tables/closures per frame).
-- - We rely on a maximum of constant-time or O(N) algorithms. Limiting searches/scans as much as possible.
-- - This codebase aims to be both highly opinionated and highly flexible:
-- - This code works because of the things it choose to solve or not solve.
-- - The port keeps upstream's structure (ImGui is a table of free functions, structures are plain tables)
-- so that upstream code, comments and documentation map almost 1:1. See docs/PORTING.md for the porting rules.
--
--
-- HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
-- ----------------------------------------------
-- - Update by re-pulling this repository (or updating the Wally package / downloading a newer release),
-- then rebuild the bundle (python3 tools/bundle.py, or --target love) and overwrite every file.
-- - This port tracks upstream; read upstream's "API BREAKING CHANGES" (see below) when the tracked upstream version changes.
-- If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
-- from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
-- likely be a comment about it. Please report any issue to the GitHub page!
-- - Try to keep your copy of Dear ImGui reasonably up to date!
--
--
-- GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
-- ---------------------------------------------------------------
-- - See https://github.com/ocornut/imgui/wiki/Getting-Started, and README.md of this repository.
-- - Run and study the examples and demo in imgui_demo.lua to get acquainted with the library.
-- - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder
-- (backends/imgui_impl_roblox.lua for Roblox, backends/imgui_impl_love.lua for LÖVE).
-- - Build a single file with tools/bundle.py: build/ImGui.luau (Roblox ModuleScript) or build/imgui_love.lua (LÖVE),
-- then require() / load it. All exported names (ImGui, ImVec2, ImVec4, IM_COL32, every ImGui*Flags enum...) are fields of the returned table.
-- - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
-- - Dear ImGui never touches or knows about your GPU state. The only function that knows about rendering is the draw function that you provide (the backend's RenderDrawData).
-- Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
-- phases of your own application. All rendering information is stored into command-lists that you will retrieve after calling ImGui.Render().
-- - Refer to the backends and demo applications in the examples/ folder for instruction on how to setup your code.
--
--
-- HOW A SIMPLE APPLICATION MAY LOOK LIKE
-- --------------------------------------
--
-- USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.lua files from the backends/ folder).
-- The sub-folders in examples/ contain examples applications following this structure.
--
-- Roblox (LocalScript, see examples/example_roblox/main.client.luau):
--
-- local G = require(ReplicatedStorage:WaitForChild("ImGui"))
-- local ImGui, ImGui_ImplRoblox = G.ImGui, G.ImGui_ImplRoblox
--
-- -- Application init: create a dear imgui context, setup some options, load fonts
-- ImGui.CreateContext()
-- local io = ImGui.GetIO()
-- -- TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags = bit32.bor(io.ConfigFlags, G.ImGuiConfigFlags.NavEnableKeyboard)' to enable keyboard controls.
-- -- TODO: Fill optional fields of the io structure later.
-- -- TODO: Load TTF/OTF fonts if you don't want to use the default font.
--
-- -- Initialize helper Platform and Renderer backend (here we are using imgui_impl_roblox.lua)
-- ImGui_ImplRoblox.Init()
--
-- -- Application main loop
-- RunService.Stepped:Connect(function()
-- -- Feed inputs to dear imgui, start new frame
-- ImGui_ImplRoblox.NewFrame()
-- ImGui.NewFrame()
--
-- -- Any application code here
-- ImGui.Text("Hello, world!")
--
-- -- Render dear imgui
-- ImGui.Render()
-- ImGui_ImplRoblox.RenderDrawData(ImGui.GetDrawData())
-- end)
--
-- -- Shutdown
-- ImGui_ImplRoblox.Shutdown()
-- ImGui.DestroyContext()
--
-- LÖVE (see examples/example_love/main.lua):
--
-- function love.load()
-- ImGui.CreateContext()
-- G.ImGui_ImplLove.Init({ root = ROOT })
-- end
-- function love.update(dt)
-- G.ImGui_ImplLove.NewFrame()
-- ImGui.NewFrame()
-- ImGui.Text("Hello, world!")
-- ImGui.Render()
-- end
-- function love.draw()
-- G.ImGui_ImplLove.RenderDrawData(ImGui.GetDrawData())
-- end
-- -- + forward love.mousemoved/mousepressed/mousereleased/wheelmoved/keypressed/keyreleased/textinput/focus
-- -- to G.ImGui_ImplLove.MouseMoved/MousePressed/MouseReleased/WheelMoved/KeyPressed/KeyReleased/TextInput/Focus.
--
-- To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application,
-- you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
-- Please read the FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" about this.
--
--
-- USAGE
-- -----
--
-- Widget functions are called every frame; their return values tell you what the user did.
-- Out-parameters of the C++ API (bool* p_open, float* v, int* v...) are passed by value and the new value is returned:
--
-- ImGui.Text("Hello, world %d", 123)
-- if ImGui.Button("Save") then
-- MySaveFunction()
-- end
-- ImGui.InputText("string", buf, 256) -- buf = ImGui.NewTextBuffer("...", 256), edited in place
-- f = ImGui.SliderFloat("float", f, 0.0, 1.0) -- returns (value, changed)
-- pressed, v = ImGui.Checkbox("checkbox", v)
--
-- -- Create a window called "My First Tool", with a menu bar.
-- my_tool_active = ImGui.Begin("My First Tool", my_tool_active, ImGuiWindowFlags.MenuBar) -- returns (open, visible)
-- if ImGui.BeginMenuBar() then
-- if ImGui.BeginMenu("File") then
-- if ImGui.MenuItem("Close", "Ctrl+W") then my_tool_active = false end
-- ImGui.EndMenu()
-- end
-- ImGui.EndMenuBar()
-- end
-- ImGui.ColorEdit4("Color", my_color) -- ImVec4 / arrays are edited in place
-- ImGui.End()
--
--
-- USING CUSTOM BACKEND / CUSTOM ENGINE
-- ------------------------------------
--
-- A backend implements the usual Dear ImGui contract: Init, NewFrame, RenderDrawData, inputs -> io:AddXXXEvent(),
-- texture updates via ImTextureData. Adding another Lua host means a new backends/imgui_impl_<name>.lua.
-- -> see https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md for basic instructions.
-- -> the backends in backends/imgui_impl_roblox.lua and backends/imgui_impl_love.lua are complete implementations.
--
-- Basic application/backend skeleton:
--
-- -- Application init: create a Dear ImGui context, setup some options, load fonts
-- ImGui.CreateContext()
-- local io = ImGui.GetIO()
-- -- TODO: set io.ConfigXXX values, e.g.
-- io.ConfigFlags = bit32.bor(io.ConfigFlags, ImGuiConfigFlags.NavEnableKeyboard) -- Enable keyboard controls
--
-- -- TODO: Load TTF/OTF fonts if you don't want to use the default font.
-- io.Fonts:AddFontFromFileTTF("fonts/Roboto-Medium.ttf", 16.0)
--
-- -- Application main loop (called once per frame)
-- local function OnFrame(dt)
-- -- Setup low-level inputs, or write to those fields from your input event handlers, etc.
-- -- (In the examples/ app this is usually done within the ImGui_ImplXXX.NewFrame() function from one of the Backends)
-- io.DeltaTime = dt -- set the time elapsed since the previous frame (in seconds)
-- io.DisplaySize.x = 1920.0 -- set the current display width
-- io.DisplaySize.y = 1280.0 -- set the current display height here
-- io:AddMousePosEvent(mouse_x, mouse_y) -- update mouse position
-- io:AddMouseButtonEvent(0, mouse_b0) -- update mouse button states
-- io:AddMouseButtonEvent(1, mouse_b1) -- update mouse button states
--
-- -- Call NewFrame(), after this point you can use ImGui.* functions anytime
-- -- (So you want to try calling NewFrame() as early as you can in your main loop to be able to use Dear ImGui everywhere)
-- ImGui.NewFrame()
--
-- -- Most of your application code here
-- ImGui.Text("Hello, world!")
-- MyGameUpdate() -- may use any Dear ImGui functions, e.g. ImGui.Begin("My window"); ImGui.Text("Hello, world!"); ImGui.End()
-- MyGameRender() -- may use any Dear ImGui functions as well!
--
-- -- End the dear imgui frame
-- -- (You want to try calling EndFrame/Render as late as you can, to be able to use Dear ImGui in your own game rendering code)
-- ImGui.EndFrame() -- this is automatically called by Render(), but available
-- ImGui.Render()
--
-- -- Update textures
-- local draw_data = ImGui.GetDrawData()
-- for _, tex in draw_data.Textures:iter() do
-- if tex.Status ~= ImTextureStatus.OK then
-- MyImGuiBackend_UpdateTexture(tex)
-- end
-- end
--
-- -- Render dear imgui contents
-- MyImGuiBackend_RenderDrawData(draw_data)
-- end
--
-- -- Shutdown
-- ImGui.DestroyContext()
--
--
--
-- API BREAKING CHANGES
-- ====================
--
-- This port tracks upstream Dear ImGui 1.93 WIP (docking branch).
-- For the change-log of API breaking changes, see the top of upstream imgui.cpp, or https://github.com/ocornut/imgui/releases
--
--
-- FREQUENTLY ASKED QUESTIONS (FAQ)
-- ================================
--
-- Read all answers online:
-- https://www.dearimgui.com/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url)
-- For questions specific to this port (Roblox, LÖVE, performance, differences from C++), see the FAQ in README.md of this repository.
--
--- Dear ImGui WIP
-- (Core Code)
--- Flags:
-- IMGUI_DISABLE_OBSOLETE_FUNCTIONS = true -- This flag is not implemented
-- NOTE: you must implement ImStd.ImFileOpen if you are not in GMod
IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS = true -- Don't implement ImStd.ImFileOpen/ImStd.ImFileClose/ImStd.ImFileRead/ImFileWrite so you can implement them yourself
IMGUI_DISABLE_DEBUG_TOOLS = false -- Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty
IMGUI_DISABLE_TIME_FUNCTIONS = false -- Disable setting platform_io.Platform_SessionDate from os.date() in Initialize()
-- Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be ~= nil.
-- - ImGui.CreateContext() will automatically set this pointer if it is nil.
-- Change to a different context by calling ImGui.SetCurrentContext().
--- @type ImGuiContext?
local GImGui = nil
-- [Luau] IM_DEBUG_BREAK() (imgui_internal.h macro): Lua can't break into a debugger. It calls the optional user hook
-- g.DebugBreakCallback(g) instead (a context field added by this port; the library can't be patched from outside the bundle).
local function IM_DEBUG_BREAK()
local g = GImGui
if g ~= nil and g.DebugBreakCallback ~= nil then
g.DebugBreakCallback(g)
end
end
ImGui = {}
ImStd = {} -- Contains functions that originally don't belong to cpp namespaces
-- IM_INCLUDE(name) is provided by the bundle (see tools/bundle.py): runs the bundled file once, returns its result.
function ImGui._GetTypeFunc() return type end
local function LUA_TableConstructorWrapper(T, size, userdata)
local p = {}
for i = 1, size do p[i] = T() end
return p
end
-- Memory Allocator functions. Use SetAllocatorFunctions() to change them.
-- - You probably don't want to modify that mid-program.
local GImAllocatorAllocFunc = LUA_TableConstructorWrapper
local GImAllocatorFreeFunc = function(p0, p1, p2) end
local GImAllocatorUserData = nil
-- IM_ALLOC() == ImGui.MemAlloc()
--- @param constructor function
--- @param size int
function ImGui.MemAlloc(constructor, size)
local p = GImAllocatorAllocFunc(constructor, size, GImAllocatorUserData)
local ctx = GImGui
if ctx then
ImGui.DebugAllocHook(ctx.DebugAllocInfo, ctx.FrameCount, p, size)
end
return p
end
-- IM_FREE() == ImGui.MemFree()
-- This does not set `owner[field]` to nil!
--- @param owner table
--- @param field string|int
function ImGui.MemFree(owner, field)
if owner ~= nil then
local ctx = GImGui
if ctx then
ImGui.DebugAllocHook(ctx.DebugAllocInfo, ctx.FrameCount, owner, -1)
end
end
return GImAllocatorFreeFunc(owner, field, GImAllocatorUserData)
end
-- [Luau] g.DemoMarkerCallback: fun(file, line, section), nil unless set by the user.
--- @param file string
--- @param line int
--- @param section string
function ImGui.DemoMarker(file, line, section)
local g = GImGui
if g.DemoMarkerCallback ~= nil then
g.DemoMarkerCallback(file, line, section)
end
end
-- We record the number of allocation in recent frames, as a way to audit/sanitize our guiding principles of "no allocations on idle/repeating frames"
--- @param info ImGuiDebugAllocInfo
--- @param frame_count int
--- @param ptr table
--- @param size size_t
function ImGui.DebugAllocHook(info, frame_count, ptr, size)
local entry = info.LastEntriesBuf[info.LastEntriesIdx]
if entry.FrameCount ~= frame_count then
info.LastEntriesIdx = info.LastEntriesIdx % (#info.LastEntriesBuf) + 1
entry = info.LastEntriesBuf[info.LastEntriesIdx]
entry.FrameCount = frame_count
entry.AllocCount = 0
entry.FreeCount = 0
end
if size ~= -1 then
-- print(string.format("[%05d] MemAlloc(%d) -> %p", frame_count, size, ptr))
entry.AllocCount = entry.AllocCount + 1
info.TotalAllocCount = info.TotalAllocCount + 1
else
-- print(string.format("[%05d] MemFree(%p)", frame_count, ptr))
entry.FreeCount = entry.FreeCount + 1
info.TotalFreeCount = info.TotalFreeCount + 1
end
end
IM_INCLUDE"imgui_h.lua"
IM_INCLUDE"imgui_internal.lua"
IM_INCLUDE"imgui_draw.lua"
IM_INCLUDE"imgui_widgets.lua"
IM_INCLUDE"imgui_tables.lua"
do --[[ [SECTION] imgui_dragdrop (was imgui_dragdrop.lua) ]]
--- Dear ImGui
-- Drag and drop: 1:1 port of imgui.cpp [SECTION] DRAG AND DROP (docking branch)
-- Payload `Data` is any Lua value (no copy is made). `data_size` is optional and only kept for API parity.
-------------------------------------------------------------------------------
-- [SECTION] DRAG AND DROP
-------------------------------------------------------------------------------
function ImGui.IsDragDropActive()
return ImGui.GetCurrentContext().DragDropActive
end
function ImGui.ClearDragDrop()
local g = ImGui.GetCurrentContext()
g.DragDropActive = false
ImGuiPayload_Clear(g.DragDropPayload)
g.DragDropAcceptFlagsCurr = 0 --[[ImGuiDragDropFlags.None]]
g.DragDropAcceptIdCurr = 0
g.DragDropAcceptIdPrev = 0
g.DragDropAcceptIdCurrRectSurface = 3.4028234663853e+38 --[[FLT_MAX]]
g.DragDropAcceptFrameCount = -1
end
-- When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
-- If the item has an identifier:
-- - This assume/require the item to be activated (typically via ButtonBehavior).
-- - Therefore if you want to use this with a mouse button other than left mouse button, it is up to the item itself to activate with another button.
-- - We then pull and use the mouse button that was used to activate the item and use it to carry on the drag.
-- If the item has no identifier:
-- - Currently always assume left mouse button.
function ImGui.BeginDragDropSource(flags)
if flags == nil then flags = 0 end
local g = ImGui.GetCurrentContext()
local window = g.CurrentWindow
-- FIXME-DRAGDROP: While in the common-most "drag from non-zero active id" case we can tell the mouse button,
-- in both SourceExtern and id==0 cases we may requires something else (explicit flags or some heuristic).
local mouse_button = 0 --[[ImGuiMouseButton.Left]]
local source_drag_active = false
local source_id = 0
local source_parent_id = 0
if bit32.band(flags, 16 --[[ImGuiDragDropFlags.SourceExtern]]) == 0 then
source_id = g.LastItemData.ID
if source_id ~= 0 then
-- Common path: items with ID
if g.ActiveId ~= source_id then return false end
if g.ActiveIdMouseButton ~= -1 then mouse_button = g.ActiveIdMouseButton end
if g.IO.MouseDown[mouse_button] == false or window.SkipItems then return false end
g.ActiveIdAllowOverlap = false
else
-- Uncommon path: items without ID
if g.IO.MouseDown[mouse_button] == false or window.SkipItems then return false end
if bit32.band(g.LastItemData.StatusFlags, 1 --[[ImGuiItemStatusFlags.HoveredRect]]) == 0 and (g.ActiveId == 0 or g.ActiveIdWindow ~= window) then
return false
end
-- If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
-- A) Read the explanation below, B) Use the ImGuiDragDropFlags.SourceAllowNullID flag.
if bit32.band(flags, 8 --[[ImGuiDragDropFlags.SourceAllowNullID]]) == 0 then
assert(false, "Use ImGuiDragDropFlags.SourceAllowNullID for items without an ID")
return false
end
-- Magic fallback to handle items with no assigned ID, e.g. Text(), Image()
-- We build a throwaway ID based on current ID stack + relative AABB of items in window.
-- THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING/RESIZING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
-- We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
-- Rely on keeping other window.LastItemXXX fields intact.
source_id = window:GetIDFromRectangle(g.LastItemData.Rect)
g.LastItemData.ID = source_id
ImGui.KeepAliveID(source_id)
local is_hovered = ImGui.ItemHoverable(g.LastItemData.Rect, source_id, g.LastItemData.ItemFlags)
if is_hovered and g.IO.MouseClicked[mouse_button] then
ImGui.SetActiveID(source_id, window)
ImGui.FocusWindow(window)
end
if g.ActiveId == source_id then g.ActiveIdAllowOverlap = is_hovered end -- Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
end
if g.ActiveId ~= source_id then return false end
source_parent_id = window.IDStack.Data[window.IDStack.Size]
source_drag_active = ImGui.IsMouseDragging(mouse_button)
-- Disable navigation and key inputs while dragging + cancel existing request if any
ImGui.SetActiveIdUsingAllKeyboardKeys()
else
-- When ImGuiDragDropFlags.SourceExtern is set:
window = nil
source_id = ImHashStr("#SourceExtern")
source_drag_active = true
mouse_button = g.IO.MouseDown[0] and 0 or -1
ImGui.KeepAliveID(source_id)
ImGui.SetActiveID(source_id, nil)
end
assert(g.DragDropWithinTarget == false) -- Can't nest BeginDragDropSource() and BeginDragDropTarget()
if not source_drag_active then return false end
-- Activate drag and drop
if not g.DragDropActive then
assert(source_id ~= 0)
ImGui.ClearDragDrop()
local payload = g.DragDropPayload
payload.SourceId = source_id
payload.SourceParentId = source_parent_id
g.DragDropActive = true
g.DragDropSourceFlags = flags
g.DragDropMouseButton = mouse_button
if payload.SourceId == g.ActiveId then g.ActiveIdNoClearOnFocusLoss = true end
end
g.DragDropSourceFrameCount = g.FrameCount
g.DragDropWithinSource = true
if bit32.band(flags, 1 --[[ImGuiDragDropFlags.SourceNoPreviewTooltip]]) == 0 then
-- Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
-- We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
local ret
if g.DragDropAcceptIdPrev ~= 0 and bit32.band(g.DragDropAcceptFlagsPrev, 4096 --[[ImGuiDragDropFlags.AcceptNoPreviewTooltip]]) ~= 0 then
ret = ImGui.BeginTooltipHidden()
else
ret = ImGui.BeginTooltip()
end
assert(ret) -- FIXME-NEWBEGIN: If this ever becomes false, we need to Begin("##Hidden", nil, ImGuiWindowFlags.NoSavedSettings) + SetWindowHiddenAndSkipItemsForCurrentFrame().
end
if bit32.band(flags, 2 --[[ImGuiDragDropFlags.SourceNoDisableHover]]) == 0 and bit32.band(flags, 16 --[[ImGuiDragDropFlags.SourceExtern]]) == 0 then
g.LastItemData.StatusFlags = bit32.band(g.LastItemData.StatusFlags, bit32.bnot(1 --[[ImGuiItemStatusFlags.HoveredRect]]))
end
return true
end
function ImGui.EndDragDropSource()
local g = ImGui.GetCurrentContext()
assert(g.DragDropActive)
assert(g.DragDropWithinSource, "Not after a BeginDragDropSource()?")
if bit32.band(g.DragDropSourceFlags, 1 --[[ImGuiDragDropFlags.SourceNoPreviewTooltip]]) == 0 then
ImGui.EndTooltip()
end
-- Discard the drag if have not called SetDragDropPayload()
if g.DragDropPayload.DataFrameCount == -1 then ImGui.ClearDragDrop() end
g.DragDropWithinSource = false
end
-- Use 'cond' to choose to submit payload on drag start or every frame
--- @param data any # any Lua value
--- @return bool accepted
function ImGui.SetDragDropPayload(type, data, data_size, cond)
local g = ImGui.GetCurrentContext()
local payload = g.DragDropPayload
if cond == nil or cond == 0 then cond = 1 --[[ImGuiCond.Always]] end
assert(type ~= nil)
assert(#type < 32, "Payload type can be at most 32 characters long")
assert(cond == 1 --[[ImGuiCond.Always]] or cond == 2 --[[ImGuiCond.Once]])
assert(payload.SourceId ~= 0) -- Not called between BeginDragDropSource() and EndDragDropSource()
if cond == 1 --[[ImGuiCond.Always]] or payload.DataFrameCount == -1 then
-- Copy payload
payload.DataType = type
payload.Data = data
payload.DataSize = data_size or ((data ~= nil) and 1 or 0)
end
payload.DataFrameCount = g.FrameCount
-- Return whether the payload has been accepted
return (g.DragDropAcceptFrameCount == g.FrameCount) or (g.DragDropAcceptFrameCount == g.FrameCount - 1)
end
function ImGui.BeginDragDropTargetCustom(bb, id)
local g = ImGui.GetCurrentContext()
if not g.DragDropActive then return false end
local window = g.CurrentWindow
local hovered_window = g.HoveredWindowUnderMovingWindow
if hovered_window == nil or window.RootWindowDockTree ~= hovered_window.RootWindowDockTree then return false end
assert(id ~= 0)
if not ImGui.IsMouseHoveringRect(bb.Min, bb.Max) or (id == g.DragDropPayload.SourceId) then return false end
if window.SkipItems then return false end
assert(g.DragDropWithinTarget == false and g.DragDropWithinSource == false) -- Can't nest BeginDragDropSource() and BeginDragDropTarget()
g.DragDropTargetRect = ImRect(bb.Min, bb.Max)
g.DragDropTargetClipRect = ImRect(window.ClipRect.Min, window.ClipRect.Max) -- May want to be overridden by user depending on use case?
g.DragDropTargetId = id
g.DragDropTargetFullViewport = 0
g.DragDropWithinTarget = true
return true
end
-- Typical usage would be:
-- if not ImGui.IsWindowHovered(bit32.bor(ImGuiHoveredFlags.AnyWindow, ImGuiHoveredFlags.AllowWhenBlockedByActiveItem)) then
-- if ImGui.BeginDragDropTargetViewport(ImGui.GetMainViewport(), nil) then
-- But we are leaving the hover test to the caller for maximum flexibility.
function ImGui.BeginDragDropTargetViewport(viewport, p_bb)
local g = ImGui.GetCurrentContext()
if not g.DragDropActive then return false end
local bb = p_bb or viewport:GetWorkRect()
local id = viewport.ID
if g.MouseViewport ~= viewport or not ImGui.IsMouseHoveringRect(bb.Min, bb.Max, false) or (id == g.DragDropPayload.SourceId) then
return false
end
assert(g.DragDropWithinTarget == false and g.DragDropWithinSource == false) -- Can't nest BeginDragDropSource() and BeginDragDropTarget()
g.DragDropTargetRect = ImRect(bb.Min, bb.Max)
g.DragDropTargetClipRect = ImRect(bb.Min, bb.Max)
g.DragDropTargetId = id
g.DragDropTargetFullViewport = id
g.DragDropWithinTarget = true
return true
end
-- We don't use BeginDragDropTargetCustom() and duplicate its code because:
-- 1) we use LastItemData's ImGuiItemStatusFlags.HoveredRect which handles items that push a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
-- 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
-- Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
function ImGui.BeginDragDropTarget()
local g = ImGui.GetCurrentContext()
if not g.DragDropActive then return false end
local window = g.CurrentWindow
if bit32.band(g.LastItemData.StatusFlags, 1 --[[ImGuiItemStatusFlags.HoveredRect]]) == 0 then return false end
local hovered_window = g.HoveredWindowUnderMovingWindow
if hovered_window == nil or window.RootWindowDockTree ~= hovered_window.RootWindowDockTree or window.SkipItems then return false end
local display_rect = (bit32.band(g.LastItemData.StatusFlags, 2 --[[ImGuiItemStatusFlags.HasDisplayRect]]) ~= 0) and g.LastItemData.DisplayRect or g.LastItemData.Rect
local id = g.LastItemData.ID
if id == 0 then
id = window:GetIDFromRectangle(display_rect)
ImGui.KeepAliveID(id)
end
if g.DragDropPayload.SourceId == id then return false end
assert(g.DragDropWithinTarget == false and g.DragDropWithinSource == false) -- Can't nest BeginDragDropSource() and BeginDragDropTarget()
g.DragDropTargetRect = ImRect(display_rect.Min, display_rect.Max)
local clip = (bit32.band(g.LastItemData.StatusFlags, 512 --[[ImGuiItemStatusFlags.HasClipRect]]) ~= 0) and g.LastItemData.ClipRect or window.ClipRect
g.DragDropTargetClipRect = ImRect(clip.Min, clip.Max)
g.DragDropTargetId = id
g.DragDropWithinTarget = true
return true
end
function ImGui.IsDragDropPayloadBeingAccepted()
local g = ImGui.GetCurrentContext()
return g.DragDropActive and g.DragDropAcceptIdPrev ~= 0
end
--- @return ImGuiPayload?
function ImGui.AcceptDragDropPayload(type, flags)
if flags == nil then flags = 0 end
local g = ImGui.GetCurrentContext()
local payload = g.DragDropPayload
assert(g.DragDropActive) -- Not called between BeginDragDropTarget() and EndDragDropTarget() ?
assert(payload.DataFrameCount ~= -1) -- Forgot to call EndDragDropTarget() ?
if type ~= nil and not ImGuiPayload_IsDataType(payload, type) then return nil end
-- Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
-- NB: We currently accept nil id as target. However, overlapping targets requires a unique ID to function!
local was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId)
local r = g.DragDropTargetRect
local r_surface = r:GetWidth() * r:GetHeight()
if r_surface > g.DragDropAcceptIdCurrRectSurface then return nil end
g.DragDropAcceptFlagsCurr = flags
g.DragDropAcceptIdCurr = g.DragDropTargetId
g.DragDropAcceptIdCurrRectSurface = r_surface
-- IMGUI_DEBUG_LOG("AcceptDragDropPayload(): %08X: accept\n", g.DragDropTargetId)
-- Render default drop visuals
payload.Preview = was_accepted_previously
flags = bit32.bor(flags, bit32.band(g.DragDropSourceFlags, 2048 --[[ImGuiDragDropFlags.AcceptNoDrawDefaultRect]])) -- Source can also inhibit the preview (useful for external sources that live for 1 frame)
local draw_target_rect = payload.Preview and bit32.band(flags, 2048 --[[ImGuiDragDropFlags.AcceptNoDrawDefaultRect]]) == 0
if draw_target_rect and g.DragDropTargetFullViewport ~= 0 then
ImGui.RenderDragDropTargetRectForViewport(g.DragDropTargetFullViewport, g.DragDropTargetRect)
elseif draw_target_rect then
ImGui.RenderDragDropTargetRectForItem(r)
end
g.DragDropAcceptFrameCount = g.FrameCount
if bit32.band(g.DragDropSourceFlags, 16 --[[ImGuiDragDropFlags.SourceExtern]]) ~= 0 and g.DragDropMouseButton == -1 then
payload.Delivery = was_accepted_previously and (g.DragDropSourceFrameCount < g.FrameCount)
else
payload.Delivery = was_accepted_previously and not ImGui.IsMouseDown(g.DragDropMouseButton) -- For extern drag sources affecting OS window focus, it's easier to just test not IsMouseDown() instead of IsMouseReleased()
end
if not payload.Delivery and bit32.band(flags, 1024 --[[ImGuiDragDropFlags.AcceptBeforeDelivery]]) == 0 then return nil end
return payload
end
-- FIXME-STYLE FIXME-DRAGDROP: Settle on a proper default visuals for drop target.
function ImGui.RenderDragDropTargetRectForItem(bb)
local g = ImGui.GetCurrentContext()
local window = g.CurrentWindow
local bb_display = ImRect(bb.Min, bb.Max)
bb_display:ClipWith(g.DragDropTargetClipRect) -- Clip THEN expand so we have a way to visualize that target is not entirely visible.
bb_display:Expand(g.Style.DragDropTargetPadding)
local push_clip_rect = not window.ClipRect:Contains(bb_display)
if push_clip_rect then window.DrawList:PushClipRectFullScreen() end
ImGui.RenderDragDropTargetRectEx(window.DrawList, bb_display, g.Style.DragDropTargetRounding)
if push_clip_rect then window.DrawList:PopClipRect() end
end
function ImGui.RenderDragDropTargetRectForViewport(viewport_id, bb)
local g = ImGui.GetCurrentContext()
local viewport = ImGui.FindViewportByID(viewport_id)
assert(viewport ~= nil)
local bb_padded = ImRect(bb.Min, bb.Max)
bb_padded:Expand(-g.Style.DragDropTargetPadding)
ImGui.RenderDragDropTargetRectEx(ImGui.GetForegroundDrawList(viewport), bb_padded, g.Style.DragDropTargetRounding)
end
function ImGui.RenderDragDropTargetRectEx(draw_list, bb, rounding)
local g = ImGui.GetCurrentContext()
draw_list:AddRectFilled(bb.Min, bb.Max, ImGui.GetColorU32(57 --[[ImGuiCol.DragDropTargetBg]]), rounding, 0)
draw_list:AddRect(bb.Min, bb.Max, ImGui.GetColorU32(56 --[[ImGuiCol.DragDropTarget]]), rounding, g.Style.DragDropTargetBorderSize)
end
--- @return ImGuiPayload?
function ImGui.GetDragDropPayload()
local g = ImGui.GetCurrentContext()
return (g.DragDropActive and g.DragDropPayload.DataFrameCount ~= -1) and g.DragDropPayload or nil
end
function ImGui.EndDragDropTarget()
local g = ImGui.GetCurrentContext()
assert(g.DragDropActive)
assert(g.DragDropWithinTarget)
g.DragDropWithinTarget = false
-- Clear drag and drop state payload right after delivery
if g.DragDropPayload.Delivery then ImGui.ClearDragDrop() end
end
-- Payload methods (C++ ImGuiPayload members): payload:IsDataType(t) etc. also work through these globals
function ImGui.PayloadIsDataType(payload, t) return ImGuiPayload_IsDataType(payload, t) end
end --[[ [SECTION] imgui_dragdrop (was imgui_dragdrop.lua) ]]
do --[[ [SECTION] imgui_docking (was imgui_docking.lua) ]]
--- Dear ImGui
-- Docking: 1:1 port of imgui.cpp [SECTION] DOCKING (docking branch, 1.93 WIP)
--
-- Port notes:
-- - Main viewport only (no multi-viewports on Roblox): viewport creation/merging code paths are kept where cheap, but
-- windows always live in the main viewport.
-- - ImGuiDockContext.Nodes (ImGuiStorage in C++) is a Lua table ID -> node. Iterations that upstream does over the
-- sorted storage are done over a sorted snapshot of the IDs (DockContextNodesSorted) for deterministic order.
-- - node.ChildNodes is a table indexed [0] and [1] like upstream.
-- - ImGuiDockPreviewData.DropRectsDraw is indexed [dir + 1] with dir in -1..3 (so keys 0..4), like upstream.
-------------------------------------------------------------------------------
-- [SECTION] DOCKING
-------------------------------------------------------------------------------
-- Docking: Internal Types
-- Docking: Forward Declarations
-- Docking: ImGuiDockContext
-- Docking: ImGuiDockContext Docking/Undocking functions
-- Docking: ImGuiDockNode
-- Docking: ImGuiDockNode Tree manipulation functions
-- Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport)
-- Docking: Builder Functions
-- Docking: Begin/End Support Functions (called from Begin/End)
-- Docking: Settings
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- Typical Docking call flow: (root level is generally public API):
-------------------------------------------------------------------------------
-- - NewFrame() new dear imgui frame
-- | DockContextNewFrameUpdateUndocking() - process queued undocking requests
-- | - DockContextProcessUndockWindow() - process one window undocking request
-- | - DockContextProcessUndockNode() - process one whole node undocking request
-- | DockContextNewFrameUpdateUndocking() - process queue docking requests, create floating dock nodes
-- | - update g.HoveredDockNode - [debug] update node hovered by mouse
-- | - DockContextProcessDock() - process one docking request
-- | - DockNodeUpdate()
-- | - DockNodeUpdateForRootNode()
-- | - DockNodeUpdateFlagsAndCollapse()
-- | - DockNodeFindInfo()
-- | - destroy unused node or tab bar
-- | - create dock node host window
-- | - Begin() etc.
-- | - DockNodeStartMouseMovingWindow()
-- | - DockNodeTreeUpdatePosSize()
-- | - DockNodeTreeUpdateSplitter()
-- | - draw node background
-- | - DockNodeUpdateTabBar() - create/update tab bar for a docking node
-- | - DockNodeAddTabBar()
-- | - DockNodeWindowMenuUpdate()
-- | - DockNodeCalcTabBarLayout()
-- | - BeginTabBarEx()
-- | - TabItemEx() calls
-- | - EndTabBar()
-- | - BeginDockableDragDropTarget()
-- | - DockNodeUpdate() - recurse into child nodes...
-------------------------------------------------------------------------------
-- - DockSpace() user submit a dockspace into a window
-- | Begin(Child) - create a child window
-- | DockNodeUpdate() - call main dock node update function
-- | End(Child)
-- | ItemSize()
-------------------------------------------------------------------------------
-- - Begin()
-- | BeginDocked()
-- | BeginDockableDragDropSource()
-- | BeginDockableDragDropTarget()
-- | - DockNodePreviewDockRender()
-------------------------------------------------------------------------------
-- - EndFrame()
-- | DockContextEndFrame()
-------------------------------------------------------------------------------
local MT = ImGui.GetMetatables()
IMGUI_PAYLOAD_TYPE_WINDOW = "_IMWINDOW" -- Payload == ImGuiWindow
local DOCKING_HOST_DRAW_CHANNEL_BG = 0 -- Dock host: background fill
local DOCKING_HOST_DRAW_CHANNEL_FG = 1 -- Dock host: decorations and contents
local DOCKING_SPLITTER_SIZE = 2.0
-------------------------------------------------------------------------------
-- Docking: flags & enums
-------------------------------------------------------------------------------
-- Flags for ImGui.DockSpace(), shared/inherited by child nodes.
-- (Some flags can be applied to individual nodes directly)
-- FIXME-DOCK: Also see ImGuiDockNodeFlagsPrivate_ which may involve using the WIP and internal DockBuilder api.
ImGuiDockNodeFlags = {
None = 0,
KeepAliveOnly = bit32.lshift(1, 0), -- -- Don't display the dockspace node but keep it alive. Windows docked into this dockspace node won't be undocked.
-- NoCentralNode = bit32.lshift(1, 1), -- -- Disable Central Node (the node which can stay empty)
NoDockingOverCentralNode = bit32.lshift(1, 2), -- -- Disable docking over the Central Node, which will be always kept empty.
PassthruCentralNode = bit32.lshift(1, 3), -- -- Enable passthru dockspace: 1) DockSpace() will render a ImGuiCol.WindowBg background covering everything excepted the Central Node when empty. Meaning the host window should probably use SetNextWindowBgAlpha(0.0) prior to Begin() when using this. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. See demo for details.
NoDockingSplit = bit32.lshift(1, 4), -- -- Disable other windows/nodes from splitting this node.
NoResize = bit32.lshift(1, 5), -- Saved -- Disable resizing node using the splitter/separators. Useful with programmatically setup dockspaces.
AutoHideTabBar = bit32.lshift(1, 6), -- -- Tab bar will automatically hide when there is a single window in the dock node.
NoUndocking = bit32.lshift(1, 7), -- -- Disable undocking this node.
-- Extend ImGuiDockNodeFlags_ (ImGuiDockNodeFlagsPrivate_)
-- [Internal]
DockSpace = bit32.lshift(1, 10), -- Saved -- A dockspace is a node that occupy space within an existing user window. Otherwise the node is floating and create its own window.
CentralNode = bit32.lshift(1, 11), -- Saved -- The central node has 2 main properties: stay visible when empty, only use "remaining" spaces from its neighbor.
NoTabBar = bit32.lshift(1, 12), -- Saved -- Tab bar is completely unavailable. No triangle in the corner to enable it back.
HiddenTabBar = bit32.lshift(1, 13), -- Saved -- Tab bar is hidden, with a triangle in the corner to show it again (NB: actual tab-bar instance may be destroyed as this is only used for single-window tab bar)
NoWindowMenuButton = bit32.lshift(1, 14), -- Saved -- Disable window/docking menu (that one that appears instead of the collapse button)
NoCloseButton = bit32.lshift(1, 15), -- Saved -- Disable close button
NoResizeX = bit32.lshift(1, 16), -- --
NoResizeY = bit32.lshift(1, 17), -- --
DockedWindowsInFocusRoute = bit32.lshift(1, 18), -- -- Any docked window will be automatically be focus-route chained (window.ParentWindowForFocusRoute set to this) so Shortcut() in this window can run when any docked window is focused.
-- Disable docking/undocking actions in this dockspace or individual node (existing docked nodes will be preserved)
-- Those are not exposed in public because the desirable sharing/inheriting/copy-flag-on-split behaviors are quite difficult to design and understand.
-- The two public flags ImGuiDockNodeFlags.NoDockingOverCentralNode/ImGuiDockNodeFlags.NoDockingSplit don't have those issues.
NoDockingSplitOther = bit32.lshift(1, 19), -- -- Disable this node from splitting other windows/nodes.
NoDockingOverMe = bit32.lshift(1, 20), -- -- Disable other windows/nodes from being docked over this node.
NoDockingOverOther = bit32.lshift(1, 21), -- -- Disable this node from being docked over another window or non-empty node.
NoDockingOverEmpty = bit32.lshift(1, 22), -- -- Disable this node from being docked over an empty node (e.g. DockSpace with no other windows)
}
do
local F = ImGuiDockNodeFlags
ImGuiDockNodeFlags.NoSplit = F.NoDockingSplit -- Renamed in 1.90
ImGuiDockNodeFlags.NoDockingInCentralNode = F.NoDockingOverCentralNode -- Renamed in 1.90
ImGuiDockNodeFlags.NoDocking = bit32.bor(F.NoDockingOverMe, F.NoDockingOverOther, F.NoDockingOverEmpty, F.NoDockingSplit, F.NoDockingSplitOther)
-- Masks
ImGuiDockNodeFlags.SharedFlagsInheritMask_ = 0xFFFFFFFF
ImGuiDockNodeFlags.NoResizeFlagsMask_ = bit32.bor(F.NoResize, F.NoResizeX, F.NoResizeY)
-- When splitting, those local flags are moved to the inheriting child, never duplicated
ImGuiDockNodeFlags.LocalFlagsTransferMask_ = bit32.bor(F.NoDockingSplit, F.NoResizeFlagsMask_, F.AutoHideTabBar, F.CentralNode, F.NoTabBar, F.HiddenTabBar, F.NoWindowMenuButton, F.NoCloseButton)
ImGuiDockNodeFlags.SavedFlagsMask_ = bit32.bor(F.NoResizeFlagsMask_, F.DockSpace, F.CentralNode, F.NoTabBar, F.HiddenTabBar, F.NoWindowMenuButton, F.NoCloseButton)
end
-- Store the source authority (dock node vs window) of a field
ImGuiDataAuthority = { Auto = 0, DockNode = 1, Window = 2 }
ImGuiDockNodeState = {
Unknown = 0,
HostWindowHiddenBecauseSingleWindow = 1,
HostWindowHiddenBecauseWindowsAreResizing = 2,
HostWindowVisible = 3,
}
-- List of colors that are stored at the time of Begin() into Docked Windows.
-- We currently store the packed colors in a simple array window.DockStyle.Colors[].
-- A better solution may involve appending into a log of colors in ImGuiContext + store offsets into those arrays in ImGuiWindow,
-- but it would be more complex as we'd need to double-buffer both as e.g. drop target may refer to window from last frame.
ImGuiWindowDockStyleCol = {
Text = 0, TabHovered = 1, TabFocused = 2, TabSelected = 3, TabSelectedOverline = 4,
TabDimmed = 5, TabDimmedSelected = 6, TabDimmedSelectedOverline = 7, UnsavedMarker = 8, COUNT = 9,
}
-------------------------------------------------------------------------------
-- Docking: Internal Types
-------------------------------------------------------------------------------
-- - ImGuiDockRequestType
-- - ImGuiDockRequest
-- - ImGuiDockPreviewData
-- - ImGuiDockNodeSettings
-- - ImGuiDockContext
-------------------------------------------------------------------------------
local ImGuiDockRequestType = { None = 0, Dock = 1, Undock = 2, Split = 3 } -- Split is the same as Dock but without a DockPayload
local function ImGuiDockRequest()
return {
Type = ImGuiDockRequestType.None,
-- DockTargetWindow: Destination/Target Window to dock into (may be a loose window or a DockNode, might be nil in which case DockTargetNode cannot be nil)
-- DockTargetNode: Destination/Target Node to dock into
-- DockPayload: Source/Payload window to dock (may be a loose window or a DockNode), [Optional]
DockTargetWindow = nil, DockTargetNode = nil, DockPayload = nil,
DockSplitDir = (-1) --[[ImGuiDir.None]], DockSplitRatio = 0.5, DockSplitOuter = false,
UndockTargetWindow = nil, UndockTargetNode = nil,
}
end
-- Persistent Settings data, stored contiguously in SettingsNodes
local function ImGuiDockNodeSettings()
return {
ID = 0, ParentNodeId = 0, ParentWindowId = 0, SelectedTabId = 0,
SplitAxis = 0 --[[ImGuiAxis.None]], Depth = 0, Flags = 0, -- Flags: NB: We save individual flags one by one in ascii format (ImGuiDockNodeFlags.SavedFlagsMask_)
Pos = ImVec2(0, 0), Size = ImVec2(0, 0), SizeRef = ImVec2(0, 0),
}
end
function ImGuiDockContext()
-- Nodes: Map ID -> ImGuiDockNode: Active nodes
return { Nodes = {}, Requests = ImVector(), NodesSettings = ImVector(), WantFullRebuild = false }
end
MT.ImGuiDockNode = {}
MT.ImGuiDockNode.__index = MT.ImGuiDockNode
local DN = MT.ImGuiDockNode
function ImGuiDockNode(id)
return setmetatable({
ID = id,
-- SharedFlags: (Write) Flags shared by all nodes of a same dockspace hierarchy (inherited from the root node)
-- LocalFlags: (Write) Flags specific to this node
-- LocalFlagsInWindows: (Write) Flags specific to this node, applied from windows
-- MergedFlags: (Read) Effective flags (== SharedFlags | LocalFlagsInNode | LocalFlagsInWindows)
SharedFlags = 0, LocalFlags = 0, LocalFlagsInWindows = 0, MergedFlags = 0,
State = 0 --[[ImGuiDockNodeState.Unknown]],
ParentNode = nil,
ChildNodes = { [0] = nil, [1] = nil }, -- [Split node only] Child nodes (left/right or top/bottom). Consider switching to an array.
Windows = ImVector(), -- Note: unordered list! Iterate TabBar.Tabs for user-order.
TabBar = nil,
-- Pos: Current position
-- Size: Current size
-- SizeRef: [Split node only] Last explicitly written-to size (overridden when using a splitter affecting the node), used to calculate Size.
Pos = ImVec2(0, 0), Size = ImVec2(0, 0), SizeRef = ImVec2(0, 0),
SplitAxis = 0 --[[ImGuiAxis.None]], -- [Split node only] Split axis (X or Y)
LastBgColor = 4294967295 --[[IM_COL32_WHITE]],
WindowClass = ImGuiWindowClass(), -- [Root node only]
-- VisibleWindow: Generally point to window which is ID is == SelectedTabID, but when CTRL+Tabbing this can be a different window.
HostWindow = nil, VisibleWindow = nil,
-- CentralNode: [Root node only] Pointer to central node.
-- OnlyNodeWithWindows: [Root node only] Set when there is a single visible node within the hierarchy.
CentralNode = nil, OnlyNodeWithWindows = nil,
CountNodeWithWindows = 0, -- [Root node only]
-- LastFrameAlive: Last frame number the node was updated or kept alive explicitly with DockSpace() + ImGuiDockNodeFlags.KeepAliveOnly
-- LastFrameActive: Last frame number the node was updated.
-- LastFrameFocused: Last frame number the node was focused.
LastFrameAlive = -1, LastFrameActive = -1, LastFrameFocused = -1,
-- LastFocusedNodeId: [Root node only] Which of our child docking node (any ancestor in the hierarchy) was last focused.
-- SelectedTabId: [Leaf node only] Which of our tab/window is selected.
-- WantCloseTabId: [Leaf node only] Set when closing a specific tab/window.
-- RefViewportId: Reference viewport ID from visible window when HostWindow == nil.
LastFocusedNodeId = 0, SelectedTabId = 0, WantCloseTabId = 0, RefViewportId = 0,
AuthorityForPos = 1 --[[ImGuiDataAuthority.DockNode]], AuthorityForSize = 1 --[[ImGuiDataAuthority.DockNode]], -- ImGuiDataAuthority
AuthorityForViewport = 0 --[[ImGuiDataAuthority.Auto]],
IsVisible = true, IsFocused = false, IsBgDrawnThisFrame = false, -- IsVisible: Set to false when the node is hidden (usually disabled as it has no active window)
HasCloseButton = false, HasWindowMenuButton = false, HasCentralNodeChild = false, -- HasCloseButton: Provide space for a close button (if any of the docked window has one). Note that button may be hidden on window without one.
-- WantCloseAll: Set when closing all tabs at once.
-- WantMouseMove: After a node extraction we need to transition toward moving the newly created host window
WantCloseAll = false, WantLockSizeOnce = false, WantMouseMove = false,
WantHiddenTabBarUpdate = false, WantHiddenTabBarToggle = false,
}, DN)
end
function DN:IsRootNode() return self.ParentNode == nil end
function DN:IsDockSpace() return bit32.band(self.MergedFlags, 1024 --[[ImGuiDockNodeFlags.DockSpace]]) ~= 0 end
function DN:IsFloatingNode() return self.ParentNode == nil and bit32.band(self.MergedFlags, 1024 --[[ImGuiDockNodeFlags.DockSpace]]) == 0 end
function DN:IsCentralNode() return bit32.band(self.MergedFlags, 2048 --[[ImGuiDockNodeFlags.CentralNode]]) ~= 0 end
function DN:IsHiddenTabBar() return bit32.band(self.MergedFlags, 8192 --[[ImGuiDockNodeFlags.HiddenTabBar]]) ~= 0 end -- Hidden tab bar can be shown back by clicking the small triangle
function DN:IsNoTabBar() return bit32.band(self.MergedFlags, 4096 --[[ImGuiDockNodeFlags.NoTabBar]]) ~= 0 end -- Never show a tab bar
function DN:IsSplitNode() return self.ChildNodes[0] ~= nil end
function DN:IsLeafNode() return self.ChildNodes[0] == nil end
function DN:IsEmpty() return self.ChildNodes[0] == nil and self.Windows.Size == 0 end
function DN:Rect() return ImRect(self.Pos.x, self.Pos.y, self.Pos.x + self.Size.x, self.Pos.y + self.Size.y) end
function DN:SetLocalFlags(flags) self.LocalFlags = flags; self:UpdateMergedFlags() end
function DN:UpdateMergedFlags() self.MergedFlags = bit32.bor(self.SharedFlags, self.LocalFlags, self.LocalFlagsInWindows) end
local function ImGuiDockPreviewData()
local d = {
FutureNode = ImGuiDockNode(0),
-- IsSidesAvailable: Hold your breath, grammar freaks..
-- IsSplitDirExplicit: Set when hovered the drop rect (vs. implicit SplitDir==None when hovered the window)
IsDropAllowed = false, IsCenterAvailable = false, IsSidesAvailable = false, IsSplitDirExplicit = false,
SplitNode = nil, SplitDir = (-1) --[[ImGuiDir.None]], SplitRatio = 0.0,
DropRectsDraw = {}, -- May be slightly different from hit-testing drop rects used in DockNodeCalcDropRects()
}
for n = 0, 4 do d.DropRectsDraw[n] = ImRect(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]], -3.4028234663853e+38 --[[FLT_MAX]], -3.4028234663853e+38 --[[FLT_MAX]]) end
return d
end
local function DockNodeGetHostWindowTitle(node)
return string.format("##DockNode_%02X", node.ID)
end
local function IsRectInverted(r) return r.Min.x > r.Max.x or r.Min.y > r.Max.y end
--- Sorted snapshot of node IDs (upstream iterates ImGuiStorage which is sorted by key)
local function DockContextNodesSorted(ctx)
local ids = {}
for id in pairs(ctx.DockContext.Nodes) do ids[#ids + 1] = id end
table.sort(ids)
return ids
end
ImGui.DockContextNodesSorted = DockContextNodesSorted
-------------------------------------------------------------------------------
-- Docking: Forward Declarations
-------------------------------------------------------------------------------
-- Settings
local DockSettingsHandler_ClearAll, DockSettingsHandler_ApplyAll, DockSettingsHandler_ReadOpen, DockSettingsHandler_ReadLine, DockSettingsHandler_WriteAll
local DockSettingsRenameNodeReferences, DockSettingsRemoveNodeReferences, DockSettingsFindNodeSettings
-------------------------------------------------------------------------------
-- Docking: ImGuiDockContext
-------------------------------------------------------------------------------
-- The lifetime model is different from the one of regular windows: we always create a ImGuiDockNode for each ImGuiDockNodeSettings,
-- or we always hold the entire docking node tree. Nodes are frequently hidden, e.g. if the window(s) or child nodes they host are not active.
-- At boot time only, we run a simple GC to remove nodes that have no references.
-- Because dock node settings (which are small, contiguous structures) are always mirrored by their corresponding dock nodes (more complete structures),
-- we can also very easily recreate the nodes from scratch given the settings data (this is what DockContextRebuild() does).
-- This is convenient as docking reconfiguration can be implemented by mostly poking at the simpler settings data.
-------------------------------------------------------------------------------
-- - DockContextInitialize()
-- - DockContextShutdown()
-- - DockContextClearNodes()
-- - DockContextRebuildNodes()
-- - DockContextNewFrameUpdateUndocking()
-- - DockContextNewFrameUpdateDocking()
-- - DockContextEndFrame()
-- - DockContextFindNodeByID()
-- - DockContextBindNodeToWindow()
-- - DockContextGenNodeID()
-- - DockContextAddNode()
-- - DockContextRemoveNode()
-- - ImGuiDockContextPruneNodeData
-- - DockContextPruneUnusedSettingsNodes()
-- - DockContextBuildNodesFromSettings()
-- - DockContextBuildAddWindowsToNodes()
-------------------------------------------------------------------------------
function ImGui.DockContextInitialize(ctx)
local g = ctx
-- Add .ini handle for persistent docking data
local ini_handler = ImGuiSettingsHandler()
ini_handler.TypeName = "Docking"
ini_handler.TypeHash = ImHashStr("Docking")
ini_handler.ClearAllFn = DockSettingsHandler_ClearAll
ini_handler.ReadInitFn = DockSettingsHandler_ClearAll -- Also clear on read
ini_handler.ReadOpenFn = DockSettingsHandler_ReadOpen
ini_handler.ReadLineFn = DockSettingsHandler_ReadLine
ini_handler.ApplyAllFn = DockSettingsHandler_ApplyAll
ini_handler.WriteAllFn = DockSettingsHandler_WriteAll
g.SettingsHandlers:push_back(ini_handler)
g.DockNodeWindowMenuHandler = ImGui.DockNodeWindowMenuHandler_Default
end
function ImGui.DockContextShutdown(ctx)
for _, id in ipairs(DockContextNodesSorted(ctx)) do
local node = ctx.DockContext.Nodes[id]
if node then ImGui.DockContextDeleteNode(ctx, node) end
end
end
function ImGui.DockContextClearNodes(ctx, root_id, clear_settings_refs)
ImGui.DockBuilderRemoveNodeDockedWindows(root_id, clear_settings_refs)
ImGui.DockBuilderRemoveNodeChildNodes(root_id)
end
-- [DEBUG] This function also acts as a defacto test to make sure we can rebuild from scratch without a glitch
-- (Different from DockSettingsHandler_ClearAll() + DockSettingsHandler_ApplyAll() because this reuses current settings!)
function ImGui.DockContextRebuildNodes(ctx)
local dc = ctx.DockContext
ImGui.SaveIniSettingsToMemory()
local root_id = 0 -- Rebuild all
ImGui.DockContextClearNodes(ctx, root_id, false)
ImGui.DockContextBuildNodesFromSettings(ctx, dc.NodesSettings.Data, dc.NodesSettings.Size)
ImGui.DockContextBuildAddWindowsToNodes(ctx, root_id)
end
-- Docking context update function, called by NewFrame()
function ImGui.DockContextNewFrameUpdateUndocking(ctx)
local g = ctx
local dc = ctx.DockContext
if bit32.band(g.IO.ConfigFlags, 128 --[[ImGuiConfigFlags.DockingEnable]]) == 0 then
if next(dc.Nodes) ~= nil or dc.Requests.Size > 0 then
ImGui.DockContextClearNodes(ctx, 0, true)
end
return
end
-- Setting NoSplit at runtime merges all nodes
if g.IO.ConfigDockingNoSplit then
for _, id in ipairs(DockContextNodesSorted(ctx)) do
local node = dc.Nodes[id]
if node and node:IsRootNode() and node:IsSplitNode() then
ImGui.DockBuilderRemoveNodeChildNodes(node.ID)
-- dc.WantFullRebuild = true
end
end
end
-- Process full rebuild
if dc.WantFullRebuild then
ImGui.DockContextRebuildNodes(ctx)
dc.WantFullRebuild = false
end
-- Process Undocking requests (we need to process them _before_ the UpdateMouseMovingWindowNewFrame call in NewFrame)
for _, req in dc.Requests:iter() do
if req.Type == ImGuiDockRequestType.Undock and req.UndockTargetWindow then
ImGui.DockContextProcessUndockWindow(ctx, req.UndockTargetWindow)
elseif req.Type == ImGuiDockRequestType.Undock and req.UndockTargetNode then
ImGui.DockContextProcessUndockNode(ctx, req.UndockTargetNode)
end
end
end
-- Docking context update function, called by NewFrame()
function ImGui.DockContextNewFrameUpdateDocking(ctx)
local g = ctx
local dc = ctx.DockContext
if bit32.band(g.IO.ConfigFlags, 128 --[[ImGuiConfigFlags.DockingEnable]]) == 0 then return end
-- [DEBUG] Store hovered dock node.
-- We could in theory use DockNodeTreeFindVisibleNodeByPos() on the root host dock node, but using .DockNode is a good shortcut.
-- Note this is mostly a debug thing and isn't actually used for docking target, because docking involve more detailed filtering.
g.DebugHoveredDockNode = nil
local hovered_window = g.HoveredWindowUnderMovingWindow
if hovered_window then
if hovered_window.DockNodeAsHost then
g.DebugHoveredDockNode = ImGui.DockNodeTreeFindVisibleNodeByPos(hovered_window.DockNodeAsHost, g.IO.MousePos)
elseif hovered_window.RootWindow.DockNode then
g.DebugHoveredDockNode = hovered_window.RootWindow.DockNode
end
end
-- Process Docking requests
for _, req in dc.Requests:iter() do
if req.Type == ImGuiDockRequestType.Dock then
ImGui.DockContextProcessDock(ctx, req)
end
end
dc.Requests:resize(0)
-- Create windows for each automatic docking nodes
for _, id in ipairs(DockContextNodesSorted(ctx)) do
local node = dc.Nodes[id]
if node and node:IsFloatingNode() then
ImGui.DockNodeUpdate(node)
end
end
end
function ImGui.DockContextEndFrame(ctx)
-- Draw backgrounds of node missing their window
local g = ctx
local dc = g.DockContext
for _, id in ipairs(DockContextNodesSorted(ctx)) do
local node = dc.Nodes[id]
if node and node.LastFrameActive == g.FrameCount and node.IsVisible and node.HostWindow and node:IsLeafNode() and not node.IsBgDrawnThisFrame then
local bg_rect = ImRect(node.Pos + ImVec2(0.0, ImGui.GetFrameHeight()), node.Pos + node.Size)
local bg_rounding_flags = ImGui.CalcRoundingFlagsForRectInRect(bg_rect, node.HostWindow:Rect(), g.Style.DockingSeparatorSize)
node.HostWindow.DrawList:ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG)
node.HostWindow.DrawList:AddRectFilled(bg_rect.Min, bg_rect.Max, node.LastBgColor, node.HostWindow.WindowRounding, bg_rounding_flags)
end
end
end
function ImGui.DockContextFindNodeByID(ctx, id)
return ctx.DockContext.Nodes[id]
end
function ImGui.DockContextGenNodeID(ctx)
-- Generate an ID for new node (the exact ID value doesn't matter as long as it is not already used)
-- FIXME-OPT FIXME-DOCK: This is suboptimal, even if the node count is small enough not to be a worry.0
-- We should poke in ctx.Nodes to find a suitable ID faster. Even more so trivial that ctx.Nodes lookup is already sorted.
local id = 0x0001
while ImGui.DockContextFindNodeByID(ctx, id) ~= nil do id = id + 1 end
return id
end
function ImGui.DockContextAddNode(ctx, id)
-- Generate an ID for the new node (the exact ID value doesn't matter as long as it is not already used) and add the first window.
if id == 0 then
id = ImGui.DockContextGenNodeID(ctx)
else
assert(ImGui.DockContextFindNodeByID(ctx, id) == nil)
end
-- We don't set node.LastFrameAlive on construction. Nodes are always created at all time to reflect .ini settings!
local node = ImGuiDockNode(id)
ctx.DockContext.Nodes[node.ID] = node
return node
end
function ImGui.DockContextRemoveNode(ctx, node, merge_sibling_into_parent_node)
assert(ImGui.DockContextFindNodeByID(ctx, node.ID) == node)
assert(node.ChildNodes[0] == nil and node.ChildNodes[1] == nil)
assert(node.Windows.Size == 0)
if node.HostWindow then node.HostWindow.DockNodeAsHost = nil end
local parent_node = node.ParentNode
local merge = (merge_sibling_into_parent_node and parent_node ~= nil)
if merge then
assert(parent_node.ChildNodes[0] == node or parent_node.ChildNodes[1] == node)
local sibling_node = (parent_node.ChildNodes[0] == node) and parent_node.ChildNodes[1] or parent_node.ChildNodes[0]
ImGui.DockNodeTreeMerge(ctx, parent_node, sibling_node)
else
if parent_node then
for n = 0, 1 do
if parent_node.ChildNodes[n] == node then node.ParentNode.ChildNodes[n] = nil end
end
end
ImGui.DockContextDeleteNode(ctx, node)
end
end
-- Raw-ish delete
function ImGui.DockContextDeleteNode(ctx, node)
local dc = ctx.DockContext
node.TabBar = nil
dc.Nodes[node.ID] = nil
end
-- Pre C++0x doesn't allow us to use a function-local type (without linkage) as template parameter, so we moved this here.
local function ImGuiDockContextPruneNodeData()
return { CountWindows = 0, CountChildWindows = 0, CountChildNodes = 0, RootId = 0 }
end
-- Garbage collect unused nodes (run once at init time)
function ImGui.DockContextPruneUnusedSettingsNodes(ctx)
local g = ctx
local dc = ctx.DockContext
assert(g.Windows.Size == 0)
local pool = {}
local function GetOrAdd(id)
local d = pool[id]
if not d then d = ImGuiDockContextPruneNodeData(); pool[id] = d end
return d
end
-- Count child nodes and compute RootID
for _, settings in dc.NodesSettings:iter() do
if pool[settings.ID] ~= nil then
settings.ID = 0 -- Duplicate
else
local parent_data = (settings.ParentNodeId ~= 0) and pool[settings.ParentNodeId] or nil
GetOrAdd(settings.ID).RootId = parent_data and parent_data.RootId or settings.ID
if settings.ParentNodeId ~= 0 then
local p = GetOrAdd(settings.ParentNodeId)
p.CountChildNodes = p.CountChildNodes + 1
end
end
end
-- Count reference to dock ids from dockspaces
-- We track the 'auto-DockNode <- manual-Window <- manual-DockSpace' in order to avoid 'auto-DockNode' being ditched by DockContextPruneUnusedSettingsNodes()
for _, settings in dc.NodesSettings:iter() do
if settings.ParentWindowId ~= 0 then
local window_settings = ImGui.FindWindowSettingsByID(settings.ParentWindowId)
if window_settings and window_settings.DockId ~= 0 then
local data = pool[window_settings.DockId]
if data then data.CountChildNodes = data.CountChildNodes + 1 end
end
end
end
-- Count reference to dock ids from window settings
-- We guard against the possibility of an invalid .ini file (RootID may point to a missing node)
for _, settings in g.SettingsWindows:iter() do
local dock_id = settings.DockId
if dock_id and dock_id ~= 0 then
local data = pool[dock_id]
if data then
data.CountWindows = data.CountWindows + 1
local data_root = (data.RootId == dock_id) and data or pool[data.RootId]
if data_root then data_root.CountChildWindows = data_root.CountChildWindows + 1 end
end
end
end
-- Prune
for _, settings in dc.NodesSettings:iter() do
local data = pool[settings.ID]
if data ~= nil and data.CountWindows <= 1 then
local data_root = (settings.ID == data.RootId) and data or pool[data.RootId]
local data_parent = (settings.ParentNodeId ~= 0) and pool[settings.ParentNodeId] or nil
local remove = false
remove = remove or (data.CountWindows == 1 and settings.ParentNodeId == 0 and data.CountChildNodes == 0 and bit32.band(settings.Flags, 2048 --[[ImGuiDockNodeFlags.CentralNode]]) == 0) -- Floating root node with only 1 window
remove = remove or (data.CountWindows == 0 and settings.ParentNodeId == 0 and data.CountChildNodes == 0) -- Leaf nodes with 0 window
remove = remove or (data_root == nil or data_root.CountChildWindows == 0)
if remove then
DockSettingsRemoveNodeReferences({ settings.ID }, 1)
settings.ID = 0
elseif data_parent and data_parent.CountChildNodes == 1 then
DockSettingsRenameNodeReferences(settings.ID, settings.ParentNodeId)
settings.ID = 0
end
end
end
end
--- @param node_settings_array table # 1-based array
function ImGui.DockContextBuildNodesFromSettings(ctx, node_settings_array, node_settings_count)
-- Build nodes
for node_n = 1, node_settings_count do
local settings = node_settings_array[node_n]
if settings.ID ~= 0 and ImGui.DockContextFindNodeByID(ctx, settings.ID) == nil then
local node = ImGui.DockContextAddNode(ctx, settings.ID)
node.ParentNode = (settings.ParentNodeId ~= 0) and ImGui.DockContextFindNodeByID(ctx, settings.ParentNodeId) or nil
node.Pos = ImVec2(settings.Pos.x, settings.Pos.y)
node.Size = ImVec2(settings.Size.x, settings.Size.y)
node.SizeRef = ImVec2(settings.SizeRef.x, settings.SizeRef.y)
node.AuthorityForPos = 1 --[[ImGuiDataAuthority.DockNode]]
node.AuthorityForSize = 1 --[[ImGuiDataAuthority.DockNode]]
node.AuthorityForViewport = 1 --[[ImGuiDataAuthority.DockNode]]
if node.ParentNode and node.ParentNode.ChildNodes[0] == nil then
node.ParentNode.ChildNodes[0] = node
elseif node.ParentNode and node.ParentNode.ChildNodes[1] == nil then
node.ParentNode.ChildNodes[1] = node
end
node.SelectedTabId = settings.SelectedTabId
node.SplitAxis = settings.SplitAxis
node:SetLocalFlags(bit32.band(settings.Flags, 261152 --[[ImGuiDockNodeFlags.SavedFlagsMask_]]))
-- Bind host window immediately if it already exist (in case of a rebuild)
-- This is useful as the RootWindowForTitleBarHighlight links necessary to highlight the currently focused node requires node.HostWindow to be set.
local root_node = ImGui.DockNodeGetRootNode(node)
node.HostWindow = ImGui.FindWindowByName(DockNodeGetHostWindowTitle(root_node))
end
end
end
function ImGui.DockContextBuildAddWindowsToNodes(ctx, root_id)
-- Rebind all windows to nodes (they can also lazily rebind but we'll have a visible glitch during the first frame)
local g = ctx
for _, window in g.Windows:iter() do
if window.DockId ~= 0 and window.LastFrameActive >= g.FrameCount - 1 and window.DockNode == nil then
local node = ImGui.DockContextFindNodeByID(ctx, window.DockId)
assert(node ~= nil) -- This should have been called after DockContextBuildNodesFromSettings()
if root_id == 0 or ImGui.DockNodeGetRootNode(node).ID == root_id then
ImGui.DockNodeAddWindow(node, window, true)
end
end
end
end
-------------------------------------------------------------------------------
-- Docking: ImGuiDockContext Docking/Undocking functions
-------------------------------------------------------------------------------
-- - DockContextQueueDock()
-- - DockContextQueueUndockWindow()
-- - DockContextQueueUndockNode()
-- - DockContextQueueNotifyRemovedNode()
-- - DockContextProcessDock()
-- - DockContextProcessUndockWindow()
-- - DockContextProcessUndockNode()
-- - DockContextCalcDropPosForDocking()
-------------------------------------------------------------------------------
function ImGui.DockContextQueueDock(ctx, target, target_node, payload, split_dir, split_ratio, split_outer)
assert(target ~= payload)
local req = ImGuiDockRequest()
req.Type = ImGuiDockRequestType.Dock
req.DockTargetWindow = target
req.DockTargetNode = target_node
req.DockPayload = payload
req.DockSplitDir = split_dir
req.DockSplitRatio = split_ratio
req.DockSplitOuter = split_outer
ctx.DockContext.Requests:push_back(req)
end
function ImGui.DockContextQueueUndockWindow(ctx, window)
local req = ImGuiDockRequest()
req.Type = ImGuiDockRequestType.Undock
req.UndockTargetWindow = window
ctx.DockContext.Requests:push_back(req)
end
function ImGui.DockContextQueueUndockNode(ctx, node)
local req = ImGuiDockRequest()
req.Type = ImGuiDockRequestType.Undock
req.UndockTargetNode = node
ctx.DockContext.Requests:push_back(req)
end
function ImGui.DockContextQueueNotifyRemovedNode(ctx, node)
for _, req in ctx.DockContext.Requests:iter() do
if req.DockTargetNode == node then req.Type = ImGuiDockRequestType.None end
end
end
function ImGui.DockContextProcessDock(ctx, req)
assert((req.Type == ImGuiDockRequestType.Dock and req.DockPayload ~= nil) or (req.Type == ImGuiDockRequestType.Split and req.DockPayload == nil))
assert(req.DockTargetWindow ~= nil or req.DockTargetNode ~= nil)
local g = ctx
local payload_window = req.DockPayload -- Optional
local target_window = req.DockTargetWindow
local node = req.DockTargetNode
-- Decide which Tab will be selected at the end of the operation
local next_selected_id = 0
local payload_node = nil
if payload_window then
payload_node = payload_window.DockNodeAsHost
payload_window.DockNodeAsHost = nil -- Important to clear this as the node will have its life as a child which might be merged/deleted later.
if payload_node and payload_node:IsLeafNode() then
next_selected_id = (payload_node.TabBar.NextSelectedTabId ~= 0) and payload_node.TabBar.NextSelectedTabId or payload_node.TabBar.SelectedTabId
end
if payload_node == nil then next_selected_id = payload_window.TabId end
end
-- FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well
-- When processing an interactive split, usually LastFrameAlive will be < g.FrameCount. But DockBuilder operations can make it ==.
if node then assert(node.LastFrameAlive <= g.FrameCount) end
if node and target_window and node == target_window.DockNodeAsHost then
assert(node.Windows.Size > 0 or node:IsSplitNode() or node:IsCentralNode())
end
-- Create new node and add existing window to it
if node == nil then
node = ImGui.DockContextAddNode(ctx, 0)
node.Pos = ImVec2(target_window.Pos.x, target_window.Pos.y)
node.Size = ImVec2(target_window.Size.x, target_window.Size.y)
if target_window.DockNodeAsHost == nil then
ImGui.DockNodeAddWindow(node, target_window, true)
local t0 = node.TabBar.Tabs.Data[1]
t0.Flags = bit32.band(t0.Flags, bit32.bnot(8388608 --[[ImGuiTabItemFlags.Unsorted]]))
target_window.DockIsActive = true
end
end
local split_dir = req.DockSplitDir
if split_dir ~= (-1) --[[ImGuiDir.None]] then
-- Split into two, one side will be our payload node unless we are dropping a loose window
local split_axis = (split_dir == 0 --[[ImGuiDir.Left]] or split_dir == 1 --[[ImGuiDir.Right]]) and 1 --[[ImGuiAxis.X]] or 2 --[[ImGuiAxis.Y]]
local split_inheritor_child_idx = (split_dir == 0 --[[ImGuiDir.Left]] or split_dir == 2 --[[ImGuiDir.Up]]) and 1 or 0 -- Current contents will be moved to the opposite side
local split_ratio = req.DockSplitRatio
ImGui.DockNodeTreeSplit(ctx, node, split_axis, split_inheritor_child_idx, split_ratio, payload_node) -- payload_node may be nil here!
local new_node = node.ChildNodes[bit32.bxor(split_inheritor_child_idx, 1)]
new_node.HostWindow = node.HostWindow
node = new_node
end
node:SetLocalFlags(bit32.band(node.LocalFlags, bit32.bnot(8192 --[[ImGuiDockNodeFlags.HiddenTabBar]])))
if node ~= payload_node then
-- Create tab bar before we call DockNodeMoveWindows (which would attempt to move the old tab-bar, which would lead us to payload tabs wrongly appearing before target tabs!)
if node.Windows.Size > 0 and node.TabBar == nil then
ImGui.DockNodeAddTabBar(node)
for n = 1, node.Windows.Size do
ImGui.TabBarAddTab(node.TabBar, 0 --[[ImGuiTabItemFlags.None]], node.Windows.Data[n])
end
end
if payload_node ~= nil then
-- Transfer full payload node (with 1+ child windows or child nodes)
if payload_node:IsSplitNode() then
if node.Windows.Size > 0 then
-- We can dock a split payload into a node that already has windows _only_ if our payload is a node tree with a single visible node.
-- In this situation, we move the windows of the target node into the currently visible node of the payload.
-- This allows us to preserve some of the underlying dock tree settings nicely.
assert(payload_node.OnlyNodeWithWindows ~= nil) -- The docking should have been blocked by DockNodePreviewDockSetup() early on and never submitted.
local visible_node = payload_node.OnlyNodeWithWindows
if visible_node.TabBar then assert(visible_node.TabBar.Tabs.Size > 0) end
ImGui.DockNodeMoveWindows(node, visible_node)
ImGui.DockNodeMoveWindows(visible_node, node)
DockSettingsRenameNodeReferences(node.ID, visible_node.ID)
end
if node:IsCentralNode() then
-- Central node property needs to be moved to a leaf node, pick the last focused one.
-- FIXME-DOCK: If we had to transfer other flags here, what would the policy be?
local last_focused_node = ImGui.DockContextFindNodeByID(ctx, payload_node.LastFocusedNodeId)
assert(last_focused_node ~= nil)
local last_focused_root_node = ImGui.DockNodeGetRootNode(last_focused_node)
assert(last_focused_root_node == ImGui.DockNodeGetRootNode(payload_node))
last_focused_node:SetLocalFlags(bit32.bor(last_focused_node.LocalFlags, 2048 --[[ImGuiDockNodeFlags.CentralNode]]))
node:SetLocalFlags(bit32.band(node.LocalFlags, bit32.bnot(2048 --[[ImGuiDockNodeFlags.CentralNode]])))
last_focused_root_node.CentralNode = last_focused_node
end
assert(node.Windows.Size == 0)
ImGui.DockNodeMoveChildNodes(node, payload_node)
else
local payload_dock_id = payload_node.ID
ImGui.DockNodeMoveWindows(node, payload_node)
DockSettingsRenameNodeReferences(payload_dock_id, node.ID)
end
ImGui.DockContextRemoveNode(ctx, payload_node, true)
elseif payload_window then
-- Transfer single window
local payload_dock_id = payload_window.DockId
node.VisibleWindow = payload_window
ImGui.DockNodeAddWindow(node, payload_window, true)
if payload_dock_id ~= 0 then
DockSettingsRenameNodeReferences(payload_dock_id, node.ID)
end
end
else
-- When docking a floating single window node we want to reevaluate auto-hiding of the tab bar
node.WantHiddenTabBarUpdate = true
end
-- Update selection immediately
local tab_bar = node.TabBar
if tab_bar then tab_bar.NextSelectedTabId = next_selected_id end
ImGui.MarkIniSettingsDirty()
end
-- Problem:
-- Undocking a large (~full screen) window would leave it so large that the bottom right sizing corner would more
-- than likely be off the screen and the window would be hard to resize to fit on screen. This can be particularly problematic
-- with 'ConfigWindowsMoveFromTitleBarOnly=true' and/or with 'ConfigWindowsResizeFromEdges=false' as well (the later can be
-- due to missing ImGuiBackendFlags.HasMouseCursors backend flag).
-- Solution:
-- When undocking a window we currently force its maximum size to 90% of the host viewport.
-- Reevaluate this when we implement preserving docked/undocked size ("docking_wip/undocked_size" branch).
local function FixLargeWindowsWhenUndocking(size, ref_viewport)
if ref_viewport == nil then return size end
local max_size = ImTrunc(ref_viewport.WorkSize * 0.90)
return ImVec2(ImMin(size.x, max_size.x), ImMin(size.y, max_size.y))
end
function ImGui.DockContextProcessUndockWindow(ctx, window, clear_persistent_docking_ref)
if clear_persistent_docking_ref == nil then clear_persistent_docking_ref = true end -- C++ default argument
if window.DockNode then
ImGui.DockNodeRemoveWindow(window.DockNode, window, clear_persistent_docking_ref and 0 or window.DockId)
else
window.DockId = 0
end
window.Collapsed = false
window.DockIsActive = false
window.DockNodeIsVisible = false
window.DockTabIsVisible = false
local sz = FixLargeWindowsWhenUndocking(window.SizeFull, window.Viewport)
ImVec2_Copy(window.Size, sz)
ImVec2_Copy(window.SizeFull, sz)
ImGui.MarkIniSettingsDirty()
end
function ImGui.DockContextProcessUndockNode(ctx, node)
assert(node:IsLeafNode())
assert(node.Windows.Size >= 1)
if node:IsRootNode() or node:IsCentralNode() then
-- In the case of a root node or central node, the node will have to stay in place. Create a new node to receive the payload.
local new_node = ImGui.DockContextAddNode(ctx, 0)
new_node.Pos = ImVec2(node.Pos.x, node.Pos.y)
new_node.Size = ImVec2(node.Size.x, node.Size.y)
new_node.SizeRef = ImVec2(node.SizeRef.x, node.SizeRef.y)
ImGui.DockNodeMoveWindows(new_node, node)
DockSettingsRenameNodeReferences(node.ID, new_node.ID)
node = new_node
else
-- Otherwise extract our node and merge our sibling back into the parent node.
assert(node.ParentNode.ChildNodes[0] == node or node.ParentNode.ChildNodes[1] == node)
local index_in_parent = (node.ParentNode.ChildNodes[0] == node) and 0 or 1
node.ParentNode.ChildNodes[index_in_parent] = nil
ImGui.DockNodeTreeMerge(ctx, node.ParentNode, node.ParentNode.ChildNodes[bit32.bxor(index_in_parent, 1)])
node.ParentNode.AuthorityForViewport = 2 --[[ImGuiDataAuthority.Window]] -- The node that stays in place keeps the viewport, so our newly dragged out node will create a new viewport
node.ParentNode = nil
end
for _, window in node.Windows:iter() do
window.Flags = bit32.band(window.Flags, bit32.bnot(16777216 --[[ImGuiWindowFlags.ChildWindow]]))
if window.ParentWindow then window.ParentWindow.DC.ChildWindows:find_erase(window) end
ImGui.UpdateWindowParentAndRootLinks(window, window.Flags, nil)
end
node.AuthorityForPos = 1 --[[ImGuiDataAuthority.DockNode]]
node.AuthorityForSize = 1 --[[ImGuiDataAuthority.DockNode]]
node.Size = FixLargeWindowsWhenUndocking(node.Size, node.Windows.Data[1].Viewport)
node.WantMouseMove = true
ImGui.MarkIniSettingsDirty()
end
-- This is mostly used for automation.
--- @return bool ok, ImVec2? out_pos
function ImGui.DockContextCalcDropPosForDocking(target, target_node, payload_window, payload_node, split_dir, split_outer)
if target ~= nil and target_node == nil then target_node = target.DockNode end
-- In DockNodePreviewDockSetup() for a root central node instead of showing both "inner" and "outer" drop rects
-- (which would be functionally identical) we only show the outer one. Reflect this here.
if target_node and target_node.ParentNode == nil and target_node:IsCentralNode() and split_dir ~= (-1) --[[ImGuiDir.None]] then
split_outer = true
end
local split_data = ImGuiDockPreviewData()
ImGui.DockNodePreviewDockSetup(target, target_node, payload_window, payload_node, split_data, false, split_outer)
local r = split_data.DropRectsDraw[split_dir + 1]
if IsRectInverted(r) then return false, nil end
return true, r:GetCenter()
end
-------------------------------------------------------------------------------
-- Docking: ImGuiDockNode
-------------------------------------------------------------------------------
-- - DockNodeGetTabOrder()
-- - DockNodeAddWindow()
-- - DockNodeRemoveWindow()
-- - DockNodeMoveChildNodes()
-- - DockNodeMoveWindows()
-- - DockNodeApplyPosSizeToWindows()
-- - DockNodeHideHostWindow()
-- - ImGuiDockNodeFindInfoResults
-- - DockNodeFindInfo()
-- - DockNodeFindWindowByID()
-- - DockNodeUpdateFlagsAndCollapse()
-- - DockNodeUpdateHasCentralNodeFlag()
-- - DockNodeUpdateVisibleFlag()
-- - DockNodeStartMouseMovingWindow()
-- - DockNodeUpdate()
-- - DockNodeUpdateWindowMenu()
-- - DockNodeBeginAmendTabBar()
-- - DockNodeEndAmendTabBar()
-- - DockNodeUpdateTabBar()
-- - DockNodeAddTabBar()
-- - DockNodeRemoveTabBar()
-- - DockNodeIsDropAllowedOne()
-- - DockNodeIsDropAllowed()
-- - DockNodeCalcTabBarLayout()
-- - DockNodeCalcSplitRects()
-- - DockNodeCalcDropRectsAndTestMousePos()
-- - DockNodePreviewDockSetup()
-- - DockNodePreviewDockRender()
-------------------------------------------------------------------------------
function ImGui.DockNodeGetTabOrder(window)
local tab_bar = window.DockNode.TabBar
if tab_bar == nil then return -1 end
local tab = ImGui.TabBarFindTabByID(tab_bar, window.TabId)
return tab and ImGui.TabBarGetTabOrder(tab_bar, tab) or -1
end
local function DockNodeHideWindowDuringHostWindowCreation(window)
window.Hidden = true
window.HiddenFramesCanSkipItems = window.Active and 1 or 2
end
function ImGui.DockNodeAddWindow(node, window, add_to_tab_bar)
if window.DockNode then
-- Can overwrite an existing window.DockNode (e.g. pointing to a disabled DockSpace node)
assert(window.DockNode.ID ~= node.ID)
ImGui.DockNodeRemoveWindow(window.DockNode, window, 0)
end
assert(window.DockNode == nil or window.DockNodeAsHost == nil)
-- If more than 2 windows appeared on the same frame leading to the creation of a new hosting window,
-- we'll hide windows until the host window is ready. Hide the 1st window after its been output (so it is not visible for one frame).
-- We will call DockNodeHideWindowDuringHostWindowCreation() on ourselves in Begin()
if node.HostWindow == nil and node.Windows.Size == 1 and node.Windows.Data[1].WasActive == false then
DockNodeHideWindowDuringHostWindowCreation(node.Windows.Data[1])
end
node.Windows:push_back(window)
node.WantHiddenTabBarUpdate = true
window.DockNode = node
window.DockId = node.ID
window.DockIsActive = (node.Windows.Size > 1)
window.DockTabWantClose = false
-- When reactivating a node with one or two loose window, the window pos/size/viewport are authoritative over the node storage.
-- In particular it is important we init the viewport from the first window so we don't create two viewports and drop one.
if node.HostWindow == nil and node:IsFloatingNode() then
if node.AuthorityForPos == 0 --[[ImGuiDataAuthority.Auto]] then node.AuthorityForPos = 2 --[[ImGuiDataAuthority.Window]] end
if node.AuthorityForSize == 0 --[[ImGuiDataAuthority.Auto]] then node.AuthorityForSize = 2 --[[ImGuiDataAuthority.Window]] end
if node.AuthorityForViewport == 0 --[[ImGuiDataAuthority.Auto]] then node.AuthorityForViewport = 2 --[[ImGuiDataAuthority.Window]] end
end
-- Add to tab bar if requested
if add_to_tab_bar then
if node.TabBar == nil then
ImGui.DockNodeAddTabBar(node)
node.TabBar.SelectedTabId = node.SelectedTabId
node.TabBar.NextSelectedTabId = node.SelectedTabId
-- Add existing windows
for n = 1, node.Windows.Size - 1 do
ImGui.TabBarAddTab(node.TabBar, 0 --[[ImGuiTabItemFlags.None]], node.Windows.Data[n])
end
end
ImGui.TabBarAddTab(node.TabBar, 8388608 --[[ImGuiTabItemFlags.Unsorted]], window)
end
ImGui.DockNodeUpdateVisibleFlag(node)
-- Update this without waiting for the next time we Begin() in the window, so our host window will have the proper title bar color on its first frame.
if node.HostWindow then
ImGui.UpdateWindowParentAndRootLinks(window, bit32.bor(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]), node.HostWindow)
end
end
function ImGui.DockNodeRemoveWindow(node, window, save_dock_id)
assert(window.DockNode == node)
-- IM_ASSERT(window.RootWindowDockTree == node.HostWindow)
-- IM_ASSERT(window.LastFrameActive < g.FrameCount) -- We may call this from Begin()
assert(save_dock_id == 0 or save_dock_id == node.ID)
window.DockNode = nil
window.DockIsActive = false
window.DockTabWantClose = false
window.DockId = save_dock_id
window.Flags = bit32.band(window.Flags, bit32.bnot(16777216 --[[ImGuiWindowFlags.ChildWindow]]))
if window.ParentWindow then window.ParentWindow.DC.ChildWindows:find_erase(window) end
ImGui.UpdateWindowParentAndRootLinks(window, window.Flags, nil) -- Update immediately
if node.HostWindow and node.HostWindow.ViewportOwned then
-- When undocking from a user interaction this will always run in NewFrame() and have not much effect.
-- But mid-frame, if we clear viewport we need to mark window as hidden as well.
window.Viewport = nil
window.ViewportId = 0
window.ViewportOwned = false
window.Hidden = true
end
-- Remove window
local idx = node.Windows:find_index(window)
assert(idx ~= nil)
node.Windows:erase(idx)
if node.VisibleWindow == window then node.VisibleWindow = nil end
-- Remove tab and possibly tab bar
node.WantHiddenTabBarUpdate = true
if node.TabBar then
ImGui.TabBarRemoveTab(node.TabBar, window.TabId)
local tab_count_threshold_for_tab_bar = node:IsCentralNode() and 1 or 2
if node.Windows.Size < tab_count_threshold_for_tab_bar then
ImGui.DockNodeRemoveTabBar(node)
end
end
if node.Windows.Size == 0 and not node:IsCentralNode() and not node:IsDockSpace() and window.DockId ~= node.ID then
-- Automatic dock node delete themselves if they are not holding at least one tab
ImGui.DockContextRemoveNode(ImGui.GetCurrentContext(), node, true)
return
end
if node.Windows.Size == 1 and not node:IsCentralNode() and node.HostWindow then
local remaining_window = node.Windows.Data[1]
-- Note: we used to transport viewport ownership here.
remaining_window.Collapsed = node.HostWindow.Collapsed
end
-- Update visibility immediately is required so the DockNodeUpdateRemoveInactiveChilds() processing can reflect changes up the tree
ImGui.DockNodeUpdateVisibleFlag(node)
end
function ImGui.DockNodeMoveChildNodes(dst_node, src_node)
assert(dst_node.Windows.Size == 0)
dst_node.ChildNodes[0] = src_node.ChildNodes[0]
dst_node.ChildNodes[1] = src_node.ChildNodes[1]
if dst_node.ChildNodes[0] then dst_node.ChildNodes[0].ParentNode = dst_node end
if dst_node.ChildNodes[1] then dst_node.ChildNodes[1].ParentNode = dst_node end
dst_node.SplitAxis = src_node.SplitAxis
dst_node.SizeRef = ImVec2(src_node.SizeRef.x, src_node.SizeRef.y)
src_node.ChildNodes[0] = nil
src_node.ChildNodes[1] = nil
end
function ImGui.DockNodeMoveWindows(dst_node, src_node)
-- Insert tabs in the same orders as currently ordered (node.Windows isn't ordered)
assert(src_node and dst_node and dst_node ~= src_node)
local src_tab_bar = src_node.TabBar
if src_tab_bar ~= nil then assert(src_node.Windows.Size <= src_node.TabBar.Tabs.Size) end
-- If the dst_node is empty we can just move the entire tab bar (to preserve selection, scrolling, etc.)
local move_tab_bar = (src_tab_bar ~= nil) and (dst_node.TabBar == nil)
if move_tab_bar then
dst_node.TabBar = src_node.TabBar
src_node.TabBar = nil
end
-- Tab order is not important here, it is preserved by sorting in DockNodeUpdateTabBar().
-- Snapshot: DockNodeAddWindow() won't touch src_node.Windows since window.DockNode is cleared first
local windows = {}
for i = 1, src_node.Windows.Size do windows[i] = src_node.Windows.Data[i] end
for _, window in ipairs(windows) do
window.DockNode = nil
window.DockIsActive = false
ImGui.DockNodeAddWindow(dst_node, window, not move_tab_bar)
end
src_node.Windows:clear()
if not move_tab_bar and src_node.TabBar then
if dst_node.TabBar then dst_node.TabBar.SelectedTabId = src_node.TabBar.SelectedTabId end
ImGui.DockNodeRemoveTabBar(src_node)
end
end
function ImGui.DockNodeApplyPosSizeToWindows(node)
for _, window in node.Windows:iter() do
ImGui.SetWindowPos(window, node.Pos, 1 --[[ImGuiCond.Always]]) -- We don't assign directly to Pos because it can break the calculation of SizeContents on next frame
ImGui.SetWindowSize(window, node.Size, 1 --[[ImGuiCond.Always]])
end
end
function ImGui.DockNodeHideHostWindow(node)
if node.HostWindow then
if node.HostWindow.DockNodeAsHost == node then node.HostWindow.DockNodeAsHost = nil end
node.HostWindow = nil
end
if node.Windows.Size == 1 then
node.VisibleWindow = node.Windows.Data[1]
node.Windows.Data[1].DockIsActive = false
end
if node.TabBar then ImGui.DockNodeRemoveTabBar(node) end
end
local function ImGuiDockNodeTreeInfo()
return { CentralNode = nil, FirstNodeWithWindows = nil, CountNodesWithWindows = 0 }
end
-- Search function called once by root node in DockNodeUpdate()
local function DockNodeFindInfo(node, info)
if node.Windows.Size > 0 then
if info.FirstNodeWithWindows == nil then info.FirstNodeWithWindows = node end
info.CountNodesWithWindows = info.CountNodesWithWindows + 1
end
if node:IsCentralNode() then
assert(info.CentralNode == nil) -- Should be only one
assert(node:IsLeafNode(), "If you get this assert: please submit .ini file + repro of actions leading to this.")
info.CentralNode = node
end
if info.CountNodesWithWindows > 1 and info.CentralNode ~= nil then return end
if node.ChildNodes[0] then DockNodeFindInfo(node.ChildNodes[0], info) end
if node.ChildNodes[1] then DockNodeFindInfo(node.ChildNodes[1], info) end
end
function ImGui.DockNodeFindWindowByID(node, id)
assert(id ~= 0)
for _, window in node.Windows:iter() do
if window.ID == id then return window end
end
return nil
end
-- - Remove inactive windows/nodes.
-- - Update visibility flag.
function ImGui.DockNodeUpdateFlagsAndCollapse(node)
local g = ImGui.GetCurrentContext()
assert(node.ParentNode == nil or node.ParentNode.ChildNodes[0] == node or node.ParentNode.ChildNodes[1] == node)
-- Inherit most flags
if node.ParentNode then
node.SharedFlags = bit32.band(node.ParentNode.SharedFlags, 4294967295 --[[ImGuiDockNodeFlags.SharedFlagsInheritMask_]])
end
-- Recurse into children
-- There is the possibility that one of our child becoming empty will delete itself and moving its sibling contents into 'node'.
-- If 'node.ChildNode[0]' delete itself, then 'node.ChildNode[1].Windows' will be moved into 'node'
-- If 'node.ChildNode[1]' delete itself, then 'node.ChildNode[0].Windows' will be moved into 'node' and the "remove inactive windows" loop will have run twice on those windows (harmless)
node.HasCentralNodeChild = false
if node.ChildNodes[0] then ImGui.DockNodeUpdateFlagsAndCollapse(node.ChildNodes[0]) end
if node.ChildNodes[1] then ImGui.DockNodeUpdateFlagsAndCollapse(node.ChildNodes[1]) end
-- Remove inactive windows, collapse nodes
-- Merge node flags overrides stored in windows
node.LocalFlagsInWindows = 0 --[[ImGuiDockNodeFlags.None]]
local window_n = 1
while window_n <= node.Windows.Size do
local window = node.Windows.Data[window_n]
assert(window.DockNode == node)
local node_was_active = (node.LastFrameActive + 1 == g.FrameCount)
local remove = false
remove = remove or (node_was_active and (window.WasActive == false)) -- Can't use 'window.LastFrameActive + 1 < g.FrameCount'. (see #9151)
remove = remove or (node_was_active and (node.WantCloseAll or node.WantCloseTabId == window.TabId) and window.HasCloseButton and bit32.band(window.Flags, 262144 --[[ImGuiWindowFlags.UnsavedDocument]]) == 0) -- Submit all _expected_ closure from last frame
remove = remove or window.DockTabWantClose
if remove then
window.DockTabWantClose = false
if node.Windows.Size == 1 and not node:IsCentralNode() then
ImGui.DockNodeHideHostWindow(node)
node.State = 1 --[[ImGuiDockNodeState.HostWindowHiddenBecauseSingleWindow]]
ImGui.DockNodeRemoveWindow(node, window, node.ID) -- Will delete the node so it'll be invalid on return
return
end
ImGui.DockNodeRemoveWindow(node, window, node.ID)
else
-- FIXME-DOCKING: Missing policies for conflict resolution, hence the "Experimental" tag on this.
-- node.LocalFlagsInWindow = bit32.band(node.LocalFlagsInWindow, bit32.bnot(window.WindowClass.DockNodeFlagsOverrideClear))
node.LocalFlagsInWindows = bit32.bor(node.LocalFlagsInWindows, window.WindowClass.DockNodeFlagsOverrideSet)
window_n = window_n + 1
end
end
node:UpdateMergedFlags()
-- Auto-hide tab bar option
local node_flags = node.MergedFlags
if node.WantHiddenTabBarUpdate and node.Windows.Size == 1 and bit32.band(node_flags, 64 --[[ImGuiDockNodeFlags.AutoHideTabBar]]) ~= 0 and not node:IsHiddenTabBar() then
node.WantHiddenTabBarToggle = true
end
node.WantHiddenTabBarUpdate = false
-- Cancel toggling if we know our tab bar is enforced to be hidden at all times
if node.WantHiddenTabBarToggle and node.VisibleWindow and bit32.band(node.VisibleWindow.WindowClass.DockNodeFlagsOverrideSet, 8192 --[[ImGuiDockNodeFlags.HiddenTabBar]]) ~= 0 then
node.WantHiddenTabBarToggle = false
end
-- Apply toggles at a single point of the frame (here!)
local prev_local_flags = node.LocalFlags
if node.Windows.Size > 1 then
node:SetLocalFlags(bit32.band(node.LocalFlags, bit32.bnot(8192 --[[ImGuiDockNodeFlags.HiddenTabBar]])))
elseif node.WantHiddenTabBarToggle then
node:SetLocalFlags(bit32.bxor(node.LocalFlags, 8192 --[[ImGuiDockNodeFlags.HiddenTabBar]]))
end
if bit32.band(bit32.bxor(node.LocalFlags, prev_local_flags), 261152 --[[ImGuiDockNodeFlags.SavedFlagsMask_]]) ~= 0 then
ImGui.MarkIniSettingsDirty() -- Bit flaky to only do this here. Perhaps compare node flags every frame? #9380
end
node.WantHiddenTabBarToggle = false
ImGui.DockNodeUpdateVisibleFlag(node)
end
-- This is rarely called as DockNodeUpdateForRootNode() generally does it most frames.
function ImGui.DockNodeUpdateHasCentralNodeChild(node)
node.HasCentralNodeChild = false
if node.ChildNodes[0] then ImGui.DockNodeUpdateHasCentralNodeChild(node.ChildNodes[0]) end
if node.ChildNodes[1] then ImGui.DockNodeUpdateHasCentralNodeChild(node.ChildNodes[1]) end
if node:IsRootNode() then
local mark_node = node.CentralNode
while mark_node do
mark_node.HasCentralNodeChild = true
mark_node = mark_node.ParentNode
end
end
end
function ImGui.DockNodeUpdateVisibleFlag(node)
-- Update visibility flag
local is_visible = (node.ParentNode == nil) and node:IsDockSpace() or node:IsCentralNode()
is_visible = is_visible or (node.Windows.Size > 0)
is_visible = is_visible or (node.ChildNodes[0] ~= nil and node.ChildNodes[0].IsVisible)
is_visible = is_visible or (node.ChildNodes[1] ~= nil and node.ChildNodes[1].IsVisible)
node.IsVisible = is_visible and true or false
end
function ImGui.DockNodeStartMouseMovingWindow(node, window)
local g = ImGui.GetCurrentContext()
assert(node.WantMouseMove == true)
ImGui.StartMouseMovingWindow(window)
ImVec2_Copy(g.ActiveIdClickOffset, g.IO.MouseClickedPos[0] - node.Pos)
g.MovingWindow = window -- If we are docked into a non moveable root window, StartMouseMovingWindow() won't set g.MovingWindow. Override that decision.
node.WantMouseMove = false
end
-- Update CentralNode, OnlyNodeWithWindows, LastFocusedNodeID. Copy window class.
function ImGui.DockNodeUpdateForRootNode(node)
ImGui.DockNodeUpdateFlagsAndCollapse(node)
-- - Setup central node pointers
-- - Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar -> FIXME-DOCK: that last part is not done yet!)
-- Cannot merge this with DockNodeUpdateFlagsAndCollapse() because FirstNodeWithWindows is found after window removal and child collapsing
local info = ImGuiDockNodeTreeInfo()
DockNodeFindInfo(node, info)
node.CentralNode = info.CentralNode
node.OnlyNodeWithWindows = (info.CountNodesWithWindows == 1) and info.FirstNodeWithWindows or nil
node.CountNodeWithWindows = info.CountNodesWithWindows
if node.LastFocusedNodeId == 0 and info.FirstNodeWithWindows ~= nil then
node.LastFocusedNodeId = info.FirstNodeWithWindows.ID
end
-- Copy the window class from of our first window so it can be used for proper dock filtering.
-- When node has mixed windows, prioritize the class with the most constraint (DockingAllowUnclassed = false) as the reference to copy.
-- FIXME-DOCK: We don't recurse properly, this code could be reworked to work from DockNodeUpdateScanRec.
local first_node_with_windows = info.FirstNodeWithWindows
if first_node_with_windows then
node.WindowClass = first_node_with_windows.Windows.Data[1].WindowClass
for n = 2, first_node_with_windows.Windows.Size do
if first_node_with_windows.Windows.Data[n].WindowClass.DockingAllowUnclassed == false then
node.WindowClass = first_node_with_windows.Windows.Data[n].WindowClass
break
end
end
end
local mark_node = node.CentralNode
while mark_node do
mark_node.HasCentralNodeChild = true
mark_node = mark_node.ParentNode
end
end
local function DockNodeSetupHostWindow(node, host_window)
-- Remove ourselves from any previous different host window
-- This can happen if a user mistakenly does (see #4295 for details):
-- - N+0: DockBuilderAddNode(id, 0) -- missing ImGuiDockNodeFlags.DockSpace
-- - N+1: NewFrame() -- will create floating host window for that node
-- - N+1: DockSpace(id) -- requalify node as dockspace, moving host window
if node.HostWindow and node.HostWindow ~= host_window and node.HostWindow.DockNodeAsHost == node then
node.HostWindow.DockNodeAsHost = nil
end
host_window.DockNodeAsHost = node
node.HostWindow = host_window
end
function ImGui.DockNodeUpdate(node)
local g = ImGui.GetCurrentContext()
assert(node.LastFrameActive ~= g.FrameCount)
node.LastFrameAlive = g.FrameCount
node.IsBgDrawnThisFrame = false
node.CentralNode = nil
node.OnlyNodeWithWindows = nil
if node:IsRootNode() then ImGui.DockNodeUpdateForRootNode(node) end
-- Remove tab bar if not needed
if node.TabBar and node:IsNoTabBar() then ImGui.DockNodeRemoveTabBar(node) end
-- Early out for hidden root dock nodes (when all DockId references are in inactive windows, or there is only 1 floating window holding on the DockId)
local want_to_hide_host_window = false
if node:IsFloatingNode() then
if node.Windows.Size <= 1 and node:IsLeafNode() then
if not g.IO.ConfigDockingAlwaysTabBar and (node.Windows.Size == 0 or not node.Windows.Data[1].WindowClass.DockingAlwaysTabBar) then
want_to_hide_host_window = true
end
end
if node.CountNodeWithWindows == 0 then want_to_hide_host_window = true end
end
if want_to_hide_host_window then
if node.Windows.Size == 1 then
-- Floating window pos/size is authoritative
local single_window = node.Windows.Data[1]
node.Pos = ImVec2(single_window.Pos.x, single_window.Pos.y)
node.Size = ImVec2(single_window.SizeFull.x, single_window.SizeFull.y)
node.AuthorityForPos = 2 --[[ImGuiDataAuthority.Window]]
node.AuthorityForSize = 2 --[[ImGuiDataAuthority.Window]]
node.AuthorityForViewport = 2 --[[ImGuiDataAuthority.Window]]
-- Transfer focus immediately so when we revert to a regular window it is immediately selected
if node.HostWindow and g.NavWindow == node.HostWindow then
ImGui.FocusWindow(single_window)
end
if node.HostWindow then
single_window.Viewport = node.HostWindow.Viewport
single_window.ViewportId = node.HostWindow.ViewportId
end
node.RefViewportId = single_window.ViewportId or 0
end
ImGui.DockNodeHideHostWindow(node)
node.State = 1 --[[ImGuiDockNodeState.HostWindowHiddenBecauseSingleWindow]]
node.WantCloseAll = false
node.WantCloseTabId = 0
node.HasCloseButton = false
node.HasWindowMenuButton = false
node.LastFrameActive = g.FrameCount
if node.WantMouseMove and node.Windows.Size == 1 then
ImGui.DockNodeStartMouseMovingWindow(node, node.Windows.Data[1])
end
return
end
-- In some circumstance we will defer creating the host window (so everything will be kept hidden),
-- while the expected visible window is resizing itself.
-- This is important for first-time (no ini settings restored) single window when io.ConfigDockingAlwaysTabBar is enabled,
-- otherwise the node ends up using the minimum window size. Effectively those windows will take an extra frame to show up:
-- N+0: Begin(): window created (with no known size), node is created
-- N+1: DockNodeUpdate(): node skip creating host window / Begin(): window size applied, not visible
-- N+2: DockNodeUpdate(): node can create host window / Begin(): window becomes visible
-- We could remove this frame if we could reliably calculate the expected window size during node update, before the Begin() code.
-- It would require a generalization of CalcWindowExpectedSize(), probably extracting code away from Begin().
-- In reality it isn't very important as user quickly ends up with size data in .ini file.
if node.IsVisible and node.HostWindow == nil and node:IsFloatingNode() and node:IsLeafNode() then
assert(node.Windows.Size > 0)
local ref_window = nil
-- Note that we prune single-window-node settings on .ini loading, so this is generally 0 for them!
if node.SelectedTabId ~= 0 then ref_window = ImGui.DockNodeFindWindowByID(node, node.SelectedTabId) end
if ref_window == nil then ref_window = node.Windows.Data[1] end
if ref_window.AutoFitFramesX > 0 or ref_window.AutoFitFramesY > 0 then
node.State = 2 --[[ImGuiDockNodeState.HostWindowHiddenBecauseWindowsAreResizing]]
return
end
end
local node_flags = node.MergedFlags
-- Decide if the node will have a close button and a window menu button
node.HasWindowMenuButton = (node.Windows.Size > 0) and bit32.band(node_flags, 16384 --[[ImGuiDockNodeFlags.NoWindowMenuButton]]) == 0
node.HasCloseButton = false
for _, window in node.Windows:iter() do
-- FIXME-DOCK: Setting DockIsActive here means that for single active window in a leaf node, DockIsActive will be cleared until the next Begin() call.
node.HasCloseButton = node.HasCloseButton or window.HasCloseButton
window.DockIsActive = (node.Windows.Size > 1)
end
if bit32.band(node_flags, 32768 --[[ImGuiDockNodeFlags.NoCloseButton]]) ~= 0 or not g.Style.DockingNodeHasCloseButton then
node.HasCloseButton = false
end
-- Bind or create host window
local host_window = nil
local beginned_into_host_window = false
if node:IsDockSpace() then
-- [Explicit root dockspace node]
assert(node.HostWindow)
host_window = node.HostWindow
else
-- [Automatic root or child nodes]
if node:IsRootNode() and node.IsVisible then
local ref_window = (node.Windows.Size > 0) and node.Windows.Data[1] or nil
-- Sync Pos
if node.AuthorityForPos == 2 --[[ImGuiDataAuthority.Window]] and ref_window then
ImGui.SetNextWindowPos(ref_window.Pos)
elseif node.AuthorityForPos == 1 --[[ImGuiDataAuthority.DockNode]] then
ImGui.SetNextWindowPos(node.Pos)
end
-- Sync Size
if node.AuthorityForSize == 2 --[[ImGuiDataAuthority.Window]] and ref_window then
ImGui.SetNextWindowSize(ref_window.SizeFull)
elseif node.AuthorityForSize == 1 --[[ImGuiDataAuthority.DockNode]] then
ImGui.SetNextWindowSize(node.Size)
end
-- Sync Collapsed
if node.AuthorityForSize == 2 --[[ImGuiDataAuthority.Window]] and ref_window then
ImGui.SetNextWindowCollapsed(ref_window.Collapsed)
end
ImGui.SetNextWindowClass(node.WindowClass)
-- Begin into the host window
local window_label = DockNodeGetHostWindowTitle(node)
local window_flags = bit32.bor(8 --[[ImGuiWindowFlags.NoScrollbar]], 16 --[[ImGuiWindowFlags.NoScrollWithMouse]], 8388608 --[[ImGuiWindowFlags.DockNodeHost]])
window_flags = bit32.bor(window_flags, 4096 --[[ImGuiWindowFlags.NoFocusOnAppearing]])
window_flags = bit32.bor(window_flags, 256 --[[ImGuiWindowFlags.NoSavedSettings]], 131072 --[[ImGuiWindowFlags.NoNavFocus]], 32 --[[ImGuiWindowFlags.NoCollapse]])
window_flags = bit32.bor(window_flags, 1 --[[ImGuiWindowFlags.NoTitleBar]])
ImGui.SetNextWindowBgAlpha(0.0) -- Don't set ImGuiWindowFlags.NoBackground because it disables borders
ImGui.PushStyleVar(2 --[[ImGuiStyleVar.WindowPadding]], ImVec2(0, 0))
ImGui.Begin(window_label, nil, window_flags)
ImGui.PopStyleVar()
beginned_into_host_window = true
host_window = g.CurrentWindow
DockNodeSetupHostWindow(node, host_window)
ImVec2_Copy(host_window.DC.CursorPos, host_window.Pos)
node.Pos = ImVec2(host_window.Pos.x, host_window.Pos.y)
node.Size = ImVec2(host_window.Size.x, host_window.Size.y)
-- We set ImGuiWindowFlags.NoFocusOnAppearing because we don't want the host window to take full focus (e.g. steal NavWindow)
-- But we still it bring it to the front of display. There's no way to choose this precise behavior via window flags.
-- One simple case to ponder if: window A has a toggle to create windows B/C/D. Dock B/C/D together, clear the toggle and enable it again.
-- When reappearing B/C/D will request focus and be moved to the top of the display pile, but they are not linked to the dock host window
-- during the frame they appear. The dock host window would keep its old display order, and the sorting in EndFrame would move B/C/D back
-- after the dock host window, losing their top-most status.
if node.HostWindow.Appearing then ImGui.BringWindowToDisplayFront(node.HostWindow) end
node.AuthorityForPos = 0 --[[ImGuiDataAuthority.Auto]]
node.AuthorityForSize = 0 --[[ImGuiDataAuthority.Auto]]
node.AuthorityForViewport = 0 --[[ImGuiDataAuthority.Auto]]
elseif node.ParentNode then
host_window = node.ParentNode.HostWindow
node.HostWindow = host_window
node.AuthorityForPos = 0 --[[ImGuiDataAuthority.Auto]]
node.AuthorityForSize = 0 --[[ImGuiDataAuthority.Auto]]
node.AuthorityForViewport = 0 --[[ImGuiDataAuthority.Auto]]
end
if node.WantMouseMove and node.HostWindow then
ImGui.DockNodeStartMouseMovingWindow(node, node.HostWindow)
end
end
node.RefViewportId = 0 -- Clear when we have a host window
-- Update focused node (the one whose title bar is highlight) within a node tree
if node:IsSplitNode() then assert(node.TabBar == nil) end
if node:IsRootNode() then
local p_window = g.NavWindow and g.NavWindow.RootWindow or nil
while p_window ~= nil and p_window.DockNode ~= nil do
local p_node = ImGui.DockNodeGetRootNode(p_window.DockNode)
if p_node == node then
node.LastFocusedNodeId = p_window.DockNode.ID -- Note: not using root node ID!
break
end
p_window = p_node.HostWindow and p_node.HostWindow.RootWindow or nil
end
end
-- Register a hit-test hole in the window unless we are currently dragging a window that is compatible with our dockspace
local central_node = node.CentralNode
local central_node_hole = node:IsRootNode() and host_window ~= nil and bit32.band(node_flags, 8 --[[ImGuiDockNodeFlags.PassthruCentralNode]]) ~= 0 and central_node ~= nil and central_node:IsEmpty()
local central_node_hole_register_hit_test_hole = central_node_hole
if central_node_hole then
local payload = ImGui.GetDragDropPayload()
if payload and ImGuiPayload_IsDataType(payload, IMGUI_PAYLOAD_TYPE_WINDOW) and ImGui.DockNodeIsDropAllowed(host_window, payload.Data) then
central_node_hole_register_hit_test_hole = false
end
end
if central_node_hole_register_hit_test_hole then
-- We add a little padding to match the "resize from edges" behavior and allow grabbing the splitter easily.
-- (But we only add it if there's something else on the other side of the hole, otherwise for e.g. fullscreen
-- covering passthru node we'd have a gap on the edge not covered by the hole)
assert(node:IsDockSpace()) -- We cannot pass this flag without the DockSpace() api. Testing this because we also setup the hole in host_window.ParentNode
local root_node = ImGui.DockNodeGetRootNode(central_node)
local root_rect = ImRect(root_node.Pos, root_node.Pos + root_node.Size)
local hole_rect = ImRect(central_node.Pos, central_node.Pos + central_node.Size)
if hole_rect.Min.x > root_rect.Min.x then hole_rect.Min.x = hole_rect.Min.x + g.WindowsBorderHoverPadding end
if hole_rect.Max.x < root_rect.Max.x then hole_rect.Max.x = hole_rect.Max.x - g.WindowsBorderHoverPadding end
if hole_rect.Min.y > root_rect.Min.y then hole_rect.Min.y = hole_rect.Min.y + g.WindowsBorderHoverPadding end
if hole_rect.Max.y < root_rect.Max.y then hole_rect.Max.y = hole_rect.Max.y - g.WindowsBorderHoverPadding end
-- GetForegroundDrawList():AddRect(hole_rect.Min, hole_rect.Max, IM_COL32(255, 0, 0, 255))
if central_node_hole and not IsRectInverted(hole_rect) then
ImGui.SetWindowHitTestHole(host_window, hole_rect.Min, hole_rect.Max - hole_rect.Min)
if host_window.ParentWindow then
ImGui.SetWindowHitTestHole(host_window.ParentWindow, hole_rect.Min, hole_rect.Max - hole_rect.Min)
end
end
end
-- Update position/size, process and draw resizing splitters
if node:IsRootNode() and host_window then
ImGui.DockNodeTreeUpdatePosSize(node, host_window.Pos, host_window.Size)
ImGui.PushStyleColor(28 --[[ImGuiCol.Separator]], g.Style.Colors[5 --[[ImGuiCol.Border]]])
ImGui.PushStyleColor(30 --[[ImGuiCol.SeparatorActive]], g.Style.Colors[33 --[[ImGuiCol.ResizeGripActive]]])
ImGui.PushStyleColor(29 --[[ImGuiCol.SeparatorHovered]], g.Style.Colors[32 --[[ImGuiCol.ResizeGripHovered]]])
ImGui.DockNodeTreeUpdateSplitter(node)
ImGui.PopStyleColor(3)
end
-- Draw empty node background (currently can only be the Central Node)
if host_window and node:IsEmpty() and node.IsVisible then
host_window.DrawList:ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG)
node.LastBgColor = (bit32.band(node_flags, 8 --[[ImGuiDockNodeFlags.PassthruCentralNode]]) ~= 0) and 0 or ImGui.GetColorU32(43 --[[ImGuiCol.DockingEmptyBg]])
if node.LastBgColor ~= 0 then
host_window.DrawList:AddRectFilled(node.Pos, node.Pos + node.Size, node.LastBgColor)
end
node.IsBgDrawnThisFrame = true
end
-- Draw whole dockspace background if ImGuiDockNodeFlags.PassthruCentralNode if set.
-- We need to draw a background at the root level if requested by ImGuiDockNodeFlags.PassthruCentralNode, but we will only know the correct pos/size
-- _after_ processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order!
local render_dockspace_bg = node:IsRootNode() and host_window ~= nil and bit32.band(node_flags, 8 --[[ImGuiDockNodeFlags.PassthruCentralNode]]) ~= 0
if render_dockspace_bg and node.IsVisible then
host_window.DrawList:ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG)
if central_node_hole then
ImGui.RenderRectFilledWithHole(host_window.DrawList, node:Rect(), central_node:Rect(), ImGui.GetColorU32(2 --[[ImGuiCol.WindowBg]]), 0.0)
else
host_window.DrawList:AddRectFilled(node.Pos, node.Pos + node.Size, ImGui.GetColorU32(2 --[[ImGuiCol.WindowBg]]), 0.0)
end
end
-- Draw and populate Tab Bar
if host_window then host_window.DrawList:ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG) end
if host_window and node.Windows.Size > 0 then
ImGui.DockNodeUpdateTabBar(node, host_window)
else
node.WantCloseAll = false
node.WantCloseTabId = 0
node.IsFocused = false
end
if node.TabBar and node.TabBar.SelectedTabId ~= 0 then
node.SelectedTabId = node.TabBar.SelectedTabId
elseif node.Windows.Size > 0 then
node.SelectedTabId = node.Windows.Data[1].TabId
end
-- Draw payload drop target
if host_window and node.IsVisible then
if node:IsRootNode() and (g.MovingWindow == nil or g.MovingWindow.RootWindowDockTree ~= host_window) then
ImGui.BeginDockableDragDropTarget(host_window)
end
end
-- We update this after DockNodeUpdateTabBar()
node.LastFrameActive = g.FrameCount
-- Recurse into children
-- FIXME-DOCK FIXME-OPT: Should not need to recurse into children
if host_window then
if node.ChildNodes[0] then ImGui.DockNodeUpdate(node.ChildNodes[0]) end
if node.ChildNodes[1] then ImGui.DockNodeUpdate(node.ChildNodes[1]) end
-- Render outer borders last (after the tab bar)
if node:IsRootNode() then ImGui.RenderWindowOuterBorders(host_window) end
end
-- End host window
if beginned_into_host_window then ImGui.End() end
end
local GWindowDockStyleColors = {
[0] = ImGuiCol.Text, ImGuiCol.TabHovered, ImGuiCol.Tab, ImGuiCol.TabSelected, ImGuiCol.TabSelectedOverline,
ImGuiCol.TabDimmed, ImGuiCol.TabDimmedSelected, ImGuiCol.TabDimmedSelectedOverline, ImGuiCol.UnsavedMarker,
}
ImGui.GWindowDockStyleColors = GWindowDockStyleColors
-- Compare TabItem nodes given the last known DockOrder (will persist in .ini file as hint), used to sort tabs when multiple tabs are added on the same frame.
local function TabItemComparerByDockOrder(ta, tb)
local a, b = ta.Window, tb.Window
local ao = (a.DockOrder == -1) and 2147483647 --[[INT_MAX]] or a.DockOrder
local bo = (b.DockOrder == -1) and 2147483647 --[[INT_MAX]] or b.DockOrder
if ao ~= bo then return ao < bo end
return a.BeginOrderWithinContext < b.BeginOrderWithinContext
end
-- Default handler for g.DockNodeWindowMenuHandler(): display the list of windows for a given dock-node.
-- This is exceptionally stored in a function pointer to also user applications to tweak this menu (undocumented)
-- Custom overrides may want to decorate, group, sort entries.
-- Please note those are internal structures: if you copy this expect occasional breakage.
-- (if you don't need to modify the "Tabs.Size == 1" behavior/path it is recommend you call this function in your handler)
function ImGui.DockNodeWindowMenuHandler_Default(ctx, node, tab_bar)
if tab_bar.Tabs.Size == 1 then
-- "Hide tab bar" option. Being one of our rare user-facing string we pull it from a table.
if ImGui.MenuItem(ImGui.LocalizeGetMsg(13 --[[ImGuiLocKey.DockingHideTabBar]]), nil, node:IsHiddenTabBar()) then
node.WantHiddenTabBarToggle = true
end
else
-- Display a selectable list of windows in this docking node
for tab_n = 1, tab_bar.Tabs.Size do
local tab = tab_bar.Tabs.Data[tab_n]
if bit32.band(tab.Flags, 2097152 --[[ImGuiTabItemFlags.Button]]) == 0 then
if ImGui.Selectable(ImGui.TabBarGetTabName(tab_bar, tab), tab.ID == tab_bar.SelectedTabId) then
ImGui.TabBarQueueFocus(tab_bar, tab)
end
ImGui.SameLine()
ImGui.Text(" ")
end
end
end
end
function ImGui.DockNodeWindowMenuUpdate(node, tab_bar)
-- Try to position the menu so it is more likely to stays within the same viewport
local g = ImGui.GetCurrentContext()
if g.Style.WindowMenuButtonPosition == 0 --[[ImGuiDir.Left]] then
ImGui.SetNextWindowPos(ImVec2(node.Pos.x, node.Pos.y + ImGui.GetFrameHeight()), 1 --[[ImGuiCond.Always]], ImVec2(0.0, 0.0))
else
ImGui.SetNextWindowPos(ImVec2(node.Pos.x + node.Size.x, node.Pos.y + ImGui.GetFrameHeight()), 1 --[[ImGuiCond.Always]], ImVec2(1.0, 0.0))
end
if ImGui.BeginPopup("#WindowMenu") then
node.IsFocused = true
g.DockNodeWindowMenuHandler(g, node, tab_bar)
ImGui.EndPopup()
end
end
-- User helper to append/amend into a dock node tab bar. Most commonly used to add e.g. a "+" button.
function ImGui.DockNodeBeginAmendTabBar(node)
if node.TabBar == nil or node.HostWindow == nil then return false end
if bit32.band(node.MergedFlags, 1 --[[ImGuiDockNodeFlags.KeepAliveOnly]]) ~= 0 then return false end
if node.TabBar.ID == 0 then return false end
ImGui.Begin(node.HostWindow.Name)
ImGui.PushOverrideID(node.ID)
local ret = ImGui.BeginTabBarEx(node.TabBar, node.TabBar.BarRect, node.TabBar.Flags)
assert(ret)
return true
end
function ImGui.DockNodeEndAmendTabBar()
ImGui.EndTabBar()
ImGui.PopID()
ImGui.End()
end
local function IsDockNodeTitleBarHighlighted(node, root_node)
-- CTRL+Tab highlight (only highlighting leaf node, not whole hierarchy)
local g = ImGui.GetCurrentContext()
if g.NavWindowingTarget then
return g.NavWindowingTarget.DockNode == node
end
-- FIXME-DOCKING: May want alternative to treat central node void differently? e.g. if (g.NavWindow == host_window)
if g.NavWindow and root_node.LastFocusedNodeId == node.ID then
-- FIXME: This could all be backed in RootWindowForTitleBarHighlight? Probably need to reorganize for both dock nodes + other RootWindowForTitleBarHighlight users (not-node)
local parent_window = g.NavWindow.RootWindow
while bit32.band(parent_window.Flags, 268435456 --[[ImGuiWindowFlags.ChildMenu]]) ~= 0 do
parent_window = parent_window.ParentWindow.RootWindow
end
local parent_node = parent_window.DockNodeAsHost or parent_window.DockNode
while parent_node ~= nil do
parent_node = ImGui.DockNodeGetRootNode(parent_node)
if parent_node == root_node then return true end
parent_node = parent_node.HostWindow and parent_node.HostWindow.RootWindow.DockNode or nil
end
end
return false
end
-- Submit the tab bar corresponding to a dock node and various housekeeping details.
function ImGui.DockNodeUpdateTabBar(node, host_window)
local g = ImGui.GetCurrentContext()
local style = g.Style
local node_was_active = (node.LastFrameActive + 1 == g.FrameCount)
node.WantCloseAll = false
node.WantCloseTabId = 0
-- Decide if we should use a focused title bar color
local is_focused = false
local root_node = ImGui.DockNodeGetRootNode(node)
if IsDockNodeTitleBarHighlighted(node, root_node) then is_focused = true end
-- Hidden tab bar will show a triangle on the upper-left (in Begin)
if node:IsHiddenTabBar() or node:IsNoTabBar() then
node.VisibleWindow = (node.Windows.Size > 0) and node.Windows.Data[1] or nil
node.IsFocused = is_focused
if is_focused then node.LastFrameFocused = g.FrameCount end
if node.VisibleWindow then
-- Notify root of visible window (used to display title in OS task bar)
if is_focused or root_node.VisibleWindow == nil then root_node.VisibleWindow = node.VisibleWindow end
if node.TabBar then node.TabBar.VisibleTabId = node.VisibleWindow.TabId end
end
return
end
-- Move ourselves to the Menu layer (so we can be accessed by tapping Alt) + undo SkipItems flag in order to draw over the title bar even if the window is collapsed
local backup_skip_item = host_window.SkipItems
if not node:IsDockSpace() then
host_window.SkipItems = false
host_window.DC.NavLayerCurrent = 1 --[[ImGuiNavLayer.Menu]]
end
-- Use PushOverrideID() instead of PushID() to use the node id _without_ the host window ID.
-- This is to facilitate computing those ID from the outside, and will affect more or less only the ID of the collapse button, popup and tabs,
-- as docked windows themselves will override the stack with their own root ID.
ImGui.PushOverrideID(node.ID)
local tab_bar = node.TabBar
local tab_bar_is_recreated = (tab_bar == nil) -- Tab bar are automatically destroyed when a node gets hidden
if tab_bar == nil then
ImGui.DockNodeAddTabBar(node)
tab_bar = node.TabBar
end
local focus_tab_id = 0
node.IsFocused = is_focused
local node_flags = node.MergedFlags
local has_window_menu_button = bit32.band(node_flags, 16384 --[[ImGuiDockNodeFlags.NoWindowMenuButton]]) == 0 and (style.WindowMenuButtonPosition ~= (-1) --[[ImGuiDir.None]])
-- In a dock node, the Collapse Button turns into the Window Menu button.
-- FIXME-DOCK FIXME-OPT: Could we recycle popups id across multiple dock nodes?
if has_window_menu_button and ImGui.IsPopupOpen("#WindowMenu") then
local next_selected_tab_id = tab_bar.NextSelectedTabId
ImGui.DockNodeWindowMenuUpdate(node, tab_bar)
if tab_bar.NextSelectedTabId ~= 0 and tab_bar.NextSelectedTabId ~= next_selected_tab_id then
focus_tab_id = tab_bar.NextSelectedTabId
end
is_focused = is_focused or node.IsFocused
end
-- Layout
local title_bar_rect, tab_bar_rect, window_menu_button_pos, close_button_pos = ImGui.DockNodeCalcTabBarLayout(node)
-- Submit new tabs, they will be added as Unsorted and sorted below based on relative DockOrder value.
local tabs_count_old = tab_bar.Tabs.Size
for window_n = 1, node.Windows.Size do
local window = node.Windows.Data[window_n]
if ImGui.TabBarFindTabByID(tab_bar, window.TabId) == nil then
ImGui.TabBarAddTab(tab_bar, 8388608 --[[ImGuiTabItemFlags.Unsorted]], window)
end
end
-- Title bar
if is_focused then node.LastFrameFocused = g.FrameCount end
local title_bar_col = ImGui.GetColorU32(host_window.Collapsed and 12 --[[ImGuiCol.TitleBgCollapsed]] or (is_focused and 11 --[[ImGuiCol.TitleBgActive]] or 10 --[[ImGuiCol.TitleBg]]))
local rounding_flags = ImGui.CalcRoundingFlagsForRectInRect(title_bar_rect, host_window:Rect(), g.Style.DockingSeparatorSize)
host_window.DrawList:AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window.WindowRounding, rounding_flags)
-- Docking/Collapse button
if has_window_menu_button then
if ImGui.CollapseButton(host_window:GetID("#COLLAPSE"), window_menu_button_pos, node) then -- == DockNodeGetWindowMenuButtonId(node)
ImGui.OpenPopup("#WindowMenu")
end
if ImGui.IsItemActive() then focus_tab_id = tab_bar.SelectedTabId end
if ImGui.IsItemHovered(bit32.bor(4096 --[[ImGuiHoveredFlags.ForTooltip]], 65536 --[[ImGuiHoveredFlags.DelayNormal]])) and g.HoveredIdTimer > 0.5 then
ImGui.SetTooltip("%s", ImGui.LocalizeGetMsg(15 --[[ImGuiLocKey.DockingDragToUndockOrMoveNode]]))
end
end
-- If multiple tabs are appearing on the same frame, sort them based on their persistent DockOrder value
local tabs_unsorted_start = tab_bar.Tabs.Size -- 0-based
local tab_n = tab_bar.Tabs.Size - 1
while tab_n >= 0 and bit32.band(tab_bar.Tabs.Data[tab_n + 1].Flags, 8388608 --[[ImGuiTabItemFlags.Unsorted]]) ~= 0 do
-- FIXME-DOCK: Consider only clearing the flag after the tab has been alive for a few consecutive frames, allowing late comers to not break sorting?
local t = tab_bar.Tabs.Data[tab_n + 1]
t.Flags = bit32.band(t.Flags, bit32.bnot(8388608 --[[ImGuiTabItemFlags.Unsorted]]))
tabs_unsorted_start = tab_n
tab_n = tab_n - 1
end
if tab_bar.Tabs.Size > tabs_unsorted_start + 1 then
local slice = {}
for i = tabs_unsorted_start + 1, tab_bar.Tabs.Size do slice[#slice + 1] = tab_bar.Tabs.Data[i] end
table.sort(slice, TabItemComparerByDockOrder)
for i, t in ipairs(slice) do tab_bar.Tabs.Data[tabs_unsorted_start + i] = t end
end
-- Apply NavWindow focus back to the tab bar
if g.NavWindow and g.NavWindow.RootWindow.DockNode == node then
tab_bar.SelectedTabId = g.NavWindow.RootWindow.TabId
end
-- Selected newly added tabs, or persistent tab ID if the tab bar was just recreated
if tab_bar_is_recreated and ImGui.TabBarFindTabByID(tab_bar, node.SelectedTabId) ~= nil then
tab_bar.SelectedTabId = node.SelectedTabId
tab_bar.NextSelectedTabId = node.SelectedTabId
elseif tab_bar.Tabs.Size > tabs_count_old then
tab_bar.SelectedTabId = tab_bar.Tabs.Data[tab_bar.Tabs.Size].Window.TabId
tab_bar.NextSelectedTabId = tab_bar.SelectedTabId
end
-- Begin tab bar
local tab_bar_flags = bit32.bor(1 --[[ImGuiTabBarFlags.Reorderable]], 2 --[[ImGuiTabBarFlags.AutoSelectNewTabs]]) -- | ImGuiTabBarFlags.NoTabListScrollingButtons)
tab_bar_flags = bit32.bor(tab_bar_flags, 4194304 --[[ImGuiTabBarFlags.SaveSettings]], 1048576 --[[ImGuiTabBarFlags.DockNode]])
tab_bar_flags = bit32.bor(tab_bar_flags, 128 --[[ImGuiTabBarFlags.FittingPolicyMixed]]) -- Enforce default policy. Since 1.92.2 this is now reasonable. May expose later if needed. (#8800, #3421)
tab_bar_flags = bit32.bor(tab_bar_flags, 64 --[[ImGuiTabBarFlags.DrawSelectedOverline]])
if not host_window.Collapsed and is_focused then
tab_bar_flags = bit32.bor(tab_bar_flags, 2097152 --[[ImGuiTabBarFlags.IsFocused]])
end
tab_bar.ID = node.ID -- GetID("#TabBar")
tab_bar.SeparatorMinX = node.Pos.x + host_window.WindowBorderSize -- Separator cover the whole node width
tab_bar.SeparatorMaxX = node.Pos.x + node.Size.x - host_window.WindowBorderSize
ImGui.BeginTabBarEx(tab_bar, tab_bar_rect, tab_bar_flags)
-- host_window.DrawList:AddRect(tab_bar_rect.Min, tab_bar_rect.Max, IM_COL32(255,0,255,255))
-- Backup style colors
local backup_style_cols = {}
for color_n = 0, 9 --[[ImGuiWindowDockStyleCol.COUNT]] - 1 do
local c = g.Style.Colors[GWindowDockStyleColors[color_n]]
backup_style_cols[color_n] = ImVec4(c.x, c.y, c.z, c.w)
end
-- Submit actual tabs
node.VisibleWindow = nil
for window_n = 1, node.Windows.Size do
local window = node.Windows.Data[window_n]
-- FIXME: Not sure if that's still taken/useful, as windows are normally removed in DockNodeUpdateFlagsAndCollapse().
if not (window.LastFrameActive + 1 < g.FrameCount and node_was_active) then
local tab_item_flags = 0
tab_item_flags = bit32.bor(tab_item_flags, window.WindowClass.TabItemFlagsOverrideSet or 0)
if bit32.band(window.Flags, 262144 --[[ImGuiWindowFlags.UnsavedDocument]]) ~= 0 then
tab_item_flags = bit32.bor(tab_item_flags, 1 --[[ImGuiTabItemFlags.UnsavedDocument]])
end
if bit32.band(tab_bar.Flags, 8 --[[ImGuiTabBarFlags.NoCloseWithMiddleMouseButton]]) ~= 0 then
tab_item_flags = bit32.bor(tab_item_flags, 4 --[[ImGuiTabItemFlags.NoCloseWithMiddleMouseButton]])
end
-- Apply stored style overrides for the window
for color_n = 0, 9 --[[ImGuiWindowDockStyleCol.COUNT]] - 1 do
ImVec4_Copy(g.Style.Colors[GWindowDockStyleColors[color_n]], ImGui.ColorConvertU32ToFloat4(window.DockStyle.Colors[color_n]))
end
-- Note that TabItemEx() calls TabBarCalcTabID() so our tab item ID will ignore the current ID stack (rightly so)
local _, tab_open = ImGui.TabItemEx(tab_bar, window.Name, window.HasCloseButton and true or nil, tab_item_flags, window)
if tab_open == false then node.WantCloseTabId = window.TabId end
if tab_bar.VisibleTabId == window.TabId then node.VisibleWindow = window end
-- Store last item data so it can be queried with IsItemXXX functions after the user Begin() call
window.DC.DockTabItemStatusFlags = g.LastItemData.StatusFlags
window.DC.DockTabItemRect = ImRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max)
-- Update navigation ID on menu layer
if g.NavWindow and g.NavWindow.RootWindow == window and bit32.band(window.DC.NavLayersActiveMask, bit32.lshift(1, 1 --[[ImGuiNavLayer.Menu]])) == 0 then
host_window.NavLastIds[1] = window.TabId
end
end
end
-- Restore style colors
for color_n = 0, 9 --[[ImGuiWindowDockStyleCol.COUNT]] - 1 do
ImVec4_Copy(g.Style.Colors[GWindowDockStyleColors[color_n]], backup_style_cols[color_n])
end
-- Notify root of visible window (used to display title in OS task bar)
if node.VisibleWindow then
if is_focused or root_node.VisibleWindow == nil then root_node.VisibleWindow = node.VisibleWindow end
end
-- Close button (after VisibleWindow was updated)
-- Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow.TabId may be ~= from tab_bar.SelectedTabId
local close_button_is_enabled = node.HasCloseButton and node.VisibleWindow and node.VisibleWindow.HasCloseButton
local close_button_is_visible = node.HasCloseButton
-- local close_button_is_visible = close_button_is_enabled -- Most people would expect this behavior of not even showing the button (leaving a hole since we can't claim that space as other windows in the tba bar have one)
if close_button_is_visible then
if not close_button_is_enabled then
ImGui.PushItemFlag(64 --[[ImGuiItemFlags.Disabled]], true)
local tc = style.Colors[0 --[[ImGuiCol.Text]]]
ImGui.PushStyleColor(0 --[[ImGuiCol.Text]], ImVec4(tc.x, tc.y, tc.z, tc.w * 0.4))
end
if ImGui.CloseButton(host_window:GetID("#CLOSE"), close_button_pos) then
node.WantCloseAll = true
for n = 1, tab_bar.Tabs.Size do ImGui.TabBarCloseTab(tab_bar, tab_bar.Tabs.Data[n]) end
end
-- if IsItemActive() then
-- focus_tab_id = tab_bar.SelectedTabId
if not close_button_is_enabled then
ImGui.PopStyleColor()
ImGui.PopItemFlag()
end
end
-- When clicking on the title bar outside of tabs, we still focus the selected tab for that node
-- FIXME: TabItems submitted earlier use AllowItemOverlap so we manually perform a more specific test for now (hovered || held) in order to not cover them.
local title_bar_id = host_window:GetID("#TITLEBAR")
if g.HoveredId == 0 or g.HoveredId == title_bar_id or g.ActiveId == title_bar_id then
-- AllowOverlap mode required for appending into dock node tab bar,
-- otherwise dragging window will steal HoveredId and amended tabs cannot get them.
ImGui.KeepAliveID(title_bar_id)
local _, _, held = ImGui.ButtonBehavior(title_bar_rect, title_bar_id, 4096 --[[ImGuiButtonFlags.AllowOverlap]])
if g.HoveredId == title_bar_id then g.LastItemData.ID = title_bar_id end
if held then
if ImGui.IsMouseClicked(0) then focus_tab_id = tab_bar.SelectedTabId end
-- Forward moving request to selected window
local tab = ImGui.TabBarFindTabByID(tab_bar, tab_bar.SelectedTabId)
if tab then
ImGui.StartMouseMovingWindowOrNode(tab.Window or node.HostWindow, node, false) -- Undock from tab bar empty space
end
end
end
-- Forward focus from host node to selected window
-- if is_focused and g.NavWindow == host_window and not g.NavWindowingTarget then
-- focus_tab_id = tab_bar.SelectedTabId
-- When clicked on a tab we requested focus to the docked child
-- This overrides the value set by "forward focus from host node to selected window".
if tab_bar.NextSelectedTabId ~= 0 then focus_tab_id = tab_bar.NextSelectedTabId end
-- Apply navigation focus
if focus_tab_id ~= 0 then
local tab = ImGui.TabBarFindTabByID(tab_bar, focus_tab_id)
if tab and tab.Window then
ImGui.FocusWindow(tab.Window)
if g.NavId == 0 then ImGui.NavInitWindow(tab.Window, false) end -- only init if FocusWindow() didn't restore anything.
end
end
ImGui.EndTabBar()
ImGui.PopID()
-- Restore SkipItems flag
if not node:IsDockSpace() then
host_window.DC.NavLayerCurrent = 0 --[[ImGuiNavLayer.Main]]
host_window.SkipItems = backup_skip_item
end
end
function ImGui.DockNodeAddTabBar(node)
assert(node.TabBar == nil)
node.TabBar = ImGuiTabBar()
end
function ImGui.DockNodeRemoveTabBar(node)
if node.TabBar == nil then return end
node.TabBar = nil
end
local function DockNodeIsDropAllowedOne(payload, host_window)
if host_window.DockNodeAsHost and host_window.DockNodeAsHost:IsDockSpace() and payload.BeginOrderWithinContext < host_window.BeginOrderWithinContext then
return false
end
local host_class = host_window.DockNodeAsHost and host_window.DockNodeAsHost.WindowClass or host_window.WindowClass
local payload_class = payload.WindowClass
if host_class.ClassId ~= payload_class.ClassId then
local pass = false
if host_class.ClassId ~= 0 and host_class.DockingAllowUnclassed and payload_class.ClassId == 0 then pass = true end
if payload_class.ClassId ~= 0 and payload_class.DockingAllowUnclassed and host_class.ClassId == 0 then pass = true end
if not pass then return false end
end
-- Prevent docking any window created above a popup
-- Technically we should support it (e.g. in the case of a long-lived modal window that had fancy docking features),
-- by e.g. adding a 'if not ImGui.IsWindowWithinBeginStackOf(host_window, popup_window)' test.
-- But it would requires more work on our end because the dock host windows is technically created in NewFrame()
-- and our .ParentXXX and .RootXXX pointers inside windows are currently mislading or lacking.
local g = ImGui.GetCurrentContext()
for i = g.OpenPopupStack.Size, 1, -1 do
local popup_window = g.OpenPopupStack.Data[i].Window
if popup_window and ImGui.IsWindowWithinBeginStackOf(payload, popup_window) then -- Payload is created from within a popup begin stack.
return false
end
end
return true
end
function ImGui.DockNodeIsDropAllowed(host_window, root_payload)
if root_payload.DockNodeAsHost and root_payload.DockNodeAsHost:IsSplitNode() then return true end -- FIXME-DOCK: Missing filtering
local payload_count = root_payload.DockNodeAsHost and root_payload.DockNodeAsHost.Windows.Size or 1
for payload_n = 1, payload_count do
local payload = root_payload.DockNodeAsHost and root_payload.DockNodeAsHost.Windows.Data[payload_n] or root_payload
if DockNodeIsDropAllowedOne(payload, host_window) then return true end
end
return false
end
-- window menu button == collapse button when not in a dock node.
-- FIXME: This is similar to RenderWindowTitleBarContents(), may want to share code.
--- @return ImRect title_rect, ImRect tab_bar_rect, ImVec2 window_menu_button_pos, ImVec2? close_button_pos
function ImGui.DockNodeCalcTabBarLayout(node)
local g = ImGui.GetCurrentContext()
local style = g.Style
local r = ImRect(node.Pos.x, node.Pos.y, node.Pos.x + node.Size.x, node.Pos.y + g.FontSize + g.Style.FramePadding.y * 2.0)
local out_title_rect = ImRect(r.Min, r.Max)
r.Min.x = r.Min.x + style.WindowBorderSize
r.Max.x = r.Max.x - style.WindowBorderSize
local button_sz = g.FontSize
r.Min.x = r.Min.x + style.FramePadding.x
r.Max.x = r.Max.x - style.FramePadding.x
local window_menu_button_pos = ImVec2(r.Min.x, r.Min.y + style.FramePadding.y)
local out_close_button_pos = nil
if node.HasCloseButton then
out_close_button_pos = ImVec2(r.Max.x - button_sz, r.Min.y + style.FramePadding.y)
r.Max.x = r.Max.x - (button_sz + style.ItemInnerSpacing.x)
end
if node.HasWindowMenuButton and style.WindowMenuButtonPosition == 0 --[[ImGuiDir.Left]] then
r.Min.x = r.Min.x + button_sz + style.ItemInnerSpacing.x
elseif node.HasWindowMenuButton and style.WindowMenuButtonPosition == 1 --[[ImGuiDir.Right]] then
window_menu_button_pos = ImVec2(r.Max.x - button_sz, r.Min.y + style.FramePadding.y)
r.Max.x = r.Max.x - (button_sz + style.ItemInnerSpacing.x)
end
return out_title_rect, r, window_menu_button_pos, out_close_button_pos
end
--- Modifies pos_old/size_old/pos_new/size_new in place (ImVec2 tables)
function ImGui.DockNodeCalcSplitRects(pos_old, size_old, pos_new, size_new, dir, size_new_desired)
local g = ImGui.GetCurrentContext()
local dock_spacing = g.Style.ItemInnerSpacing.x
local axis = (dir == 0 --[[ImGuiDir.Left]] or dir == 1 --[[ImGuiDir.Right]]) and 1 --[[ImGuiAxis.X]] or 2 --[[ImGuiAxis.Y]]
local a, o = axis, 3 - axis -- the port's ImGuiAxis is 1-based (X=1,Y=2): it is directly the ImVec2 index
pos_new[o] = pos_old[o]
size_new[o] = size_old[o]
-- Distribute size on given axis (with a desired size or equally)
local w_avail = size_old[a] - dock_spacing
if size_new_desired[a] > 0.0 and size_new_desired[a] <= w_avail * 0.5 then
size_new[a] = size_new_desired[a]
size_old[a] = IM_TRUNC(w_avail - size_new[a])
else
size_new[a] = IM_TRUNC(w_avail * 0.5)
size_old[a] = IM_TRUNC(w_avail - size_new[a])
end
-- Position each node
if dir == 1 --[[ImGuiDir.Right]] or dir == 3 --[[ImGuiDir.Down]] then
pos_new[a] = pos_old[a] + size_old[a] + dock_spacing
elseif dir == 0 --[[ImGuiDir.Left]] or dir == 2 --[[ImGuiDir.Up]] then
pos_new[a] = pos_old[a]
pos_old[a] = pos_new[a] + size_new[a] + dock_spacing
end
end
local function ImGetDirQuadrantFromDeltaLocal(dx, dy)
if math.abs(dx) > math.abs(dy) then
return (dx > 0.0) and 1 --[[ImGuiDir.Right]] or 0 --[[ImGuiDir.Left]]
end
return (dy > 0.0) and 3 --[[ImGuiDir.Down]] or 2 --[[ImGuiDir.Up]]
end
-- Retrieve the drop rectangles for a given direction or for the center + perform hit testing.
--- @return bool hit, ImRect out_r
function ImGui.DockNodeCalcDropRectsAndTestMousePos(parent, dir, outer_docking, test_mouse_pos)
local g = ImGui.GetCurrentContext()
local parent_smaller_axis = ImMin(parent:GetWidth(), parent:GetHeight())
local hs_for_central_nodes = ImMin(g.FontSize * 1.5, ImMax(g.FontSize * 0.5, parent_smaller_axis / 8.0))
local hs_w, hs_h, off -- Half-size, longer axis / Half-size, smaller axis / Distance from edge or center
if outer_docking then
-- hs_w = ImTrunc(ImClamp(parent_smaller_axis - hs_for_central_nodes * 4.0, g.FontSize * 0.5, g.FontSize * 8.0))
-- hs_h = ImTrunc(hs_w * 0.15)
-- off = ImVec2(ImTrunc(parent:GetWidth() * 0.5 - GetFrameHeightWithSpacing() * 1.4 - hs_h), ImTrunc(parent:GetHeight() * 0.5 - GetFrameHeightWithSpacing() * 1.4 - hs_h))
hs_w = ImTrunc(hs_for_central_nodes * 1.50)
hs_h = ImTrunc(hs_for_central_nodes * 0.80)
off = ImVec2(ImTrunc(parent:GetWidth() * 0.5 - hs_h), ImTrunc(parent:GetHeight() * 0.5 - hs_h))
else
hs_w = ImTrunc(hs_for_central_nodes)
hs_h = ImTrunc(hs_for_central_nodes * 0.90)
off = ImVec2(ImTrunc(hs_w * 2.40), ImTrunc(hs_w * 2.40))
end
local pc = parent:GetCenter()
local c = ImVec2(ImTrunc(pc.x), ImTrunc(pc.y))
local out_r
if dir == (-1) --[[ImGuiDir.None]] then out_r = ImRect(c.x - hs_w, c.y - hs_w, c.x + hs_w, c.y + hs_w)
elseif dir == 2 --[[ImGuiDir.Up]] then out_r = ImRect(c.x - hs_w, c.y - off.y - hs_h, c.x + hs_w, c.y - off.y + hs_h)
elseif dir == 3 --[[ImGuiDir.Down]] then out_r = ImRect(c.x - hs_w, c.y + off.y - hs_h, c.x + hs_w, c.y + off.y + hs_h)
elseif dir == 0 --[[ImGuiDir.Left]] then out_r = ImRect(c.x - off.x - hs_h, c.y - hs_w, c.x - off.x + hs_h, c.y + hs_w)
elseif dir == 1 --[[ImGuiDir.Right]] then out_r = ImRect(c.x + off.x - hs_h, c.y - hs_w, c.x + off.x + hs_h, c.y + hs_w) end
if test_mouse_pos == nil then return false, out_r end
local hit_r = ImRect(out_r.Min, out_r.Max)
if not outer_docking then
-- Custom hit testing for the 5-way selection, designed to reduce flickering when moving diagonally between sides
hit_r:Expand(ImTrunc(hs_w * 0.30))
local mdx, mdy = test_mouse_pos.x - c.x, test_mouse_pos.y - c.y
local mouse_delta_len2 = mdx * mdx + mdy * mdy
local r_threshold_center = hs_w * 1.4
local r_threshold_sides = hs_w * (1.4 + 1.2)
if mouse_delta_len2 < r_threshold_center * r_threshold_center then
return (dir == (-1) --[[ImGuiDir.None]]), out_r
end
if mouse_delta_len2 < r_threshold_sides * r_threshold_sides then
return (dir == ImGetDirQuadrantFromDeltaLocal(mdx, mdy)), out_r
end
end
return hit_r:Contains(test_mouse_pos), out_r
end
-- host_node may be nil if the window doesn't have a DockNode already.
-- FIXME-DOCK: This is misnamed since it's also doing the filtering.
function ImGui.DockNodePreviewDockSetup(host_window, host_node, payload_window, payload_node, data, is_explicit_target, is_outer_docking)
local g = ImGui.GetCurrentContext()
-- There is an edge case when docking into a dockspace which only has inactive nodes.
-- In this case DockNodeTreeFindNodeByPos() will have selected a leaf node which is inactive.
-- Because the inactive leaf node doesn't have proper pos/size yet, we'll use the root node as reference.
if payload_node == nil then payload_node = payload_window.DockNodeAsHost end
local ref_node_for_rect = (host_node and not host_node.IsVisible) and ImGui.DockNodeGetRootNode(host_node) or host_node
if ref_node_for_rect then assert(ref_node_for_rect.IsVisible == true) end
-- Filter, figure out where we are allowed to dock
local src_node_flags = payload_node and payload_node.MergedFlags or payload_window.WindowClass.DockNodeFlagsOverrideSet
local dst_node_flags = host_node and host_node.MergedFlags or host_window.WindowClass.DockNodeFlagsOverrideSet
data.IsCenterAvailable = true
if is_outer_docking then
data.IsCenterAvailable = false
elseif g.IO.ConfigDockingNoDockingOver then
data.IsCenterAvailable = false
elseif bit32.band(dst_node_flags, 1048576 --[[ImGuiDockNodeFlags.NoDockingOverMe]]) ~= 0 then
data.IsCenterAvailable = false
elseif host_node and bit32.band(dst_node_flags, 4 --[[ImGuiDockNodeFlags.NoDockingOverCentralNode]]) ~= 0 and host_node:IsCentralNode() then
data.IsCenterAvailable = false
elseif (not host_node or not host_node:IsEmpty()) and payload_node and payload_node:IsSplitNode() and (payload_node.OnlyNodeWithWindows == nil) then -- Is _visibly_ split?
data.IsCenterAvailable = false
elseif bit32.band(src_node_flags, 2097152 --[[ImGuiDockNodeFlags.NoDockingOverOther]]) ~= 0 and (not host_node or not host_node:IsEmpty()) then
data.IsCenterAvailable = false
elseif bit32.band(src_node_flags, 4194304 --[[ImGuiDockNodeFlags.NoDockingOverEmpty]]) ~= 0 and host_node and host_node:IsEmpty() then
data.IsCenterAvailable = false
end
data.IsSidesAvailable = true
if bit32.band(dst_node_flags, 16 --[[ImGuiDockNodeFlags.NoDockingSplit]]) ~= 0 or g.IO.ConfigDockingNoSplit then
data.IsSidesAvailable = false
elseif not is_outer_docking and host_node and host_node.ParentNode == nil and host_node:IsCentralNode() then
data.IsSidesAvailable = false
elseif bit32.band(src_node_flags, 524288 --[[ImGuiDockNodeFlags.NoDockingSplitOther]]) ~= 0 then
data.IsSidesAvailable = false
end
-- Build a tentative future node (reuse same structure because it is practical. Shape will be readjusted when previewing a split)
data.FutureNode.HasCloseButton = (host_node and host_node.HasCloseButton or (not host_node and host_window.HasCloseButton)) or payload_window.HasCloseButton
data.FutureNode.HasWindowMenuButton = host_node and true or (bit32.band(host_window.Flags, 32 --[[ImGuiWindowFlags.NoCollapse]]) == 0)
local rp = ref_node_for_rect and ref_node_for_rect.Pos or host_window.Pos
local rs = ref_node_for_rect and ref_node_for_rect.Size or host_window.Size
data.FutureNode.Pos = ImVec2(rp.x, rp.y)
data.FutureNode.Size = ImVec2(rs.x, rs.y)
-- Calculate drop shapes geometry for allowed splitting directions
data.SplitNode = host_node
data.SplitDir = (-1) --[[ImGuiDir.None]]
data.IsSplitDirExplicit = false
if not host_window.Collapsed then
for dir = (-1) --[[ImGuiDir.None]], 4 --[[ImGuiDir.COUNT]] - 1 do
if not ((dir == (-1) --[[ImGuiDir.None]] and not data.IsCenterAvailable) or (dir ~= (-1) --[[ImGuiDir.None]] and not data.IsSidesAvailable)) then
local hit, r = ImGui.DockNodeCalcDropRectsAndTestMousePos(data.FutureNode:Rect(), dir, is_outer_docking, g.IO.MousePos)
data.DropRectsDraw[dir + 1] = r
if hit then
data.SplitDir = dir
data.IsSplitDirExplicit = true
end
end
end
end
-- When docking without holding Shift, we only allow and preview docking when hovering over a drop rect or over the title bar
data.IsDropAllowed = (data.SplitDir ~= (-1) --[[ImGuiDir.None]]) or data.IsCenterAvailable
if not is_explicit_target and not data.IsSplitDirExplicit and not g.IO.ConfigDockingWithShift then
data.IsDropAllowed = false
end
-- Calculate split area
data.SplitRatio = 0.0
if data.SplitDir ~= (-1) --[[ImGuiDir.None]] then
local split_dir = data.SplitDir
local split_axis = (split_dir == 0 --[[ImGuiDir.Left]] or split_dir == 1 --[[ImGuiDir.Right]]) and 1 --[[ImGuiAxis.X]] or 2 --[[ImGuiAxis.Y]]
local pos_new, pos_old = ImVec2(0, 0), ImVec2(data.FutureNode.Pos.x, data.FutureNode.Pos.y)
local size_new, size_old = ImVec2(0, 0), ImVec2(data.FutureNode.Size.x, data.FutureNode.Size.y)
ImGui.DockNodeCalcSplitRects(pos_old, size_old, pos_new, size_new, split_dir, payload_window.Size)
-- Calculate split ratio so we can pass it down the docking request
local split_ratio = ImSaturate(size_new[split_axis] / data.FutureNode.Size[split_axis])
data.FutureNode.Pos = pos_new
data.FutureNode.Size = size_new
data.SplitRatio = (split_dir == 1 --[[ImGuiDir.Right]] or split_dir == 3 --[[ImGuiDir.Down]]) and (1.0 - split_ratio) or split_ratio
end
end
function ImGui.DockNodePreviewDockRender(host_window, host_node, root_payload, data)
local g = ImGui.GetCurrentContext()
assert(g.CurrentWindow == host_window) -- Because we rely on font size to calculate tab sizes
-- With this option, we only display the preview on the target viewport, and the payload viewport is made transparent.
-- To compensate for the single layer obstructed by the payload, we'll increase the alpha of the preview nodes.
local is_transparent_payload = g.IO.ConfigDockingTransparentPayload
local overlay_draw_lists = { ImGui.GetForegroundDrawList(host_window.Viewport) }
-- Draw main preview rectangle
local overlay_col_main = ImGui.GetColorU32(42 --[[ImGuiCol.DockingPreview]], is_transparent_payload and 0.60 or 0.40)
local overlay_col_drop = ImGui.GetColorU32(42 --[[ImGuiCol.DockingPreview]], is_transparent_payload and 0.90 or 0.70)
local overlay_col_drop_hovered = ImGui.GetColorU32(42 --[[ImGuiCol.DockingPreview]], is_transparent_payload and 1.20 or 1.00)
local overlay_col_lines = ImGui.GetColorU32(60 --[[ImGuiCol.NavWindowingHighlight]], is_transparent_payload and 0.80 or 0.60)
-- Display area preview
local can_preview_tabs = (root_payload.DockNodeAsHost == nil or root_payload.DockNodeAsHost.Windows.Size > 0)
if data.IsDropAllowed then
local overlay_rect = data.FutureNode:Rect()
if data.SplitDir == (-1) --[[ImGuiDir.None]] and can_preview_tabs then
overlay_rect.Min.y = overlay_rect.Min.y + ImGui.GetFrameHeight()
end
if data.SplitDir ~= (-1) --[[ImGuiDir.None]] or data.IsCenterAvailable then
for _, dl in ipairs(overlay_draw_lists) do
dl:AddRectFilled(overlay_rect.Min, overlay_rect.Max, overlay_col_main, host_window.WindowRounding, ImGui.CalcRoundingFlagsForRectInRect(overlay_rect, host_window:Rect(), g.Style.DockingSeparatorSize))
end
end
end
-- Display tab shape/label preview unless we are splitting node (it generally makes the situation harder to read)
if data.IsDropAllowed and can_preview_tabs and data.SplitDir == (-1) --[[ImGuiDir.None]] and data.IsCenterAvailable then
-- Compute target tab bar geometry so we can locate our preview tabs
local _, tab_bar_rect = ImGui.DockNodeCalcTabBarLayout(data.FutureNode)
local tab_pos = ImVec2(tab_bar_rect.Min.x, tab_bar_rect.Min.y)
if host_node and host_node.TabBar then
if not host_node:IsHiddenTabBar() and not host_node:IsNoTabBar() then
tab_pos.x = tab_pos.x + host_node.TabBar.WidthAllTabs + g.Style.ItemInnerSpacing.x -- We don't use OffsetNewTab because when using non-persistent-order tab bar it is incremented with each Tab submission.
else
tab_pos.x = tab_pos.x + g.Style.ItemInnerSpacing.x + ImGui.TabItemCalcSize(host_node.Windows.Data[1]).x
end
elseif bit32.band(host_window.Flags, 8388608 --[[ImGuiWindowFlags.DockNodeHost]]) == 0 then
tab_pos.x = tab_pos.x + g.Style.ItemInnerSpacing.x + ImGui.TabItemCalcSize(host_window).x -- Account for slight offset which will be added when changing from title bar to tab bar
end
-- Draw tab shape/label preview (payload may be a loose window or a host window carrying multiple tabbed windows)
if root_payload.DockNodeAsHost then
assert(root_payload.DockNodeAsHost.Windows.Size <= root_payload.DockNodeAsHost.TabBar.Tabs.Size)
end
local tab_bar_with_payload = root_payload.DockNodeAsHost and root_payload.DockNodeAsHost.TabBar or nil
local payload_count = tab_bar_with_payload and tab_bar_with_payload.Tabs.Size or 1
for payload_n = 1, payload_count do
-- DockNode's TabBar may have non-window Tabs manually appended by user
local payload_window = tab_bar_with_payload and tab_bar_with_payload.Tabs.Data[payload_n].Window or root_payload
if not (tab_bar_with_payload and payload_window == nil) and DockNodeIsDropAllowedOne(payload_window, host_window) then
-- Calculate the tab bounding box for each payload window
local tab_size = ImGui.TabItemCalcSize(payload_window)
local tab_bb = ImRect(tab_pos.x, tab_pos.y, tab_pos.x + tab_size.x, tab_pos.y + tab_size.y)
tab_pos.x = tab_pos.x + tab_size.x + g.Style.ItemInnerSpacing.x
local overlay_col_text = ImGui.GetColorU32(payload_window.DockStyle.Colors[0 --[[ImGuiWindowDockStyleCol.Text]]], 1.0, true)
local overlay_col_tabs = ImGui.GetColorU32(payload_window.DockStyle.Colors[3 --[[ImGuiWindowDockStyleCol.TabSelected]]], 1.0, true)
local overlay_col_unsaved_marker = ImGui.GetColorU32(payload_window.DockStyle.Colors[8 --[[ImGuiWindowDockStyleCol.UnsavedMarker]]], 1.0, true)
ImGui.PushStyleColor(0 --[[ImGuiCol.Text]], ImGui.ColorConvertU32ToFloat4(overlay_col_text))
ImGui.PushStyleColor(58 --[[ImGuiCol.UnsavedMarker]], ImGui.ColorConvertU32ToFloat4(overlay_col_unsaved_marker))
for _, dl in ipairs(overlay_draw_lists) do
local tab_flags = bit32.band(payload_window.Flags, 262144 --[[ImGuiWindowFlags.UnsavedDocument]]) ~= 0 and 1 --[[ImGuiTabItemFlags.UnsavedDocument]] or 0
local contains = tab_bar_rect:Contains(tab_bb)
if not contains then dl:PushClipRect(tab_bar_rect.Min, tab_bar_rect.Max) end
ImGui.TabItemBackground(dl, tab_bb, tab_flags, overlay_col_tabs)
ImGui.TabItemLabelAndCloseButton(dl, tab_bb, tab_flags, g.Style.FramePadding, payload_window.Name, 0, 0, false)
if not contains then dl:PopClipRect() end
end
ImGui.PopStyleColor(2)
end
end
end
-- Display drop boxes
local scale = g.Style._MainScale or 1.0 -- FIXME-DPI
local overlay_rounding = ImMax(math.floor(3.0 * scale), g.Style.FrameRounding)
for dir = (-1) --[[ImGuiDir.None]], 4 --[[ImGuiDir.COUNT]] - 1 do
local draw_r = data.DropRectsDraw[dir + 1]
if not IsRectInverted(draw_r) then
local draw_r_in = ImRect(draw_r.Min, draw_r.Max)
draw_r_in:Expand(math.floor(-2.0 * scale))
local overlay_col = (data.SplitDir == dir and data.IsSplitDirExplicit) and overlay_col_drop_hovered or overlay_col_drop
local thickness = math.floor(1.0 * scale)
for _, dl in ipairs(overlay_draw_lists) do
local cc = draw_r_in:GetCenter()
local center = ImVec2(math.floor(cc.x), math.floor(cc.y))
dl:AddRectFilled(draw_r.Min, draw_r.Max, overlay_col, overlay_rounding)
dl:AddRect(draw_r_in.Min, draw_r_in.Max, overlay_col_lines, overlay_rounding, thickness)
if dir == 0 --[[ImGuiDir.Left]] or dir == 1 --[[ImGuiDir.Right]] then
dl:AddLineV(center.x, draw_r_in.Min.y, draw_r_in.Max.y, overlay_col_lines, thickness)
end
if dir == 2 --[[ImGuiDir.Up]] or dir == 3 --[[ImGuiDir.Down]] then
dl:AddLineH(draw_r_in.Min.x, draw_r_in.Max.x, center.y, overlay_col_lines, thickness)
end
end
end
-- Stop after ImGuiDir.None
if (host_node and bit32.band(host_node.MergedFlags, 16 --[[ImGuiDockNodeFlags.NoDockingSplit]]) ~= 0) or g.IO.ConfigDockingNoSplit then
return
end
end
end
-------------------------------------------------------------------------------
-- Docking: ImGuiDockNode Tree manipulation functions
-------------------------------------------------------------------------------
-- - DockNodeTreeSplit()
-- - DockNodeTreeMerge()
-- - DockNodeTreeUpdatePosSize()
-- - DockNodeTreeUpdateSplitterFindTouchingNode()
-- - DockNodeTreeUpdateSplitter()
-- - DockNodeTreeFindFallbackLeafNode()
-- - DockNodeTreeFindNodeByPos()
-------------------------------------------------------------------------------
function ImGui.DockNodeTreeSplit(ctx, parent_node, split_axis, split_inheritor_child_idx, split_ratio, new_node)
local g = ImGui.GetCurrentContext()
assert(split_axis ~= 0 --[[ImGuiAxis.None]])
local child_0 = (new_node and split_inheritor_child_idx ~= 0) and new_node or ImGui.DockContextAddNode(ctx, 0)
child_0.ParentNode = parent_node
local child_1 = (new_node and split_inheritor_child_idx ~= 1) and new_node or ImGui.DockContextAddNode(ctx, 0)
child_1.ParentNode = parent_node
local child_inheritor = (split_inheritor_child_idx == 0) and child_0 or child_1
ImGui.DockNodeMoveChildNodes(child_inheritor, parent_node)
parent_node.ChildNodes[0] = child_0
parent_node.ChildNodes[1] = child_1
parent_node.ChildNodes[split_inheritor_child_idx].VisibleWindow = parent_node.VisibleWindow
parent_node.SplitAxis = split_axis
parent_node.VisibleWindow = nil
parent_node.AuthorityForPos = 1 --[[ImGuiDataAuthority.DockNode]]
parent_node.AuthorityForSize = 1 --[[ImGuiDataAuthority.DockNode]]
local a = split_axis
local size_avail = parent_node.Size[a] - g.Style.DockingSeparatorSize
size_avail = ImMax(size_avail, g.Style.WindowMinSize[a] * 2.0)
assert(size_avail > 0.0) -- If you created a node manually with DockBuilderAddNode(), you need to also call DockBuilderSetNodeSize() before splitting.
child_0.SizeRef = ImVec2(parent_node.Size.x, parent_node.Size.y)
child_1.SizeRef = ImVec2(parent_node.Size.x, parent_node.Size.y)
child_0.SizeRef[a] = ImTrunc(size_avail * split_ratio)
child_1.SizeRef[a] = ImTrunc(size_avail - child_0.SizeRef[a])
ImGui.DockNodeMoveWindows(parent_node.ChildNodes[split_inheritor_child_idx], parent_node)
DockSettingsRenameNodeReferences(parent_node.ID, parent_node.ChildNodes[split_inheritor_child_idx].ID)
ImGui.DockNodeUpdateHasCentralNodeChild(ImGui.DockNodeGetRootNode(parent_node))
ImGui.DockNodeTreeUpdatePosSize(parent_node, parent_node.Pos, parent_node.Size)
-- Flags transfer (e.g. this is where we transfer the ImGuiDockNodeFlags.CentralNode property)
child_0.SharedFlags = bit32.band(parent_node.SharedFlags, 4294967295 --[[ImGuiDockNodeFlags.SharedFlagsInheritMask_]])
child_1.SharedFlags = bit32.band(parent_node.SharedFlags, 4294967295 --[[ImGuiDockNodeFlags.SharedFlagsInheritMask_]])
child_inheritor.LocalFlags = bit32.band(parent_node.LocalFlags, 260208 --[[ImGuiDockNodeFlags.LocalFlagsTransferMask_]])
parent_node.LocalFlags = bit32.band(parent_node.LocalFlags, bit32.bnot(260208 --[[ImGuiDockNodeFlags.LocalFlagsTransferMask_]]))
child_0:UpdateMergedFlags()
child_1:UpdateMergedFlags()
parent_node:UpdateMergedFlags()
if child_inheritor:IsCentralNode() then
ImGui.DockNodeGetRootNode(parent_node).CentralNode = child_inheritor
end
end
function ImGui.DockNodeTreeMerge(ctx, parent_node, merge_lead_child)
-- When called from DockContextProcessUndockNode() it is possible that one of the child is nil.
local child_0 = parent_node.ChildNodes[0]
local child_1 = parent_node.ChildNodes[1]
assert(child_0 or child_1)
assert(merge_lead_child == child_0 or merge_lead_child == child_1)
if (child_0 and child_0.Windows.Size > 0) or (child_1 and child_1.Windows.Size > 0) then
assert(parent_node.TabBar == nil)
assert(parent_node.Windows.Size == 0)
end
local backup_last_explicit_size = ImVec2(parent_node.SizeRef.x, parent_node.SizeRef.y)
ImGui.DockNodeMoveChildNodes(parent_node, merge_lead_child)
if child_0 then
ImGui.DockNodeMoveWindows(parent_node, child_0) -- Generally only 1 of the 2 child node will have windows
DockSettingsRenameNodeReferences(child_0.ID, parent_node.ID)
end
if child_1 then
ImGui.DockNodeMoveWindows(parent_node, child_1)
DockSettingsRenameNodeReferences(child_1.ID, parent_node.ID)
end
ImGui.DockNodeApplyPosSizeToWindows(parent_node)
parent_node.AuthorityForPos = 0 --[[ImGuiDataAuthority.Auto]]
parent_node.AuthorityForSize = 0 --[[ImGuiDataAuthority.Auto]]
parent_node.AuthorityForViewport = 0 --[[ImGuiDataAuthority.Auto]]
parent_node.VisibleWindow = merge_lead_child.VisibleWindow
parent_node.SizeRef = backup_last_explicit_size
-- Flags transfer
local M = 260208 --[[ImGuiDockNodeFlags.LocalFlagsTransferMask_]]
parent_node.LocalFlags = bit32.band(parent_node.LocalFlags, bit32.bnot(M)) -- Preserve Dockspace flag
parent_node.LocalFlags = bit32.bor(parent_node.LocalFlags, bit32.band(child_0 and child_0.LocalFlags or 0, M))
parent_node.LocalFlags = bit32.bor(parent_node.LocalFlags, bit32.band(child_1 and child_1.LocalFlags or 0, M))
parent_node.LocalFlagsInWindows = bit32.bor(child_0 and child_0.LocalFlagsInWindows or 0, child_1 and child_1.LocalFlagsInWindows or 0) -- FIXME: Would be more consistent to update from actual windows
parent_node:UpdateMergedFlags()
if child_0 then ImGui.DockContextDeleteNode(ctx, child_0) end
if child_1 then ImGui.DockContextDeleteNode(ctx, child_1) end
end
-- Update Pos/Size for a node hierarchy (don't affect child Windows yet)
-- (Depth-first, Pre-Order)
function ImGui.DockNodeTreeUpdatePosSize(node, pos, size, only_write_to_single_node)
-- During the regular dock node update we write to all nodes.
-- 'only_write_to_single_node' is only set when turning a node visible mid-frame and we need its size right-away.
local g = ImGui.GetCurrentContext()
local write_to_node = only_write_to_single_node == nil or only_write_to_single_node == node
if write_to_node then
node.Pos = ImVec2(pos.x, pos.y)
node.Size = ImVec2(size.x, size.y)
end
if node:IsLeafNode() then return end
local child_0 = node.ChildNodes[0]
local child_1 = node.ChildNodes[1]
local child_0_pos, child_1_pos = ImVec2(pos.x, pos.y), ImVec2(pos.x, pos.y)
local child_0_size, child_1_size = ImVec2(size.x, size.y), ImVec2(size.x, size.y)
local child_0_is_toward_single_node = (only_write_to_single_node ~= nil and ImGui.DockNodeIsInHierarchyOf(only_write_to_single_node, child_0))
local child_1_is_toward_single_node = (only_write_to_single_node ~= nil and ImGui.DockNodeIsInHierarchyOf(only_write_to_single_node, child_1))
local child_0_is_or_will_be_visible = child_0.IsVisible or child_0_is_toward_single_node
local child_1_is_or_will_be_visible = child_1.IsVisible or child_1_is_toward_single_node
if child_0_is_or_will_be_visible and child_1_is_or_will_be_visible then
local spacing = g.Style.DockingSeparatorSize
local a = node.SplitAxis
local size_avail = ImMax(size[a] - spacing, 0.0)
-- Size allocation policy
-- 1) The first 0..WindowMinSize[axis]*2 are allocated evenly to both windows.
local size_min_each = ImTrunc(ImMin(size_avail, g.Style.WindowMinSize[a] * 2.0) * 0.5)
-- FIXME: Blocks 2) and 3) are essentially doing nearly the same thing.
-- Difference are: write-back to SizeRef; application of a minimum size; rounding before ImTrunc()
-- Clarify and rework differences between Size & SizeRef and purpose of WantLockSizeOnce
-- 2) Process locked absolute size (during a splitter resize we preserve the child of nodes not touching the splitter edge)
if child_0.WantLockSizeOnce and not child_1.WantLockSizeOnce then
child_0_size[a] = ImMin(size_avail - 1.0, child_0.Size[a]); child_0.SizeRef[a] = child_0_size[a]
child_1_size[a] = size_avail - child_0_size[a]; child_1.SizeRef[a] = child_1_size[a]
elseif child_1.WantLockSizeOnce and not child_0.WantLockSizeOnce then
child_1_size[a] = ImMin(size_avail - 1.0, child_1.Size[a]); child_1.SizeRef[a] = child_1_size[a]
child_0_size[a] = size_avail - child_1_size[a]; child_0.SizeRef[a] = child_0_size[a]
elseif child_0.WantLockSizeOnce and child_1.WantLockSizeOnce then
-- FIXME-DOCK: We cannot honor the requested size, so apply ratio.
-- Currently this path will only be taken if code programmatically sets WantLockSizeOnce
local split_ratio = child_0_size[a] / (child_0_size[a] + child_1_size[a])
child_0_size[a] = ImTrunc(size_avail * split_ratio); child_0.SizeRef[a] = child_0_size[a]
child_1_size[a] = size_avail - child_0_size[a]; child_1.SizeRef[a] = child_1_size[a]
-- 3) If one window is the central node (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the central node
elseif child_0.SizeRef[a] ~= 0.0 and child_1.HasCentralNodeChild then
child_0_size[a] = ImMin(size_avail - size_min_each, child_0.SizeRef[a])
child_1_size[a] = size_avail - child_0_size[a]
elseif child_1.SizeRef[a] ~= 0.0 and child_0.HasCentralNodeChild then
child_1_size[a] = ImMin(size_avail - size_min_each, child_1.SizeRef[a])
child_0_size[a] = size_avail - child_1_size[a]
else
-- 4) Otherwise distribute according to the relative ratio of each SizeRef value
local split_ratio = child_0.SizeRef[a] / (child_0.SizeRef[a] + child_1.SizeRef[a])
child_0_size[a] = ImMax(size_min_each, ImTrunc(size_avail * split_ratio + 0.5))
child_1_size[a] = size_avail - child_0_size[a]
end
child_1_pos[a] = child_1_pos[a] + spacing + child_0_size[a]
end
if only_write_to_single_node == nil then
child_0.WantLockSizeOnce = false
child_1.WantLockSizeOnce = false
end
local child_0_recurse = only_write_to_single_node and child_0_is_toward_single_node or (not only_write_to_single_node and child_0.IsVisible)
local child_1_recurse = only_write_to_single_node and child_1_is_toward_single_node or (not only_write_to_single_node and child_1.IsVisible)
if child_0_recurse then ImGui.DockNodeTreeUpdatePosSize(child_0, child_0_pos, child_0_size) end
if child_1_recurse then ImGui.DockNodeTreeUpdatePosSize(child_1, child_1_pos, child_1_size) end
end
local function DockNodeTreeUpdateSplitterFindTouchingNode(node, axis, side, touching_nodes)
if node:IsLeafNode() then
table.insert(touching_nodes, node)
return
end
if node.ChildNodes[0].IsVisible then
if node.SplitAxis ~= axis or side == 0 or not node.ChildNodes[1].IsVisible then
DockNodeTreeUpdateSplitterFindTouchingNode(node.ChildNodes[0], axis, side, touching_nodes)
end
end
if node.ChildNodes[1].IsVisible then
if node.SplitAxis ~= axis or side == 1 or not node.ChildNodes[0].IsVisible then
DockNodeTreeUpdateSplitterFindTouchingNode(node.ChildNodes[1], axis, side, touching_nodes)
end
end
end
local WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04 -- Reduce visual noise by only highlighting the border after a certain time.
-- (Depth-First, Pre-Order)
function ImGui.DockNodeTreeUpdateSplitter(node)
if node:IsLeafNode() then return end
local g = ImGui.GetCurrentContext()
local child_0 = node.ChildNodes[0]
local child_1 = node.ChildNodes[1]
if child_0.IsVisible and child_1.IsVisible then
-- Bounding box of the splitter cover the space between both nodes (w = Spacing, h = Size[xy^1] for when splitting horizontally)
local axis = node.SplitAxis
assert(axis ~= 0 --[[ImGuiAxis.None]])
local a, o = axis, 3 - axis -- the port's ImGuiAxis is 1-based (X=1,Y=2): it is directly the ImVec2 index
local bb = ImRect(ImVec2(child_0.Pos.x, child_0.Pos.y), ImVec2(child_1.Pos.x, child_1.Pos.y))
bb.Min[a] = bb.Min[a] + child_0.Size[a]
bb.Max[o] = bb.Max[o] + child_1.Size[o]
-- if g.IO.KeyCtrl then GetForegroundDrawList(g.CurrentWindow.Viewport):AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,255)) end
local merged_flags = bit32.bor(child_0.MergedFlags, child_1.MergedFlags) -- Merged flags for BOTH childs
local no_resize_axis_flag = (axis == 1 --[[ImGuiAxis.X]]) and 65536 --[[ImGuiDockNodeFlags.NoResizeX]] or 131072 --[[ImGuiDockNodeFlags.NoResizeY]]
if bit32.band(merged_flags, 32 --[[ImGuiDockNodeFlags.NoResize]]) ~= 0 or bit32.band(merged_flags, no_resize_axis_flag) ~= 0 then
local window = g.CurrentWindow
window.DrawList:AddRectFilled(bb.Min, bb.Max, ImGui.GetColorU32(28 --[[ImGuiCol.Separator]]), g.Style.FrameRounding)
else
-- bb.Min[axis] = bb.Min[axis] + 1 -- Display a little inward so highlight doesn't connect with nearby tabs on the neighbor node.
-- bb.Max[axis] = bb.Max[axis] - 1
ImGui.PushID(node.ID)
-- Find resizing limits by gathering list of nodes that are touching the splitter line.
local touching_nodes = { [0] = {}, [1] = {} }
local min_size = g.Style.WindowMinSize[a]
local resize_limits = { [0] = node.ChildNodes[0].Pos[a] + min_size, [1] = node.ChildNodes[1].Pos[a] + node.ChildNodes[1].Size[a] - min_size }
local splitter_id = ImGui.GetID("##Splitter")
if g.ActiveId == splitter_id then -- Only process when splitter is active
DockNodeTreeUpdateSplitterFindTouchingNode(child_0, axis, 1, touching_nodes[0])
DockNodeTreeUpdateSplitterFindTouchingNode(child_1, axis, 0, touching_nodes[1])
for _, tn in ipairs(touching_nodes[0]) do
resize_limits[0] = ImMax(resize_limits[0], tn:Rect().Min[a] + min_size)
end
for _, tn in ipairs(touching_nodes[1]) do
resize_limits[1] = ImMin(resize_limits[1], tn:Rect().Max[a] - min_size)
end
end
-- Use a short delay before highlighting the splitter (and changing the mouse cursor) in order for regular mouse movement to not highlight many splitters
local cur_size_0 = child_0.Size[a]
local cur_size_1 = child_1.Size[a]
local min_size_0 = resize_limits[0] - child_0.Pos[a]
local min_size_1 = child_1.Pos[a] + child_1.Size[a] - resize_limits[1]
local bg_col = ImGui.GetColorU32(2 --[[ImGuiCol.WindowBg]])
local changed
changed, cur_size_0, cur_size_1 = ImGui.SplitterBehavior(bb, ImGui.GetID("##Splitter"), axis, cur_size_0, cur_size_1, min_size_0, min_size_1, g.WindowsBorderHoverPadding, WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER, bg_col)
if changed then
if #touching_nodes[0] > 0 and #touching_nodes[1] > 0 then
child_0.Size[a] = cur_size_0; child_0.SizeRef[a] = cur_size_0
child_1.Pos[a] = child_1.Pos[a] - (cur_size_1 - child_1.Size[a])
child_1.Size[a] = cur_size_1; child_1.SizeRef[a] = cur_size_1
-- Lock the size of every node that is a sibling of the node we are touching
-- This might be less desirable if we can merge sibling of a same axis into the same parental level.
for side_n = 0, 1 do
for _, touching_node in ipairs(touching_nodes[side_n]) do
while touching_node.ParentNode ~= node do
if touching_node.ParentNode.SplitAxis == axis then
-- Mark other node so its size will be preserved during the upcoming call to DockNodeTreeUpdatePosSize().
local node_to_preserve = touching_node.ParentNode.ChildNodes[side_n]
node_to_preserve.WantLockSizeOnce = true
end
touching_node = touching_node.ParentNode
end
end
end
ImGui.DockNodeTreeUpdatePosSize(child_0, child_0.Pos, child_0.Size)
ImGui.DockNodeTreeUpdatePosSize(child_1, child_1.Pos, child_1.Size)
ImGui.MarkIniSettingsDirty()
end
end
ImGui.PopID()
end
end
if child_0.IsVisible then ImGui.DockNodeTreeUpdateSplitter(child_0) end
if child_1.IsVisible then ImGui.DockNodeTreeUpdateSplitter(child_1) end
end
function ImGui.DockNodeTreeFindFallbackLeafNode(node)
if node:IsLeafNode() then return node end
local leaf_node = ImGui.DockNodeTreeFindFallbackLeafNode(node.ChildNodes[0])
if leaf_node then return leaf_node end
leaf_node = ImGui.DockNodeTreeFindFallbackLeafNode(node.ChildNodes[1])
if leaf_node then return leaf_node end
return nil
end
function ImGui.DockNodeTreeFindVisibleNodeByPos(node, pos)
if not node.IsVisible then return nil end
local r = ImRect(node.Pos, node.Pos + node.Size)
if not r:Contains(pos) then return nil end
if node:IsLeafNode() then return node end
local hovered_node = ImGui.DockNodeTreeFindVisibleNodeByPos(node.ChildNodes[0], pos)
if hovered_node then return hovered_node end
hovered_node = ImGui.DockNodeTreeFindVisibleNodeByPos(node.ChildNodes[1], pos)
if hovered_node then return hovered_node end
-- This means we are hovering over the splitter/spacing of a parent node
return node
end
-------------------------------------------------------------------------------
-- Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport)
-------------------------------------------------------------------------------
-- - SetWindowDock() [Internal]
-- - DockSpace()
-- - DockSpaceOverViewport()
-------------------------------------------------------------------------------
-- [Internal] Called via SetNextWindowDockID()
function ImGui.SetWindowDock(window, dock_id, cond)
-- Test condition (NB: bit 0 is always true) and clear flags for next time
if cond and cond ~= 0 and bit32.band(window.SetWindowDockAllowFlags, cond) == 0 then return end
window.SetWindowDockAllowFlags = bit32.band(window.SetWindowDockAllowFlags, bit32.bnot(bit32.bor(2 --[[ImGuiCond.Once]], 4 --[[ImGuiCond.FirstUseEver]], 8 --[[ImGuiCond.Appearing]])))
if window.DockId == dock_id then return end
-- If the user attempt to set a dock id that is a split node, we'll dig within to find a suitable docking spot
local g = ImGui.GetCurrentContext()
local new_node = ImGui.DockContextFindNodeByID(g, dock_id)
if new_node and new_node:IsSplitNode() then
-- Policy: Find central node or latest focused node. We first move back to our root node.
new_node = ImGui.DockNodeGetRootNode(new_node)
if new_node.CentralNode then
assert(new_node.CentralNode:IsCentralNode())
dock_id = new_node.CentralNode.ID
else
dock_id = new_node.LastFocusedNodeId
end
end
if window.DockId == dock_id then return end
if window.DockNode then ImGui.DockNodeRemoveWindow(window.DockNode, window, 0) end
window.DockId = dock_id
end
-- Create an explicit dockspace node within an existing window. Also expose dock node flags and creates a CentralNode by default.
-- The Central Node is always displayed even when empty and shrink/extend according to the requested size of its neighbors.
-- DockSpace() needs to be submitted _before_ any window they can host. If you use a dockspace, submit it early in your app.
-- When ImGuiDockNodeFlags.KeepAliveOnly is set, nothing is submitted in the current window (function may be called from any location).
function ImGui.DockSpace(dockspace_id, size_arg, flags, window_class)
if size_arg == nil then size_arg = ImVec2(0, 0) end
if flags == nil then flags = 0 end
local g = ImGui.GetCurrentContext()
local window = ImGui.GetCurrentWindowRead()
if bit32.band(g.IO.ConfigFlags, 128 --[[ImGuiConfigFlags.DockingEnable]]) == 0 then return 0 end
-- Early out if parent window is hidden/collapsed
-- This is faster but also DockNodeUpdateTabBar() relies on TabBarLayout() running (which won't if SkipItems=true) to set NextSelectedTabId = 0). See #2960.
-- If for whichever reason this is causing problem we would need to ensure that DockNodeUpdateTabBar() ends up clearing NextSelectedTabId even if SkipItems=true.
if window.SkipItems then flags = bit32.bor(flags, 1 --[[ImGuiDockNodeFlags.KeepAliveOnly]]) end
if bit32.band(flags, 1 --[[ImGuiDockNodeFlags.KeepAliveOnly]]) == 0 then window = ImGui.GetCurrentWindow() end -- call to set window.WriteAccessed = true;
assert(bit32.band(flags, 1024 --[[ImGuiDockNodeFlags.DockSpace]]) == 0) -- Flag is automatically set by DockSpace() as LocalFlags, not SharedFlags!
assert(bit32.band(flags, 2048 --[[ImGuiDockNodeFlags.CentralNode]]) == 0) -- Flag is automatically set by DockSpace() as LocalFlags, not SharedFlags! (#8145)
assert(dockspace_id ~= 0)
local node = ImGui.DockContextFindNodeByID(g, dockspace_id)
if node == nil then
node = ImGui.DockContextAddNode(g, dockspace_id)
node:SetLocalFlags(2048 --[[ImGuiDockNodeFlags.CentralNode]])
end
node.SharedFlags = flags
node.WindowClass = window_class or ImGuiWindowClass()
-- When a DockSpace transitioned form implicit to explicit this may be called a second time
-- It is possible that the node has already been claimed by a docked window which appeared before the DockSpace() node, so we overwrite IsDockSpace again.
if node.LastFrameActive == g.FrameCount and bit32.band(flags, 1 --[[ImGuiDockNodeFlags.KeepAliveOnly]]) == 0 then
assert(node:IsDockSpace() == false, "Cannot call DockSpace() twice a frame with the same ID")
node:SetLocalFlags(bit32.bor(node.LocalFlags, 1024 --[[ImGuiDockNodeFlags.DockSpace]]))
return dockspace_id
end
node:SetLocalFlags(bit32.bor(node.LocalFlags, 1024 --[[ImGuiDockNodeFlags.DockSpace]]))
-- Keep alive mode, this is allow windows docked into this node so stay docked even if they are not visible
if bit32.band(flags, 1 --[[ImGuiDockNodeFlags.KeepAliveOnly]]) ~= 0 then
node.LastFrameAlive = g.FrameCount
return dockspace_id
end
local content_avail = ImGui.GetContentRegionAvail()
local size = ImVec2(ImTrunc(size_arg.x), ImTrunc(size_arg.y))
if size.x <= 0.0 then size.x = ImMax(content_avail.x + size.x, 4.0) end -- Arbitrary minimum child size (0.0f causing too much issues)
if size.y <= 0.0 then size.y = ImMax(content_avail.y + size.y, 4.0) end
assert(size.x > 0.0 and size.y > 0.0)
node.Pos = ImVec2(window.DC.CursorPos.x, window.DC.CursorPos.y)
node.Size = ImVec2(size.x, size.y)
node.SizeRef = ImVec2(size.x, size.y)
ImGui.SetNextWindowPos(node.Pos)
ImGui.SetNextWindowSize(node.Size)
g.NextWindowData.PosUndock = false
-- FIXME-DOCK: Why do we need a child window to host a dockspace, could we host it in the existing window?
-- FIXME-DOCK: What is the reason for not simply calling BeginChild()? (OK to have a reason but should be commented)
local window_flags = bit32.bor(16777216 --[[ImGuiWindowFlags.ChildWindow]], 8388608 --[[ImGuiWindowFlags.DockNodeHost]])
window_flags = bit32.bor(window_flags, 256 --[[ImGuiWindowFlags.NoSavedSettings]], 2 --[[ImGuiWindowFlags.NoResize]], 32 --[[ImGuiWindowFlags.NoCollapse]], 1 --[[ImGuiWindowFlags.NoTitleBar]])
window_flags = bit32.bor(window_flags, 8 --[[ImGuiWindowFlags.NoScrollbar]], 16 --[[ImGuiWindowFlags.NoScrollWithMouse]])
window_flags = bit32.bor(window_flags, 128 --[[ImGuiWindowFlags.NoBackground]])
local title = string.format("%s/DockSpace_%08X", window.Name, dockspace_id)
ImGui.PushStyleVar(8 --[[ImGuiStyleVar.ChildBorderSize]], 0.0)
ImGui.Begin(title, nil, window_flags)
ImGui.PopStyleVar()
local host_window = g.CurrentWindow
DockNodeSetupHostWindow(node, host_window)
host_window.ChildId = window:GetID(title)
node.OnlyNodeWithWindows = nil
assert(node:IsRootNode())
-- We need to handle the rare case were a central node is missing.
-- This can happen if the node was first created manually with DockBuilderAddNode() but _without_ the ImGuiDockNodeFlags.Dockspace.
-- Doing it correctly would set the _CentralNode flags, which would then propagate according to subsequent split.
-- It would also be ambiguous to attempt to assign a central node while there are split nodes, so we wait until there's a single node remaining.
-- The specific sub-property of _CentralNode we are interested in recovering here is the "Don't delete when empty" property,
-- as it doesn't make sense for an empty dockspace to not have this property.
if node:IsLeafNode() and not node:IsCentralNode() then
node:SetLocalFlags(bit32.bor(node.LocalFlags, 2048 --[[ImGuiDockNodeFlags.CentralNode]]))
end
-- Update the node
ImGui.DockNodeUpdate(node)
ImGui.End()
local bb = ImRect(node.Pos, node.Pos + size)
ImGui.ItemSize(size)
ImGui.ItemAdd(bb, dockspace_id, nil, 2 --[[ImGuiItemFlags.NoNav]]) -- Not a nav point (could be, would need to draw the nav rect and replicate/refactor activation from BeginChild(), but seems like CTRL+Tab works better here?)
if bit32.band(g.LastItemData.StatusFlags, 1 --[[ImGuiItemStatusFlags.HoveredRect]]) ~= 0 and ImGui.IsWindowChildOf(g.HoveredWindow, host_window, false, true) then -- To fullfill IsItemHovered(), similar to EndChild()
g.LastItemData.StatusFlags = bit32.bor(g.LastItemData.StatusFlags, 128 --[[ImGuiItemStatusFlags.HoveredWindow]])
end
return dockspace_id
end
-- Tips: Use with ImGuiDockNodeFlags.PassthruCentralNode!
-- The limitation with this call is that your window won't have a local menu bar, but you can also use BeginMainMenuBar().
-- Even though we could pass window flags, it would also require the user to be able to call BeginMenuBar() somehow meaning we can't Begin/End in a single function.
-- If you really want a menu bar inside the same window as the one hosting the dockspace, you will need to copy this code somewhere and tweak it.
function ImGui.DockSpaceOverViewport(dockspace_id, viewport, dockspace_flags, window_class)
if dockspace_id == nil then dockspace_id = 0 end
if dockspace_flags == nil then dockspace_flags = 0 end
if viewport == nil then viewport = ImGui.GetMainViewport() end
-- Submit a window filling the entire viewport
ImGui.SetNextWindowPos(viewport.WorkPos)
ImGui.SetNextWindowSize(viewport.WorkSize)
ImGui.SetNextWindowViewport(viewport.ID)
local host_window_flags = 0
host_window_flags = bit32.bor(host_window_flags, 1 --[[ImGuiWindowFlags.NoTitleBar]], 32 --[[ImGuiWindowFlags.NoCollapse]], 2 --[[ImGuiWindowFlags.NoResize]], 4 --[[ImGuiWindowFlags.NoMove]], 524288 --[[ImGuiWindowFlags.NoDocking]])
host_window_flags = bit32.bor(host_window_flags, 8192 --[[ImGuiWindowFlags.NoBringToFrontOnFocus]], 131072 --[[ImGuiWindowFlags.NoNavFocus]])
if bit32.band(dockspace_flags, 8 --[[ImGuiDockNodeFlags.PassthruCentralNode]]) ~= 0 then
host_window_flags = bit32.bor(host_window_flags, 128 --[[ImGuiWindowFlags.NoBackground]])
end
-- FIXME-OPT: When using ImGuiDockNodeFlags.KeepAliveOnly with DockSpaceOverViewport() we might be able to spare submitting the window,
-- since DockSpace() with that flag doesn't need a window. We'd only need to compute the default ID accordingly.
if bit32.band(dockspace_flags, 1 --[[ImGuiDockNodeFlags.KeepAliveOnly]]) ~= 0 then
host_window_flags = bit32.bor(host_window_flags, 512 --[[ImGuiWindowFlags.NoMouseInputs]])
end
local label = string.format("WindowOverViewport_%08X", viewport.ID)
ImGui.PushStyleVar(3 --[[ImGuiStyleVar.WindowRounding]], 0.0)
ImGui.PushStyleVar(4 --[[ImGuiStyleVar.WindowBorderSize]], 0.0)
ImGui.PushStyleVar(2 --[[ImGuiStyleVar.WindowPadding]], ImVec2(0.0, 0.0))
ImGui.Begin(label, nil, host_window_flags)
ImGui.PopStyleVar(3)
-- Submit the dockspace
if dockspace_id == 0 then dockspace_id = ImGui.GetID("DockSpace") end
ImGui.DockSpace(dockspace_id, ImVec2(0.0, 0.0), dockspace_flags, window_class)
ImGui.End()
return dockspace_id
end
-------------------------------------------------------------------------------
-- Docking: Builder Functions
-------------------------------------------------------------------------------
-- Very early end-user API to manipulate dock nodes.
-- Only available in imgui_internal.h. Expect this API to change/break!
-- It is expected that those functions are all called _before_ the dockspace node submission.
-------------------------------------------------------------------------------
-- - DockBuilderDockWindow()
-- - DockBuilderGetNode()
-- - DockBuilderSetNodePos()
-- - DockBuilderSetNodeSize()
-- - DockBuilderAddNode()
-- - DockBuilderRemoveNode()
-- - DockBuilderRemoveNodeChildNodes()
-- - DockBuilderRemoveNodeDockedWindows()
-- - DockBuilderSplitNode()
-- - DockBuilderCopyNodeRec()
-- - DockBuilderCopyNode()
-- - DockBuilderCopyWindowSettings()
-- - DockBuilderCopyDockSpace()
-- - DockBuilderFinish()
-------------------------------------------------------------------------------
function ImGui.DockBuilderDockWindow(window_name, node_id)
-- We don't preserve relative order of multiple docked windows (by clearing DockOrder back to -1)
local window_id = ImHashStr(window_name)
local window = ImGui.FindWindowByID(window_id)
if window then
-- Apply to created window
local prev_node_id = window.DockId
ImGui.SetWindowDock(window, node_id, 1 --[[ImGuiCond.Always]])
if window.DockId ~= prev_node_id then window.DockOrder = -1 end
else
-- Apply to settings
local settings = ImGui.FindWindowSettingsByID(window_id)
if settings == nil then settings = ImGui.CreateNewWindowSettings(window_name) end
if settings.DockId ~= node_id then settings.DockOrder = -1 end
settings.DockId = node_id
end
end
function ImGui.DockBuilderGetNode(node_id)
return ImGui.DockContextFindNodeByID(ImGui.GetCurrentContext(), node_id)
end
function ImGui.DockBuilderGetCentralNode(node_id)
local node = ImGui.DockBuilderGetNode(node_id)
if not node then return nil end
return ImGui.DockNodeGetRootNode(node).CentralNode
end
function ImGui.DockBuilderSetNodePos(node_id, pos)
local node = ImGui.DockContextFindNodeByID(ImGui.GetCurrentContext(), node_id)
if node == nil then return end
node.Pos = ImVec2(pos.x, pos.y)
node.AuthorityForPos = 1 --[[ImGuiDataAuthority.DockNode]]
end
function ImGui.DockBuilderSetNodeSize(node_id, size)
local node = ImGui.DockContextFindNodeByID(ImGui.GetCurrentContext(), node_id)
if node == nil then return end
assert(size.x > 0.0 and size.y > 0.0)
node.Size = ImVec2(size.x, size.y)
node.SizeRef = ImVec2(size.x, size.y)
node.AuthorityForSize = 1 --[[ImGuiDataAuthority.DockNode]]
end
-- Make sure to use the ImGuiDockNodeFlags.DockSpace flag to create a dockspace node! Otherwise this will create a floating node!
-- - Floating node: you can then call DockBuilderSetNodePos()/DockBuilderSetNodeSize() to position and size the floating node.
-- - Dockspace node: calling DockBuilderSetNodePos() is unnecessary.
-- - If you intend to split a node immediately after creation using DockBuilderSplitNode(), make sure to call DockBuilderSetNodeSize() beforehand!
-- For various reason, the splitting code currently needs a base size otherwise space may not be allocated as precisely as you would expect.
-- - Use (id == 0) to let the system allocate a node identifier.
-- - Existing node with a same id will be removed.
function ImGui.DockBuilderAddNode(node_id, flags)
if node_id == nil then node_id = 0 end
if flags == nil then flags = 0 end
local g = ImGui.GetCurrentContext()
if node_id ~= 0 then ImGui.DockBuilderRemoveNode(node_id) end
local node
if bit32.band(flags, 1024 --[[ImGuiDockNodeFlags.DockSpace]]) ~= 0 then
ImGui.DockSpace(node_id, ImVec2(0, 0), bit32.bor(bit32.band(flags, bit32.bnot(1024 --[[ImGuiDockNodeFlags.DockSpace]])), 1 --[[ImGuiDockNodeFlags.KeepAliveOnly]]))
node = ImGui.DockContextFindNodeByID(g, node_id)
else
node = ImGui.DockContextAddNode(g, node_id)
node:SetLocalFlags(flags)
end
node.LastFrameAlive = g.FrameCount -- Set this otherwise BeginDocked will undock during the same frame.
return node.ID
end
function ImGui.DockBuilderRemoveNode(node_id)
local g = ImGui.GetCurrentContext()
local node = ImGui.DockContextFindNodeByID(g, node_id)
if node == nil then return end
ImGui.DockBuilderRemoveNodeDockedWindows(node_id, true)
ImGui.DockBuilderRemoveNodeChildNodes(node_id)
-- Node may have moved or deleted if e.g. any merge happened
node = ImGui.DockContextFindNodeByID(g, node_id)
if node == nil then return end
if node:IsCentralNode() and node.ParentNode then
node.ParentNode:SetLocalFlags(bit32.bor(node.ParentNode.LocalFlags, 2048 --[[ImGuiDockNodeFlags.CentralNode]]))
end
ImGui.DockContextRemoveNode(g, node, true)
end
--- @param a ImGuiDockNode
--- @param b ImGuiDockNode
local function DockNodeComparerDepthMostFirst(a, b) -- [Luau] table.sort() "less than" predicate
return ImGui.DockNodeGetDepth(a) > ImGui.DockNodeGetDepth(b)
end
-- root_id = 0 to remove all, root_id != 0 to remove child of given node.
function ImGui.DockBuilderRemoveNodeChildNodes(root_id)
local g = ImGui.GetCurrentContext()
local dc = g.DockContext
local root_node = (root_id ~= 0) and ImGui.DockContextFindNodeByID(g, root_id) or nil
if root_id ~= 0 and root_node == nil then return end
local has_central_node = false
local backup_root_node_authority_for_pos = root_node and root_node.AuthorityForPos or 0 --[[ImGuiDataAuthority.Auto]]
local backup_root_node_authority_for_size = root_node and root_node.AuthorityForSize or 0 --[[ImGuiDataAuthority.Auto]]
-- Process active windows
local nodes_to_remove = {}
for _, id in ipairs(DockContextNodesSorted(g)) do
local node = dc.Nodes[id]
if node then
local want_removal = (root_id == 0) or (node.ID ~= root_id and ImGui.DockNodeGetRootNode(node).ID == root_id)
if want_removal then
if node:IsCentralNode() then has_central_node = true end
if root_id ~= 0 then ImGui.DockContextQueueNotifyRemovedNode(g, node) end
if root_node then
ImGui.DockNodeMoveWindows(root_node, node)
DockSettingsRenameNodeReferences(node.ID, root_node.ID)
end
table.insert(nodes_to_remove, node)
end
end
end
-- DockNodeMoveWindows->DockNodeAddWindow will normally set those when reaching two windows (which is only adequate during interactive merge)
-- Make sure we don't lose our current pos/size. (FIXME-DOCK: Consider tidying up that code in DockNodeAddWindow instead)
if root_node then
root_node.AuthorityForPos = backup_root_node_authority_for_pos
root_node.AuthorityForSize = backup_root_node_authority_for_size
end
-- Apply to settings
for _, settings in g.SettingsWindows:iter() do
local window_settings_dock_id = settings.DockId
if window_settings_dock_id and window_settings_dock_id ~= 0 then
for _, n in ipairs(nodes_to_remove) do
if n.ID == window_settings_dock_id then
settings.DockId = root_id
break
end
end
end
end
-- Not really efficient, but easier to destroy a whole hierarchy considering DockContextRemoveNode is attempting to merge nodes
if #nodes_to_remove > 1 then
table.sort(nodes_to_remove, DockNodeComparerDepthMostFirst)
end
for _, n in ipairs(nodes_to_remove) do
ImGui.DockContextRemoveNode(g, n, false)
end
if root_id == 0 then
dc.Nodes = {}
dc.Requests:clear()
elseif has_central_node then
root_node.CentralNode = root_node
root_node:SetLocalFlags(bit32.bor(root_node.LocalFlags, 2048 --[[ImGuiDockNodeFlags.CentralNode]]))
end
end
function ImGui.DockBuilderRemoveNodeDockedWindows(root_id, clear_settings_refs)
if clear_settings_refs == nil then clear_settings_refs = true end
-- Clear references in settings
local g = ImGui.GetCurrentContext()
if clear_settings_refs then
for _, settings in g.SettingsWindows:iter() do
local want_removal = (root_id == 0) or (settings.DockId == root_id)
if not want_removal and settings.DockId ~= 0 then
local node = ImGui.DockContextFindNodeByID(g, settings.DockId)
if node and ImGui.DockNodeGetRootNode(node).ID == root_id then want_removal = true end
end
if want_removal then settings.DockId = 0 end
end
end
-- Clear references in windows
for n = 1, g.Windows.Size do
local window = g.Windows.Data[n]
local want_removal = (root_id == 0) or (window.DockNode and ImGui.DockNodeGetRootNode(window.DockNode).ID == root_id) or (window.DockNodeAsHost and window.DockNodeAsHost.ID == root_id)
if want_removal then
local backup_dock_id = window.DockId
ImGui.DockContextProcessUndockWindow(g, window, clear_settings_refs)
if not clear_settings_refs then assert(window.DockId == backup_dock_id) end
end
end
end
-- Returns (id_at_dir, id_at_opposite_dir): the IDs of the two new nodes created.
-- First return value is ID of the node at the specified direction.
-- FIXME-DOCK: We are not exposing nor using split_outer.
--- @return ImGuiID id_at_dir, ImGuiID id_at_opposite_dir
function ImGui.DockBuilderSplitNode(id, split_dir, size_ratio_for_node_at_dir)
local g = ImGui.GetCurrentContext()
assert(split_dir ~= (-1) --[[ImGuiDir.None]])
local node = ImGui.DockContextFindNodeByID(g, id)
if node == nil then
assert(node ~= nil)
return 0, 0
end
local req = ImGuiDockRequest()
req.Type = ImGuiDockRequestType.Split
req.DockTargetWindow = nil
req.DockTargetNode = node
req.DockPayload = nil
req.DockSplitDir = split_dir
req.DockSplitRatio = ImSaturate((split_dir == 0 --[[ImGuiDir.Left]] or split_dir == 2 --[[ImGuiDir.Up]]) and size_ratio_for_node_at_dir or (1.0 - size_ratio_for_node_at_dir))
req.DockSplitOuter = false
ImGui.DockContextProcessDock(g, req)
local first = (split_dir == 0 --[[ImGuiDir.Left]] or split_dir == 2 --[[ImGuiDir.Up]])
local id_at_dir = node.ChildNodes[first and 0 or 1].ID
local id_at_opposite_dir = node.ChildNodes[first and 1 or 0].ID
return id_at_dir, id_at_opposite_dir
end
local function DockBuilderCopyNodeRec(src_node, dst_node_id_if_known, out_node_remap_pairs)
local g = ImGui.GetCurrentContext()
local dst_node = ImGui.DockContextAddNode(g, dst_node_id_if_known)
dst_node.SharedFlags = src_node.SharedFlags
dst_node.LocalFlags = src_node.LocalFlags
dst_node.LocalFlagsInWindows = 0 --[[ImGuiDockNodeFlags.None]]
dst_node.Pos = ImVec2(src_node.Pos.x, src_node.Pos.y)
dst_node.Size = ImVec2(src_node.Size.x, src_node.Size.y)
dst_node.SizeRef = ImVec2(src_node.SizeRef.x, src_node.SizeRef.y)
dst_node.SplitAxis = src_node.SplitAxis
dst_node:UpdateMergedFlags()
table.insert(out_node_remap_pairs, src_node.ID)
table.insert(out_node_remap_pairs, dst_node.ID)
for child_n = 0, 1 do
if src_node.ChildNodes[child_n] then
dst_node.ChildNodes[child_n] = DockBuilderCopyNodeRec(src_node.ChildNodes[child_n], 0, out_node_remap_pairs)
dst_node.ChildNodes[child_n].ParentNode = dst_node
end
end
return dst_node
end
--- @param out_node_remap_pairs table # 1-based array, cleared and filled with (src_id, dst_id) pairs
function ImGui.DockBuilderCopyNode(src_node_id, dst_node_id, out_node_remap_pairs)
local g = ImGui.GetCurrentContext()
assert(src_node_id ~= 0)
assert(dst_node_id ~= 0)
assert(out_node_remap_pairs ~= nil)
ImGui.DockBuilderRemoveNode(dst_node_id)
local src_node = ImGui.DockContextFindNodeByID(g, src_node_id)
assert(src_node ~= nil)
for k in pairs(out_node_remap_pairs) do out_node_remap_pairs[k] = nil end
DockBuilderCopyNodeRec(src_node, dst_node_id, out_node_remap_pairs)
assert((#out_node_remap_pairs % 2) == 0)
end
function ImGui.DockBuilderCopyWindowSettings(src_name, dst_name)
local src_window = ImGui.FindWindowByName(src_name)
if src_window == nil then return end
local dst_window = ImGui.FindWindowByName(dst_name)
if dst_window then
ImVec2_Copy(dst_window.Pos, src_window.Pos)
ImVec2_Copy(dst_window.Size, src_window.Size)
ImVec2_Copy(dst_window.SizeFull, src_window.SizeFull)
dst_window.Collapsed = src_window.Collapsed
else
local dst_settings = ImGui.FindWindowSettingsByID(ImHashStr(dst_name))
if not dst_settings then dst_settings = ImGui.CreateNewWindowSettings(dst_name) end
local wx, wy = ImTrunc(src_window.Pos.x), ImTrunc(src_window.Pos.y)
if src_window.ViewportId ~= 0 and src_window.ViewportId ~= 0x11111111 then
ImVec2_CopyV(dst_settings.ViewportPos, wx, wy)
dst_settings.ViewportId = src_window.ViewportId
ImVec2_CopyV(dst_settings.Pos, 0, 0)
else
ImVec2_CopyV(dst_settings.Pos, wx, wy)
end
ImVec2_CopyV(dst_settings.Size, ImTrunc(src_window.SizeFull.x), ImTrunc(src_window.SizeFull.y))
dst_settings.Collapsed = src_window.Collapsed
end
end
-- FIXME: Will probably want to change this signature, in particular how the window remapping pairs are passed.
--- @param in_window_remap_pairs table # 1-based array of window names: src0, dst0, src1, dst1, ...
function ImGui.DockBuilderCopyDockSpace(src_dockspace_id, dst_dockspace_id, in_window_remap_pairs)
assert(src_dockspace_id ~= 0)
assert(dst_dockspace_id ~= 0)
assert(in_window_remap_pairs ~= nil)
assert((#in_window_remap_pairs % 2) == 0)
-- Duplicate entire dock
-- FIXME: When overwriting dst_dockspace_id, windows that aren't part of our dockspace window class but that are docked in a same node will be split apart,
-- whereas we could attempt to at least keep them together in a new, same floating node.
local node_remap_pairs = {}
ImGui.DockBuilderCopyNode(src_dockspace_id, dst_dockspace_id, node_remap_pairs)
-- Attempt to transition all the upcoming windows associated to dst_dockspace_id into the newly created hierarchy of dock nodes
-- (The windows associated to src_dockspace_id are staying in place)
local src_windows = {}
for remap_window_n = 1, #in_window_remap_pairs, 2 do
local src_window_name = in_window_remap_pairs[remap_window_n]
local dst_window_name = in_window_remap_pairs[remap_window_n + 1]
local src_window_id = ImHashStr(src_window_name)
src_windows[src_window_id] = true
-- Search in the remapping tables
local src_dock_id = 0
local src_window = ImGui.FindWindowByID(src_window_id)
if src_window then
src_dock_id = src_window.DockId
else
local src_window_settings = ImGui.FindWindowSettingsByID(src_window_id)
if src_window_settings then src_dock_id = src_window_settings.DockId end
end
local dst_dock_id = 0
for dock_remap_n = 1, #node_remap_pairs, 2 do
if node_remap_pairs[dock_remap_n] == src_dock_id then
dst_dock_id = node_remap_pairs[dock_remap_n + 1]
--node_remap_pairs[dock_remap_n] = node_remap_pairs[dock_remap_n + 1] = 0 -- Clear
break
end
end
if dst_dock_id ~= 0 then
-- Docked windows gets redocked into the new node hierarchy.
ImGui.DockBuilderDockWindow(dst_window_name, dst_dock_id)
else
-- Floating windows gets their settings transferred (regardless of whether the new window already exist or not)
-- When this is leading to a Copy and not a Move, we would get two overlapping floating windows. Could we possibly dock them together?
ImGui.DockBuilderCopyWindowSettings(src_window_name, dst_window_name)
end
end
-- Anything else in the source nodes of 'node_remap_pairs' are windows that are not included in the remapping list.
-- Find those windows and move to them to the cloned dock node. This may be optional?
-- Dock those are a second step as undocking would invalidate source dock nodes.
local dock_remaining_windows = {}
for dock_remap_n = 1, #node_remap_pairs, 2 do
local src_dock_id = node_remap_pairs[dock_remap_n]
if src_dock_id ~= 0 then
local dst_dock_id = node_remap_pairs[dock_remap_n + 1]
local node = ImGui.DockBuilderGetNode(src_dock_id)
for _, window in node.Windows:iter() do
if not src_windows[window.ID] then
-- Docked windows gets redocked into the new node hierarchy.
table.insert(dock_remaining_windows, { Window = window, DockId = dst_dock_id })
end
end
end
end
for _, task in ipairs(dock_remaining_windows) do
ImGui.DockBuilderDockWindow(task.Window.Name, task.DockId)
end
end
-- FIXME-DOCK: This is awkward because in series of split user is likely to loose access to its root node.
function ImGui.DockBuilderFinish(root_id)
--DockContextRebuild(g)
ImGui.DockContextBuildAddWindowsToNodes(ImGui.GetCurrentContext(), root_id)
end
-------------------------------------------------------------------------------
-- Docking: Begin/End Support Functions (called from Begin/End)
-------------------------------------------------------------------------------
-- - GetWindowAlwaysWantOwnTabBar()
-- - DockContextBindNodeToWindow()
-- - BeginDocked()
-- - BeginDockableDragDropSource()
-- - BeginDockableDragDropTarget()
-------------------------------------------------------------------------------
function ImGui.GetWindowAlwaysWantOwnTabBar(window)
local g = ImGui.GetCurrentContext()
if g.IO.ConfigDockingAlwaysTabBar or window.WindowClass.DockingAlwaysTabBar then
if bit32.band(window.Flags, bit32.bor(16777216 --[[ImGuiWindowFlags.ChildWindow]], 1 --[[ImGuiWindowFlags.NoTitleBar]], 524288 --[[ImGuiWindowFlags.NoDocking]])) == 0 then
if not window.IsFallbackWindow then return true end -- We don't support AlwaysTabBar on the fallback/implicit window to avoid unused dock-node overhead/noise
end
end
return false
end
function ImGui.DockContextBindNodeToWindow(ctx, window)
local g = ctx
local node = ImGui.DockContextFindNodeByID(ctx, window.DockId)
assert(window.DockNode == nil)
-- We should not be docking into a split node (SetWindowDock should avoid this)
if node and node:IsSplitNode() then
ImGui.DockContextProcessUndockWindow(ctx, window)
return nil
end
-- Create node
if node == nil then
node = ImGui.DockContextAddNode(ctx, window.DockId)
node.AuthorityForPos = 2 --[[ImGuiDataAuthority.Window]]
node.AuthorityForSize = 2 --[[ImGuiDataAuthority.Window]]
node.AuthorityForViewport = 2 --[[ImGuiDataAuthority.Window]]
node.LastFrameAlive = g.FrameCount
end
-- If the node just turned visible and is part of a hierarchy, it doesn't have a Size assigned by DockNodeTreeUpdatePosSize() yet,
-- so we're forcing a Pos/Size update from the first ancestor that is already visible (often it will be the root node).
-- If we don't do this, the window will be assigned a zero-size on its first frame, which won't ideally warm up the layout.
-- This is a little wonky because we don't normally update the Pos/Size of visible node mid-frame.
if not node.IsVisible then
local ancestor_node = node
while not ancestor_node.IsVisible and ancestor_node.ParentNode do
ancestor_node = ancestor_node.ParentNode
end
assert(ancestor_node.Size.x > 0.0 and ancestor_node.Size.y > 0.0)
ImGui.DockNodeUpdateHasCentralNodeChild(ImGui.DockNodeGetRootNode(ancestor_node))
ImGui.DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node.Pos, ancestor_node.Size, node)
end
-- Add window to node
local node_was_visible = node.IsVisible
ImGui.DockNodeAddWindow(node, window, true)
node.IsVisible = node_was_visible -- Don't mark visible right away (so DockContextEndFrame() doesn't render it, maybe other side effects? will see)
assert(node == window.DockNode)
return node
end
local function StoreDockStyleForWindow(window)
local g = ImGui.GetCurrentContext()
for color_n = 0, 9 --[[ImGuiWindowDockStyleCol.COUNT]] - 1 do
window.DockStyle.Colors[color_n] = ImGui.ColorConvertFloat4ToU32(g.Style.Colors[GWindowDockStyleColors[color_n]])
end
end
ImGui.StoreDockStyleForWindow = StoreDockStyleForWindow
--- @return bool? p_open
function ImGui.BeginDocked(window, p_open)
local g = ImGui.GetCurrentContext()
-- Specific extra processing for fallback window (#9151), could be in Begin() as well.
if window.IsFallbackWindow and not window.WasActive then
DockNodeHideWindowDuringHostWindowCreation(window)
return p_open
end
local auto_dock_node = ImGui.GetWindowAlwaysWantOwnTabBar(window)
if auto_dock_node then
if window.DockId == 0 then
assert(window.DockNode == nil)
window.DockId = ImGui.DockContextGenNodeID(g)
end
else
-- Calling SetNextWindowPos() undock windows by default (by setting PosUndock)
local want_undock = false
want_undock = want_undock or bit32.band(window.Flags, 524288 --[[ImGuiWindowFlags.NoDocking]]) ~= 0
want_undock = want_undock or (bit32.band(g.NextWindowData.HasFlags, 1 --[[ImGuiNextWindowDataFlags.HasPos]]) ~= 0 and bit32.band(window.SetWindowPosAllowFlags, g.NextWindowData.PosCond) ~= 0 and g.NextWindowData.PosUndock)
if want_undock then
ImGui.DockContextProcessUndockWindow(g, window)
return p_open
end
end
-- Bind to our dock node
local node = window.DockNode
if node ~= nil then assert(window.DockId == node.ID) end
if window.DockId ~= 0 and node == nil then
node = ImGui.DockContextBindNodeToWindow(g, window)
if node == nil then return p_open end
end
-- Undock if our dockspace node disappeared
-- Note how we are testing for LastFrameAlive and NOT LastFrameActive. A DockSpace node can be maintained alive while being inactive with ImGuiDockNodeFlags.KeepAliveOnly.
if node.LastFrameAlive < g.FrameCount then
-- If the window has been orphaned, transition the docknode to an implicit node processed in DockContextNewFrameUpdateDocking()
local root_node = ImGui.DockNodeGetRootNode(node)
if root_node.LastFrameAlive < g.FrameCount then
ImGui.DockContextProcessUndockWindow(g, window)
else
window.DockIsActive = true
end
return p_open
end
-- Store style overrides
StoreDockStyleForWindow(window)
-- Fast path return. It is common for windows to hold on a persistent DockId but be the only visible window,
-- and never create neither a host window neither a tab bar.
-- FIXME-DOCK: replace .HostWindow nil compare with something more explicit (~was initially intended as a first frame test)
if node.HostWindow == nil then
if node.State == 2 --[[ImGuiDockNodeState.HostWindowHiddenBecauseWindowsAreResizing]] then
window.DockIsActive = true
end
if node.Windows.Size > 1 and window.Appearing then -- Only hide appearing window
DockNodeHideWindowDuringHostWindowCreation(window)
end
return p_open
end
-- We can have zero-sized nodes (e.g. children of a small-size dockspace)
assert(node.HostWindow)
assert(node:IsLeafNode())
assert(node.Size.x >= 0.0 and node.Size.y >= 0.0)
node.State = 3 --[[ImGuiDockNodeState.HostWindowVisible]]
-- Undock if we are submitted earlier than the host window
if bit32.band(node.MergedFlags, 1 --[[ImGuiDockNodeFlags.KeepAliveOnly]]) == 0 and window.BeginOrderWithinContext < node.HostWindow.BeginOrderWithinContext then
ImGui.DockContextProcessUndockWindow(g, window)
return p_open
end
-- Position/Size window
ImGui.SetNextWindowPos(node.Pos)
ImGui.SetNextWindowSize(node.Size)
g.NextWindowData.PosUndock = false -- Cancel implicit undocking of SetNextWindowPos()
window.DockIsActive = true
window.DockNodeIsVisible = true
window.DockTabIsVisible = false
if bit32.band(node.MergedFlags, 1 --[[ImGuiDockNodeFlags.KeepAliveOnly]]) ~= 0 then return p_open end
-- When the window is selected we mark it as visible.
if node.VisibleWindow == window then window.DockTabIsVisible = true end
-- Update window flag
assert(bit32.band(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) == 0)
window.Flags = bit32.bor(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]], 2 --[[ImGuiWindowFlags.NoResize]])
window.ChildFlags = bit32.bor(window.ChildFlags, 2 --[[ImGuiChildFlags.AlwaysUseWindowPadding]])
if node:IsHiddenTabBar() or node:IsNoTabBar() then
window.Flags = bit32.bor(window.Flags, 1 --[[ImGuiWindowFlags.NoTitleBar]])
else
window.Flags = bit32.band(window.Flags, bit32.bnot(1 --[[ImGuiWindowFlags.NoTitleBar]])) -- Clear the NoTitleBar flag in case the user set it: confusingly enough we need a title bar height so we are correctly offset, but it won't be displayed!
end
-- Save new dock order only if the window has been visible once already
-- This allows multiple windows to be created in the same frame and have their respective dock orders preserved.
if node.TabBar and window.WasActive then
window.DockOrder = ImGui.DockNodeGetTabOrder(window)
end
if (node.WantCloseAll or node.WantCloseTabId == window.TabId) and p_open ~= nil then
p_open = false
end
-- Update ChildId to allow returning from Child to Parent with Escape
local parent_window = window.DockNode.HostWindow
window.ChildId = parent_window:GetID(window.Name)
return p_open
end
function ImGui.BeginDockableDragDropSource(window)
local g = ImGui.GetCurrentContext()
assert(g.ActiveId == window.MoveId)
assert(g.MovingWindow == window)
assert(g.CurrentWindow == window)
-- 0: Hold SHIFT to disable docking, 1: Hold SHIFT to enable docking.
if g.IO.ConfigDockingWithShift ~= g.IO.KeyShift then
-- When ConfigDockingWithShift is set, display a tooltip to increase UI affordance.
-- We cannot set for HoveredWindowUnderMovingWindow ~= nil here, as it is only valid/useful when drag and drop is already active
-- (because of the 'is_mouse_dragging_with_an_expected_destination' logic in UpdateViewportsNewFrame() function)
if g.IO.ConfigDockingWithShift and g.MouseStationaryTimer >= 1.0 and g.ActiveId >= 1 then
ImGui.SetTooltip("%s", ImGui.LocalizeGetMsg(14 --[[ImGuiLocKey.DockingHoldShiftToDock]]))
end
return
end
g.LastItemData.ID = window.MoveId
window = window.RootWindowDockTree
assert(bit32.band(window.Flags, 524288 --[[ImGuiWindowFlags.NoDocking]]) == 0)
local is_drag_docking = g.IO.ConfigDockingWithShift or ImRect(0, 0, window.SizeFull.x, ImGui.GetFrameHeight()):Contains(g.ActiveIdClickOffset) -- FIXME-DOCKING: Need to make this stateful and explicit
local drag_drop_flags = bit32.bor(1 --[[ImGuiDragDropFlags.SourceNoPreviewTooltip]], 4 --[[ImGuiDragDropFlags.SourceNoHoldToOpenOthers]], 32 --[[ImGuiDragDropFlags.PayloadAutoExpire]], 64 --[[ImGuiDragDropFlags.PayloadNoCrossContext]], 128 --[[ImGuiDragDropFlags.PayloadNoCrossProcess]])
if is_drag_docking and ImGui.BeginDragDropSource(drag_drop_flags) then
ImGui.SetDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, window)
ImGui.EndDragDropSource()
StoreDockStyleForWindow(window) -- Store style overrides while dragging (even when not docked) because docking preview may need it.
end
end
function ImGui.BeginDockableDragDropTarget(window)
local g = ImGui.GetCurrentContext()
--IM_ASSERT(window.RootWindowDockTree == window) -- May also be a DockSpace
assert(bit32.band(window.Flags, 524288 --[[ImGuiWindowFlags.NoDocking]]) == 0)
if not g.DragDropActive then return end
--GetForegroundDrawList(window):AddRect(window.Pos, window.Pos + window.Size, IM_COL32(255, 255, 0, 255))
if not ImGui.BeginDragDropTargetCustom(window:Rect(), window.ID) then return end
-- Peek into the payload before calling AcceptDragDropPayload() so we can handle overlapping dock nodes with filtering
-- (this is a little unusual pattern, normally most code would call AcceptDragDropPayload directly)
local payload = g.DragDropPayload
if not ImGuiPayload_IsDataType(payload, IMGUI_PAYLOAD_TYPE_WINDOW) or not ImGui.DockNodeIsDropAllowed(window, payload.Data) then
ImGui.EndDragDropTarget()
return
end
local payload_window = payload.Data
if ImGui.AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, bit32.bor(1024 --[[ImGuiDragDropFlags.AcceptBeforeDelivery]], 2048 --[[ImGuiDragDropFlags.AcceptNoDrawDefaultRect]])) then
-- Select target node
-- (Important: we cannot use g.HoveredDockNode here! Because each of our target node have filters based on payload, each candidate drop target will do its own evaluation)
local dock_into_floating_window = false
local node = nil
if window.DockNodeAsHost then
-- Cannot assume that node will ~= nil even though we passed the rectangle test: it depends on padding/spacing handled by DockNodeTreeFindVisibleNodeByPos().
node = ImGui.DockNodeTreeFindVisibleNodeByPos(window.DockNodeAsHost, g.IO.MousePos)
-- There is an edge case when docking into a dockspace which only has _inactive_ nodes (because none of the windows are active)
-- In this case we need to fallback into any leaf mode, possibly the central node.
-- FIXME-20181220: We should not have to test for IsLeafNode() here but we have another bug to fix first.
if node and node:IsDockSpace() and node:IsRootNode() then
node = (node.CentralNode and node:IsLeafNode()) and node.CentralNode or ImGui.DockNodeTreeFindFallbackLeafNode(node)
end
else
if window.DockNode then
node = window.DockNode
else
dock_into_floating_window = true -- Dock into a regular window
end
end
local explicit_target_rect
if node and node.TabBar and not node:IsHiddenTabBar() and not node:IsNoTabBar() then
explicit_target_rect = node.TabBar.BarRect
else
explicit_target_rect = ImRect(window.Pos, window.Pos + ImVec2(window.Size.x, ImGui.GetFrameHeight()))
end
local is_explicit_target = g.IO.ConfigDockingWithShift or ImGui.IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max)
-- Preview docking request and find out split direction/ratio
--local do_preview = true -- Ignore testing for payload:IsPreview() which removes one frame of delay, but breaks overlapping drop targets within the same window.
local do_preview = ImGuiPayload_IsPreview(payload) or ImGuiPayload_IsDelivery(payload)
if do_preview and (node ~= nil or dock_into_floating_window) then
-- If we have a non-leaf node it means we are hovering the border of a parent node, in which case only outer markers will appear.
local split_inner = ImGuiDockPreviewData()
local split_outer = ImGuiDockPreviewData()
local split_data = split_inner
if node and (node.ParentNode or node:IsCentralNode() or not node:IsLeafNode()) then
local root_node = ImGui.DockNodeGetRootNode(node)
if root_node then
ImGui.DockNodePreviewDockSetup(window, root_node, payload_window, nil, split_outer, is_explicit_target, true)
if split_outer.IsSplitDirExplicit then split_data = split_outer end
end
end
if not node or node:IsLeafNode() then
ImGui.DockNodePreviewDockSetup(window, node, payload_window, nil, split_inner, is_explicit_target, false)
end
if split_data == split_outer then split_inner.IsDropAllowed = false end
-- Draw inner then outer, so that previewed tab (in inner data) will be behind the outer drop boxes
ImGui.DockNodePreviewDockRender(window, node, payload_window, split_inner)
ImGui.DockNodePreviewDockRender(window, node, payload_window, split_outer)
-- Queue docking request
if split_data.IsDropAllowed and ImGuiPayload_IsDelivery(payload) then
ImGui.DockContextQueueDock(g, window, split_data.SplitNode, payload_window, split_data.SplitDir, split_data.SplitRatio, split_data == split_outer)
end
end
end
ImGui.EndDragDropTarget()
end
-------------------------------------------------------------------------------
-- Docking: Settings
-------------------------------------------------------------------------------
-- - DockSettingsRenameNodeReferences()
-- - DockSettingsRemoveNodeReferences()
-- - DockSettingsFindNodeSettings()
-- - DockSettingsHandler_ApplyAll()
-- - DockSettingsHandler_ReadOpen()
-- - DockSettingsHandler_ReadLine()
-- - DockSettingsHandler_DockNodeToSettings()
-- - DockSettingsHandler_WriteAll()
-------------------------------------------------------------------------------
DockSettingsRenameNodeReferences = function(old_node_id, new_node_id)
local g = ImGui.GetCurrentContext()
for _, window in g.Windows:iter() do
if window.DockId == old_node_id and window.DockNode == nil then window.DockId = new_node_id end
end
---- FIXME-OPT: We could remove this loop by storing the index in the map
for _, settings in g.SettingsWindows:iter() do
if settings.DockId == old_node_id then settings.DockId = new_node_id end
end
end
-- Remove references stored in ImGuiWindowSettings to the given ImGuiDockNodeSettings
--- @param node_ids table # 1-based array
DockSettingsRemoveNodeReferences = function(node_ids, node_ids_count)
local g = ImGui.GetCurrentContext()
local found = 0
---- FIXME-OPT: We could remove this loop by storing the index in the map
for _, settings in g.SettingsWindows:iter() do
for node_n = 1, node_ids_count do
if settings.DockId == node_ids[node_n] then
settings.DockId = 0
settings.DockOrder = -1
found = found + 1
if found < node_ids_count then break end
return
end
end
end
end
DockSettingsFindNodeSettings = function(ctx, id)
-- FIXME-OPT
for _, s in ctx.DockContext.NodesSettings:iter() do
if s.ID == id then return s end
end
return nil
end
-- Clear settings data
DockSettingsHandler_ClearAll = function(ctx, handler)
local dc = ctx.DockContext
dc.NodesSettings:clear()
ImGui.DockContextClearNodes(ctx, 0, true)
end
-- Recreate nodes based on settings data
DockSettingsHandler_ApplyAll = function(ctx, handler)
-- Prune settings at boot time only
local dc = ctx.DockContext
if ctx.Windows.Size == 0 then ImGui.DockContextPruneUnusedSettingsNodes(ctx) end
ImGui.DockContextBuildNodesFromSettings(ctx, dc.NodesSettings.Data or {}, dc.NodesSettings.Size)
ImGui.DockContextBuildAddWindowsToNodes(ctx, 0)
end
DockSettingsHandler_ReadOpen = function(ctx, handler, name)
if name ~= "Data" then return nil end
return true
end
DockSettingsHandler_ReadLine = function(ctx, handler, entry, line)
-- Parsing, e.g.
-- " DockNode ID=0x00000001 Pos=383,193 Size=201,322 Split=Y,0.506 "
-- " DockNode ID=0x00000002 Parent=0x00000001 "
-- Important: this code expect currently fields in a fixed order.
local node = ImGuiDockNodeSettings()
line = line:gsub("^[ \t]+", "")
if line:sub(1, 8) == "DockNode" then
line = line:sub(9)
elseif line:sub(1, 9) == "DockSpace" then
line = line:sub(10)
node.Flags = bit32.bor(node.Flags, 1024 --[[ImGuiDockNodeFlags.DockSpace]])
else
return
end
line = line:gsub("^[ \t]+", "")
local function take(pattern)
local caps = { line:match("^" .. pattern .. "()") }
if #caps == 0 then return nil end
line = line:sub(caps[#caps])
caps[#caps] = nil
return table.unpack(caps)
end
local v = take("ID=0x(%x+)")
if not v then return end
node.ID = tonumber(v, 16)
v = take(" Parent=0x(%x+)")
if v then node.ParentNodeId = tonumber(v, 16); if node.ParentNodeId == 0 then return end end
v = take(" Window=0x(%x+)")
if v then node.ParentWindowId = tonumber(v, 16); if node.ParentWindowId == 0 then return end end
if node.ParentNodeId == 0 then
local x, y = take(" Pos=(%-?%d+),(%-?%d+)")
if not x then return end
node.Pos = ImVec2(tonumber(x), tonumber(y))
x, y = take(" Size=(%-?%d+),(%-?%d+)")
if not x then return end
node.Size = ImVec2(tonumber(x), tonumber(y))
else
local x, y = take(" SizeRef=(%-?%d+),(%-?%d+)")
if x then node.SizeRef = ImVec2(tonumber(x), tonumber(y)) end
end
local c = take(" Split=(%a)")
if c then
if c == "X" then node.SplitAxis = 1 --[[ImGuiAxis.X]] elseif c == "Y" then node.SplitAxis = 2 --[[ImGuiAxis.Y]] end
end
local function flag(name, f)
local x = take(" " .. name .. "=(%-?%d+)")
if x and tonumber(x) ~= 0 then node.Flags = bit32.bor(node.Flags, f) end
end
flag("NoResize", 32 --[[ImGuiDockNodeFlags.NoResize]])
flag("CentralNode", 2048 --[[ImGuiDockNodeFlags.CentralNode]])
flag("NoTabBar", 4096 --[[ImGuiDockNodeFlags.NoTabBar]])
flag("HiddenTabBar", 8192 --[[ImGuiDockNodeFlags.HiddenTabBar]])
flag("NoWindowMenuButton", 16384 --[[ImGuiDockNodeFlags.NoWindowMenuButton]])
flag("NoCloseButton", 32768 --[[ImGuiDockNodeFlags.NoCloseButton]])
v = take(" Selected=0x(%x+)")
if v then node.SelectedTabId = tonumber(v, 16) end
if node.ParentNodeId ~= 0 then
local parent_settings = DockSettingsFindNodeSettings(ctx, node.ParentNodeId)
if parent_settings then node.Depth = parent_settings.Depth + 1 end
end
ctx.DockContext.NodesSettings:push_back(node)
end
local function DockSettingsHandler_DockNodeToSettings(dc, node, depth)
local node_settings = ImGuiDockNodeSettings()
node_settings.ID = node.ID
node_settings.ParentNodeId = node.ParentNode and node.ParentNode.ID or 0
node_settings.ParentWindowId = (node:IsDockSpace() and node.HostWindow and node.HostWindow.ParentWindow) and node.HostWindow.ParentWindow.ID or 0
node_settings.SelectedTabId = node.SelectedTabId
node_settings.SplitAxis = node:IsSplitNode() and node.SplitAxis or 0 --[[ImGuiAxis.None]]
node_settings.Depth = depth
node_settings.Flags = bit32.band(node.LocalFlags, 261152 --[[ImGuiDockNodeFlags.SavedFlagsMask_]])
node_settings.Pos = ImVec2(ImTrunc(node.Pos.x), ImTrunc(node.Pos.y))
node_settings.Size = ImVec2(ImTrunc(node.Size.x), ImTrunc(node.Size.y))
node_settings.SizeRef = ImVec2(ImTrunc(node.SizeRef.x), ImTrunc(node.SizeRef.y))
dc.NodesSettings:push_back(node_settings)
if node.ChildNodes[0] then DockSettingsHandler_DockNodeToSettings(dc, node.ChildNodes[0], depth + 1) end
if node.ChildNodes[1] then DockSettingsHandler_DockNodeToSettings(dc, node.ChildNodes[1], depth + 1) end
end
DockSettingsHandler_WriteAll = function(ctx, handler, buf)
local g = ctx
local dc = ctx.DockContext
if bit32.band(g.IO.ConfigFlags, 128 --[[ImGuiConfigFlags.DockingEnable]]) == 0 then return end
-- Gather settings data
-- (unlike our windows settings, because nodes are always built we can do a full rewrite of the SettingsNode buffer)
dc.NodesSettings:resize(0)
for _, id in ipairs(DockContextNodesSorted(ctx)) do
local node = dc.Nodes[id]
if node and node:IsRootNode() then
DockSettingsHandler_DockNodeToSettings(dc, node, 0)
end
end
local max_depth = 0
for _, s in dc.NodesSettings:iter() do max_depth = ImMax(s.Depth, max_depth) end
-- Write to text buffer
buf:appendf("[%s][Data]\n", handler.TypeName)
for _, s in dc.NodesSettings:iter() do
buf:append(string.rep(" ", s.Depth * 2) .. ((bit32.band(s.Flags, 1024 --[[ImGuiDockNodeFlags.DockSpace]]) ~= 0) and "DockSpace" or "DockNode ") .. string.rep(" ", (max_depth - s.Depth) * 2)) -- Text align nodes to facilitate looking at .ini file
buf:appendf(" ID=0x%08X", s.ID)
if s.ParentNodeId ~= 0 then
buf:appendf(" Parent=0x%08X SizeRef=%d,%d", s.ParentNodeId, s.SizeRef.x, s.SizeRef.y)
else
if s.ParentWindowId ~= 0 then buf:appendf(" Window=0x%08X", s.ParentWindowId) end
buf:appendf(" Pos=%d,%d Size=%d,%d", s.Pos.x, s.Pos.y, s.Size.x, s.Size.y)
end
if s.SplitAxis ~= 0 --[[ImGuiAxis.None]] then buf:appendf(" Split=%s", (s.SplitAxis == 1 --[[ImGuiAxis.X]]) and "X" or "Y") end
if bit32.band(s.Flags, 32 --[[ImGuiDockNodeFlags.NoResize]]) ~= 0 then buf:append(" NoResize=1") end
if bit32.band(s.Flags, 2048 --[[ImGuiDockNodeFlags.CentralNode]]) ~= 0 then buf:append(" CentralNode=1") end
if bit32.band(s.Flags, 4096 --[[ImGuiDockNodeFlags.NoTabBar]]) ~= 0 then buf:append(" NoTabBar=1") end
if bit32.band(s.Flags, 8192 --[[ImGuiDockNodeFlags.HiddenTabBar]]) ~= 0 then buf:append(" HiddenTabBar=1") end
if bit32.band(s.Flags, 16384 --[[ImGuiDockNodeFlags.NoWindowMenuButton]]) ~= 0 then buf:append(" NoWindowMenuButton=1") end
if bit32.band(s.Flags, 32768 --[[ImGuiDockNodeFlags.NoCloseButton]]) ~= 0 then buf:append(" NoCloseButton=1") end
if s.SelectedTabId ~= 0 then buf:appendf(" Selected=0x%08X", s.SelectedTabId) end
buf:append("\n")
end
buf:append("\n")
end
-------------------------------------------------------------------------------
-- Docking: helpers (imgui_internal.h inlines) + widgets used by docking
-------------------------------------------------------------------------------
function ImGui.DockNodeGetRootNode(node)
while node.ParentNode do node = node.ParentNode end
return node
end
function ImGui.DockNodeIsInHierarchyOf(node, parent)
while node do
if node == parent then return true end
node = node.ParentNode
end
return false
end
function ImGui.DockNodeGetDepth(node)
local depth = 0
while node.ParentNode do node = node.ParentNode; depth = depth + 1 end
return depth
end
function ImGui.DockNodeGetWindowMenuButtonId(node)
return ImHashStr("#COLLAPSE", nil, node.ID)
end
function ImGui.GetWindowDockID()
return ImGui.GetCurrentContext().CurrentWindow.DockId
end
function ImGui.IsWindowDocked()
return ImGui.GetCurrentContext().CurrentWindow.DockIsActive
end
function ImGui.SetNextWindowDockID(id, cond)
local g = ImGui.GetCurrentContext()
g.NextWindowData.HasFlags = bit32.bor(g.NextWindowData.HasFlags, 4096 --[[ImGuiNextWindowDataFlags.HasDock]])
g.NextWindowData.DockCond = (cond and cond ~= 0) and cond or 1 --[[ImGuiCond.Always]]
g.NextWindowData.DockId = id
end
-- We use 'undock == false' when dragging from title bar to allow moving groups of floating nodes without undocking them.
function ImGui.StartMouseMovingWindowOrNode(window, node, undock)
local g = ImGui.GetCurrentContext()
local can_undock_node = false
if undock and node ~= nil and node.VisibleWindow and bit32.band(node.VisibleWindow.Flags, 4 --[[ImGuiWindowFlags.NoMove]]) == 0 and bit32.band(node.MergedFlags, 128 --[[ImGuiDockNodeFlags.NoUndocking]]) == 0 then
-- Can undock if:
-- - part of a hierarchy with more than one visible node (if only one is visible, we'll just move the root window)
-- - part of a dockspace node hierarchy: so we can undock the last single visible node too. Undocking from a fixed/central node will create a new node and copy windows.
local root_node = ImGui.DockNodeGetRootNode(node)
if root_node.OnlyNodeWithWindows ~= node or root_node.CentralNode ~= nil then
can_undock_node = true
end
end
local clicked = ImGui.IsMouseClicked(0)
local dragging = ImGui.IsMouseDragging(0)
if can_undock_node and dragging then
ImGui.DockContextQueueUndockNode(g, node) -- Will lead to DockNodeStartMouseMovingWindow() -> StartMouseMovingWindow() being called next frame
elseif not can_undock_node and (clicked or dragging) and g.MovingWindow ~= window then
ImGui.StartMouseMovingWindow(window)
end
end
-- This is less wide than RenderArrow() and we use in dock nodes instead of the regular RenderArrow() to denote a change of functionality,
-- and because the saved space means that the left-most tab label can stay at exactly the same position as the label of a loose window.
function ImGui.RenderArrowDockMenu(draw_list, p_min, sz, col)
draw_list:AddRectFilled(p_min + ImVec2(sz * 0.20, sz * 0.15), p_min + ImVec2(sz * 0.80, sz * 0.30), col)
ImGui.RenderArrowPointingAt(draw_list, p_min + ImVec2(sz * 0.50, sz * 0.85), ImVec2(sz * 0.30, sz * 0.40), 3 --[[ImGuiDir.Down]], col)
end
-- Upstream CollapseButton (docking branch): replaces the port's simpler version
-- The Collapse button also functions as a Dock Menu button.
function ImGui.CollapseButton(id, pos, dock_node)
local g = ImGui.GetCurrentContext()
local window = g.CurrentWindow
local bb = ImRect(pos, pos + ImVec2(g.FontSize, g.FontSize))
local is_clipped = not ImGui.ItemAdd(bb, id)
local pressed, hovered, held = ImGui.ButtonBehavior(bb, id, 0 --[[ImGuiButtonFlags.None]])
if is_clipped then return pressed end
-- Render
--local is_dock_menu = (window.DockNodeAsHost and not window.Collapsed)
local bg_col = ImGui.GetColorU32((held and hovered) and 24 --[[ImGuiCol.ButtonActive]] or (hovered and 23 --[[ImGuiCol.ButtonHovered]] or 22 --[[ImGuiCol.Button]]))
local text_col = ImGui.GetColorU32(0 --[[ImGuiCol.Text]])
if hovered or held then
window.DrawList:AddRectFilled(bb.Min, bb.Max, bg_col)
end
ImGui.RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags and 2 --[[ImGuiNavRenderCursorFlags.Compact]] or nil)
if dock_node then
ImGui.RenderArrowDockMenu(window.DrawList, bb.Min, g.FontSize, text_col)
else
ImGui.RenderArrow(window.DrawList, bb.Min, text_col, window.Collapsed and 1 --[[ImGuiDir.Right]] or 3 --[[ImGuiDir.Down]], 1.0)
end
-- Switch to moving the window after mouse is moved beyond the initial drag threshold
if ImGui.IsItemActive() and ImGui.IsMouseDragging(0) then
ImGui.StartMouseMovingWindowOrNode(window, dock_node, true) -- Undock from window/collapse menu button
end
return pressed
end
-- Using 'hover_visibility_delay' allows us to hide the highlight and mouse cursor for a short time, which can be convenient to reduce visual noise.
--- @return bool held, float size1, float size2
function ImGui.SplitterBehavior(bb, id, axis, size1, size2, min_size1, min_size2, hover_extend, hover_visibility_delay, bg_col)
hover_extend = hover_extend or 0.0
hover_visibility_delay = hover_visibility_delay or 0.0
bg_col = bg_col or 0
local g = ImGui.GetCurrentContext()
local window = g.CurrentWindow
if not ImGui.ItemAdd(bb, id, nil, 2 --[[ImGuiItemFlags.NoNav]]) then return false, size1, size2 end
-- FIXME: AFAIK the only leftover reason for passing ImGuiButtonFlags.AllowOverlap here is
-- to allow caller of SplitterBehavior() to call SetItemAllowOverlap() after the item.
-- Nowadays we would instead want to use SetNextItemAllowOverlap() before the item.
local button_flags = bit32.bor(2048 --[[ImGuiButtonFlags.FlattenChildren]], 4096 --[[ImGuiButtonFlags.AllowOverlap]])
local bb_interact = ImRect(bb.Min, bb.Max)
bb_interact:Expand(axis == 2 --[[ImGuiAxis.Y]] and ImVec2(0.0, hover_extend) or ImVec2(hover_extend, 0.0))
local _, hovered, held = ImGui.ButtonBehavior(bb_interact, id, button_flags)
if hovered then
g.LastItemData.StatusFlags = bit32.bor(g.LastItemData.StatusFlags, 1 --[[ImGuiItemStatusFlags.HoveredRect]]) -- for IsItemHovered(), because bb_interact is larger than bb
end
if held or (hovered and g.HoveredIdPreviousFrame == id and g.HoveredIdTimer >= hover_visibility_delay) then
ImGui.SetMouseCursor(axis == 2 --[[ImGuiAxis.Y]] and 3 --[[ImGuiMouseCursor.ResizeNS]] or 4 --[[ImGuiMouseCursor.ResizeEW]])
end
local bb_render = ImRect(bb.Min, bb.Max)
if held then
local d = g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min
local mouse_delta = d[axis]
-- Minimum pane size
local size_1_maximum_delta = ImMax(0.0, size1 - min_size1)
local size_2_maximum_delta = ImMax(0.0, size2 - min_size2)
if mouse_delta < -size_1_maximum_delta then mouse_delta = -size_1_maximum_delta end
if mouse_delta > size_2_maximum_delta then mouse_delta = size_2_maximum_delta end
-- Apply resize
if mouse_delta ~= 0.0 then
size1 = ImMax(size1 + mouse_delta, min_size1)
size2 = ImMax(size2 - mouse_delta, min_size2)
bb_render:Translate(axis == 1 --[[ImGuiAxis.X]] and ImVec2(mouse_delta, 0.0) or ImVec2(0.0, mouse_delta))
ImGui.MarkItemEdited(id)
end
end
-- Render at new position
if bit32.band(bg_col, 4278190080 --[[IM_COL32_A_MASK]]) ~= 0 then
window.DrawList:AddRectFilled(bb_render.Min, bb_render.Max, bg_col, 0.0)
end
local col = ImGui.GetColorU32(held and 30 --[[ImGuiCol.SeparatorActive]] or ((hovered and g.HoveredIdTimer >= hover_visibility_delay) and 29 --[[ImGuiCol.SeparatorHovered]] or 28 --[[ImGuiCol.Separator]]))
window.DrawList:AddRectFilled(bb_render.Min, bb_render.Max, col, 0.0)
return held, size1, size2
end
end --[[ [SECTION] imgui_docking (was imgui_docking.lua) ]]
-- Debug options
local IMGUI_DEBUG_NAV_SCORING = false -- Display navigation scoring preview when hovering items. Hold Ctrl to display for all candidates. Ctrl+Arrow to change last direction.
local IMGUI_DEBUG_NAV_RECTS = false -- Display the reference navigation rectangle for each window
-- Default font size if unspecified in both style.FontSizeBase and AddFontXXX() calls.
local FONT_DEFAULT_SIZE_BASE = 20
local WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04 -- Reduce visual noise by only highlighting the border after a certain time.
local WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 0.70 -- Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved.
-- Tooltip offset
local TOOLTIP_DEFAULT_OFFSET_MOUSE = ImVec2(16, 10) -- Multiplied by g.Style.MouseCursorScale
local TOOLTIP_DEFAULT_OFFSET_TOUCH = ImVec2(0, -20) -- Multiplied by g.Style.MouseCursorScale
local TOOLTIP_DEFAULT_PIVOT_TOUCH = ImVec2(0.5, 1.0) -- Multiplied by g.Style.MouseCursorScale
local IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111 -- Using an arbitrary constant instead of e.g. ImHashStr("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter.
local string = string
ImFormatString = string.format -- TODO: an simplified version that operates on byte tables directly?
--- @module "imstd_minstdio"
ImStd.sscanf = IM_INCLUDE"imstd_minstdio.lua"
local math = math
local bit32 = bit32
-------------------------------------------------------------------------------
-- [SECTION] MISC HELPERS/UTILITIES (File functions)
-------------------------------------------------------------------------------
--- This closes the _file.
--- @param _file any # File object
function IM_FILE_CLOSE(_file) end
--- This returns the size of _file in bytes.
--- @param _file any # File object
--- @return int
function IM_FILE_SIZE(_file) return -1 end
--- This writes _str into _file.
--- @param _file any # File object
--- @param _str string
function IM_FILE_WRITE(_file, _str) end
--- This reads the specified _count of chars and returns them as a binary string.
--- @param _file any # File object
--- @param _count int
--- @return string
function IM_FILE_READ(_file, _count) return "" end
--- This opens the file at _filename in _mode and returns the File object.
--- @param _filename string
--- @param _mode string # e.g. "rb"
--- @return any
function ImStd.ImFileOpen(_filename, _mode) end
--- [GMod] Platform specific
if gmod then
IM_FILE_CLOSE = FindMetaTable("File").Close --- @type function
IM_FILE_SIZE = FindMetaTable("File").Size --- @type function
IM_FILE_WRITE = FindMetaTable("File").Write --- @type function
IM_FILE_READ = FindMetaTable("File").Read --- @type function
end
-- Default file functions
if not IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS then
--- [GMod] Another Platform specific
if gmod then
function ImStd.ImFileOpen(filename, mode) return file.Open(filename, mode, "GAME") end
end
function ImStd.ImFileClose(f) IM_FILE_CLOSE(f) end
function ImStd.ImFileGetSize(f) return IM_FILE_SIZE(f) end
--- @param f any # File object
--- @param data table # 1-based table to store result data
--- @param count int # Amount of bytes to read from `f`
function ImStd.ImFileRead(f, data, count)
local CHUNK_SIZE = 8000 -- We don't read byte by byte
local offset = 0
while offset < count do
local read_size = math.min(CHUNK_SIZE, count - offset)
local str = IM_FILE_READ(f, read_size)
local bytes = {string.byte(str, 1, read_size)}
for i = 1, read_size do
data[offset + i] = bytes[i]
end
offset = offset + read_size
end
end
-- Helper: Load file content into memory
-- This can't really be used with "rt" because fseek size won't match read size.
--- @param filename string
--- @param mode string
--- @return table?, integer?
function ImStd.ImFileLoadToMemory(filename, mode)
local f = ImStd.ImFileOpen(filename, mode)
if not f then return end
local file_size = ImStd.ImFileGetSize(f)
if file_size <= 0 then
ImStd.ImFileClose(f)
return
end
local file_data = {}
ImStd.ImFileRead(f, file_data, file_size)
if #file_data == 0 then
ImStd.ImFileClose(f)
return
end
ImStd.ImFileClose(f)
return file_data, file_size
end
end
--- Forward Declarations
local CalcNextScrollFromScrollTargetAndClamp
local MT = ImGui.GetMetatables()
-- Scale all spacing/padding/thickness values. Do not scale fonts.
-- Consider not calling this if your initial scale factor if <1.0.
-- Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times.
--- @param scale_factor float
function MT.ImGuiStyle:ScaleAllSizes(scale_factor)
self._MainScale = self._MainScale * scale_factor
self.WindowPadding = ImTrunc(self.WindowPadding * scale_factor)
self.WindowRounding = ImTrunc(self.WindowRounding * scale_factor)
self.WindowBorderSize = ImTrunc(self.WindowBorderSize * scale_factor)
self.WindowMinSize = ImTrunc(self.WindowMinSize * scale_factor)
self.WindowBorderHoverPadding = ImTrunc(self.WindowBorderHoverPadding * scale_factor)
self.ChildRounding = ImTrunc(self.ChildRounding * scale_factor)
self.ChildBorderSize = ImTrunc(self.ChildBorderSize * scale_factor)
self.PopupRounding = ImTrunc(self.PopupRounding * scale_factor)
self.PopupBorderSize = ImTrunc(self.PopupBorderSize * scale_factor)
self.FramePadding = ImTrunc(self.FramePadding * scale_factor)
self.FrameBorderSize = ImTrunc(self.FrameBorderSize * scale_factor)
self.FrameRounding = ImTrunc(self.FrameRounding * scale_factor)
self.ItemSpacing = ImTrunc(self.ItemSpacing * scale_factor)
self.ItemInnerSpacing = ImTrunc(self.ItemInnerSpacing * scale_factor)
self.CellPadding = ImTrunc(self.CellPadding * scale_factor)
self.TouchExtraPadding = ImTrunc(self.TouchExtraPadding * scale_factor)
self.IndentSpacing = ImTrunc(self.IndentSpacing * scale_factor)
self.ColumnsMinSpacing = ImTrunc(self.ColumnsMinSpacing * scale_factor)
self.ScrollbarSize = ImTrunc(self.ScrollbarSize * scale_factor)
self.ScrollbarRounding = ImTrunc(self.ScrollbarRounding * scale_factor)
self.ScrollbarPadding = ImTrunc(self.ScrollbarPadding * scale_factor)
self.GrabMinSize = ImTrunc(self.GrabMinSize * scale_factor)
self.GrabRounding = ImTrunc(self.GrabRounding * scale_factor)
self.LogSliderDeadzone = ImTrunc(self.LogSliderDeadzone * scale_factor)
self.ImageRounding = ImTrunc(self.ImageRounding * scale_factor)
self.ImageBorderSize = ImTrunc(self.ImageBorderSize * scale_factor)
self.TabRounding = ImTrunc(self.TabRounding * scale_factor)
self.TabBorderSize = ImTrunc(self.TabBorderSize * scale_factor)
self.TabMinWidthBase = ImTrunc(self.TabMinWidthBase * scale_factor)
self.TabMinWidthShrink = ImTrunc(self.TabMinWidthShrink * scale_factor)
self.TabCloseButtonMinWidthSelected = (self.TabCloseButtonMinWidthSelected > 0.0 and self.TabCloseButtonMinWidthSelected ~= 3.4028234663853e+38 --[[FLT_MAX]]) and ImTrunc(self.TabCloseButtonMinWidthSelected * scale_factor) or self.TabCloseButtonMinWidthSelected
self.TabCloseButtonMinWidthUnselected = (self.TabCloseButtonMinWidthUnselected > 0.0 and self.TabCloseButtonMinWidthUnselected ~= 3.4028234663853e+38 --[[FLT_MAX]]) and ImTrunc(self.TabCloseButtonMinWidthUnselected * scale_factor) or self.TabCloseButtonMinWidthUnselected
self.TabBarBorderSize = ImTrunc(self.TabBarBorderSize * scale_factor)
self.TabBarOverlineSize = ImTrunc(self.TabBarOverlineSize * scale_factor)
self.TreeLinesSize = ImTrunc(self.TreeLinesSize * scale_factor)
self.TreeLinesRounding = ImTrunc(self.TreeLinesRounding * scale_factor)
self.MenuItemRounding = ImTrunc(self.MenuItemRounding * scale_factor)
self.SelectableRounding = ImTrunc(self.SelectableRounding * scale_factor)
self.DragDropTargetRounding = ImTrunc(self.DragDropTargetRounding * scale_factor)
self.DragDropTargetBorderSize = ImTrunc(self.DragDropTargetBorderSize * scale_factor)
self.DragDropTargetPadding = ImTrunc(self.DragDropTargetPadding * scale_factor)
self.ColorMarkerSize = ImTrunc(self.ColorMarkerSize * scale_factor)
self.InputTextCursorSize = ImTrunc(self.InputTextCursorSize * scale_factor)
self.SeparatorSize = ImTrunc(self.SeparatorSize * scale_factor)
self.SeparatorTextBorderSize = ImTrunc(self.SeparatorTextBorderSize * scale_factor)
self.SeparatorTextPadding = ImTrunc(self.SeparatorTextPadding * scale_factor)
self.DockingSeparatorSize = ImTrunc(self.DockingSeparatorSize * scale_factor)
self.DisplayWindowPadding = ImTrunc(self.DisplayWindowPadding * scale_factor)
self.DisplaySafeAreaPadding = ImTrunc(self.DisplaySafeAreaPadding * scale_factor)
self.MouseCursorScale = ImTrunc(self.MouseCursorScale * scale_factor)
end
-- Data for resizing from resize grip / corner
local ImGuiResizeGripDef = {
{CornerPosN = ImVec2(1, 1), InnerDir = ImVec2(-1, -1), AngleMin12 = 0, AngleMax12 = 3}, -- Lower-right
{CornerPosN = ImVec2(0, 1), InnerDir = ImVec2( 1, -1), AngleMin12 = 3, AngleMax12 = 6} -- Lower-left
}
-- Data for resizing from borders
-- InnerDir: Normal toward inside, SegmentN1/SegmentN2: End positions, normalized (0,0: upper left), OuterAngle: Angle toward outside
--- [1] Left, [2] Right, [3] Up, [4] Down
local ImGuiResizeBorderDef = {
{InnerDir = ImVec2( 1, 0), SegmentN1 = ImVec2( 0, 1), SegmentN2 = ImVec2( 0, 0), OuterAngle = IM_PI * 1.00}, -- Left
{InnerDir = ImVec2(-1, 0), SegmentN1 = ImVec2( 1, 0), SegmentN2 = ImVec2( 1, 1), OuterAngle = IM_PI * 0.00}, -- Right
{InnerDir = ImVec2( 0, 1), SegmentN1 = ImVec2( 0, 0), SegmentN2 = ImVec2( 1, 0), OuterAngle = IM_PI * 1.50}, -- Up
{InnerDir = ImVec2( 0, -1), SegmentN1 = ImVec2( 1, 1), SegmentN2 = ImVec2( 0, 1), OuterAngle = IM_PI * 0.50} -- Down
}
--- @param window ImGuiWindow
--- @param border_n int
--- @param perp_padding float
--- @param thickness float
--- @return ImRect
--- @nodiscard
local function GetResizeBorderRect(window, border_n, perp_padding, thickness)
local rect = window:Rect()
if thickness == 0.0 then
ImVec2_CopyV(rect.Max, rect.Max.x - 1, rect.Max.y - 1)
end
if border_n == 0 --[[ImGuiDir.Left]] then
return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding)
end
if border_n == 1 --[[ImGuiDir.Right]] then
return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding)
end
if border_n == 2 --[[ImGuiDir.Up]] then
return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness)
end
if border_n == 3 --[[ImGuiDir.Down]] then
return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness)
end
assert(false)
return ImRect()
end
-- Known size hash
-- It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
--- @param data table|number
--- @param size int? # size = -1 to indicate that `data` is a single number
--- @param seed int?
--- @return int
function ImHashData(data, size, seed)
seed = seed or 0
local FNV_OFFSET_BASIS = 0x811C9DC5
local FNV_PRIME = 0x01000193
local hash = bit32.bxor(FNV_OFFSET_BASIS, seed)
if size == -1 then --- @cast data number
hash = bit32.bxor(hash, data)
hash = bit32.band(hash * FNV_PRIME, 0xFFFFFFFF)
else
size = size or #data
for i = 1, size do
hash = bit32.bxor(hash, data[i])
hash = bit32.band(hash * FNV_PRIME, 0xFFFFFFFF)
end
end
return hash
end
-- Zero-terminated string hash, with support for ### to reset back to seed value.
-- e.g. "label###id" outputs the same hash as "id" (and "label" is generally displayed by the UI functions)
-- Use FNV1a, as one ImGui FIXME suggested
--- @param str string
--- @param size int?
--- @param seed int?
--- @return int
-- [Luau] memo for whole-string hashes: labels are re-hashed with the same seed every frame.
-- ponytail: whole-memo reset at HASH_MEMO_MAX entries, LRU if ID churn makes it thrash
local hash_memo, hash_memo_n, HASH_MEMO_MAX = {}, 0, 16384
local ImHashStrRaw
function ImHashStr(str, size, seed)
if seed == nil then seed = 0 end
if size == nil or size == #str then
local m = hash_memo[seed]
if m == nil then m = {}; hash_memo[seed] = m end
local h = m[str]
if h == nil then
if hash_memo_n >= HASH_MEMO_MAX then hash_memo = { [seed] = m }; table.clear(m); hash_memo_n = 0 end
h = ImHashStrRaw(str, #str, seed)
m[str] = h
hash_memo_n = hash_memo_n + 1
end
return h
end
return ImHashStrRaw(str, size, seed)
end
@native
function ImHashStrRaw(str, size, seed)
if str == "" or size == 0 then
return seed -- need to match cpp code edge case behavior while using FNV ourselves
end
local FNV_OFFSET_BASIS = 0x811C9DC5
local FNV_PRIME = 0x01000193
local hash = bit32.bxor(FNV_OFFSET_BASIS, seed)
local i = 1
local c
while i <= size do
c = string.byte(str, i)
-- `###` to reset back to initial hash value
if c == 35 and string.byte(str, i + 1) == 35 and string.byte(str, i + 2) == 35 then
hash = bit32.bxor(FNV_OFFSET_BASIS, seed)
i = i + 2
else
hash = bit32.bxor(hash, c)
hash = bit32.band(hash * FNV_PRIME, 0xFFFFFFFF)
end
i = i + 1
end
return hash
end
do --[[CharFromUtf8]]
local lengths = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0}
local masks = {0x00, 0x7f, 0x1f, 0x0f, 0x07}
local mins = {0x400000, 0, 0x80, 0x800, 0x10000}
local shiftc = {0, 18, 12, 6, 0}
local shifte = {0, 6, 4, 2, 0}
local s = {0, 0, 0, 0}
-- Convert UTF-8 to 32-bit character, process single character input.
-- A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8).
-- We handle UTF-8 decoding error by skipping forward.
--- @param in_text ImString
--- @param pos int
--- @param in_text_end int
--- @return int wanted
--- @return unsigned_int out_char
function ImStd.ImTextCharFromUtf8(in_text, pos, in_text_end)
local len = lengths[bit32.rshift(ImStrByte(in_text, pos), 3) + 1]
local wanted = len > 0 and len or 1
-- IMPORTANT: if in_text_end == nil it assume we have enough space!
if in_text_end == nil then
in_text_end = pos + wanted -- Max length, nulls will be taken into account.
end
-- Copy at most 'len' bytes, stop copying at 0 or past in_text_end. Branch predictor does a good job here,
-- so it is fast even with excessive branching.
s[1] = (pos < in_text_end) and ImStrByte(in_text, pos) or 0
s[2] = (pos + 1 < in_text_end) and ImStrByte(in_text, pos + 1) or 0
s[3] = (pos + 2 < in_text_end) and ImStrByte(in_text, pos + 2) or 0
s[4] = (pos + 3 < in_text_end) and ImStrByte(in_text, pos + 3) or 0
-- Assume a four-byte character and load four bytes. Unused bits are shifted out.
local out_char
out_char = bit32.lshift(bit32.band(s[1], masks[len + 1]), 18)
out_char = bit32.bor(out_char, bit32.lshift(bit32.band(s[2], 0x3f), 12))
out_char = bit32.bor(out_char, bit32.lshift(bit32.band(s[3], 0x3f), 6))
out_char = bit32.bor(out_char, bit32.lshift(bit32.band(s[4], 0x3f), 0))
out_char = bit32.rshift(out_char, shiftc[len + 1])
-- Accumulate the various error conditions.
local e = 0
e = bit32.lshift((out_char < mins[len + 1]) and 1 or 0, 6) -- non-canonical encoding
e = bit32.bor(e, bit32.lshift((bit32.rshift(out_char, 11) == 0x1b) and 1 or 0, 7)) -- surrogate half?
e = bit32.bor(e, bit32.lshift((out_char > 65535 --[[IM_UNICODE_CODEPOINT_MAX]]) and 1 or 0, 8)) -- out of range we can store in ImWchar (FIXME: May evolve)
e = bit32.bor(e, bit32.rshift(bit32.band(s[2], 0xc0), 2))
e = bit32.bor(e, bit32.rshift(bit32.band(s[3], 0xc0), 4))
e = bit32.bor(e, bit32.rshift(s[4], 6))
e = bit32.bxor(e, 0x2a) -- top two bits of each tail byte correct?
e = bit32.rshift(e, shifte[len + 1])
if e ~= 0 then
-- No bytes are consumed when in_text[pos] == 0 || pos == in_text_end.
-- One byte is consumed in case of invalid first byte of in_text.
-- All available bytes (at most `len` bytes) are consumed on incomplete/invalid second to last bytes.
-- Invalid or incomplete input may consume less bytes than wanted, therefore every byte has to be inspected in s.
wanted = ImMin(wanted, (s[1] ~= 0 and 1 or 0) + (s[2] ~= 0 and 1 or 0) + (s[3] ~= 0 and 1 or 0) + (s[4] ~= 0 and 1 or 0))
out_char = 65533 --[[IM_UNICODE_CODEPOINT_INVALID]]
end
return wanted, out_char
end
end
-- Based on stb_to_utf8() from github.com/nothings/stb/
--- @param buf char[]
--- @param buf_size int
--- @param c unsigned_int
local function ImTextCharToUtf8_inline(buf, buf_size, c)
if c < 0x80 then
buf[1] = c
return 1
end
if c < 0x800 then
if buf_size < 2 then
return 0
end
buf[1] = (0xc0 + bit32.rshift(c, 6))
buf[2] = (0x80 + bit32.band(c, 0x3f))
return 2
end
if c < 0x10000 then
if buf_size < 3 then
return 0
end
buf[1] = (0xe0 + bit32.rshift(c, 12))
buf[2] = (0x80 + bit32.band(bit32.rshift(c, 6), 0x3f))
buf[3] = (0x80 + bit32.band(c, 0x3f))
return 3
end
if c <= 0x10FFFF then
if buf_size < 4 then
return 0
end
buf[1] = (0xf0 + bit32.rshift(c, 18))
buf[2] = (0x80 + bit32.band(bit32.rshift(c, 12), 0x3f))
buf[3] = (0x80 + bit32.band(bit32.rshift(c, 6), 0x3f))
buf[4] = (0x80 + bit32.band(c, 0x3f))
return 4
end
-- Invalid code point, the max unicode is 0x10FFFF
return 0
end
--- @param out_buf [char, char, char, char, char]
--- @param c unsigned_int
function ImStd.ImTextCharToUtf8(out_buf, c)
local count = ImTextCharToUtf8_inline(out_buf, 5, c)
out_buf[count + 1] = 0
return count
end
-- Not optimal but we very rarely use this function.
--- @param in_text ImString
--- @param pos int
--- @param in_text_end int
--- @return int
function ImStd.ImTextCountUtf8BytesFromChar(in_text, pos, in_text_end)
local bytes, unused = ImStd.ImTextCharFromUtf8(in_text, pos, in_text_end)
return bytes
end
--- @param text ImString
--- @param in_text_start int
--- @param in_p int
function ImStd.ImTextFindPreviousUtf8Codepoint(text, in_text_start, in_p)
while in_p > in_text_start do
in_p = in_p - 1
if bit32.band(ImStrByte(text, in_p), 0xC0) ~= 0x80 then
return in_p
end
end
return in_text_start
end
--- @param text ImString
--- @param in_text_start int
--- @param in_text_end int
--- @param in_p int
--- @return int
function ImStd.ImTextFindValidUtf8CodepointEnd(text, in_text_start, in_text_end, in_p)
if in_text_start == in_p then
return in_text_start
end
local prev = ImStd.ImTextFindPreviousUtf8Codepoint(text, in_text_start, in_p)
local prev_c_len, prev_c = ImStd.ImTextCharFromUtf8(text, prev, in_text_end)
-- Check if the previous character is valid and fits within the range
if prev_c ~= 65533 --[[IM_UNICODE_CODEPOINT_INVALID]] and prev_c_len <= (in_p - prev) then
return in_p
end
return prev
end
-------------------------------------------------------------------------------
-- [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
-------------------------------------------------------------------------------
--- @param p1 ImVec2
--- @param p2 ImVec2
--- @param p3 ImVec2
--- @param p4 ImVec2
--- @param p ImVec2
--- @param num_segments int
--- @return ImVec2
--- @nodiscard
function ImBezierCubicClosestPoint(p1, p2, p3, p4, p, num_segments)
assert(num_segments > 0) -- Use ImBezierCubicClosestPointCasteljau()
local p_last = p1
local p_closest = ImVec2(0, 0)
local p_closest_dist2 = 3.4028234663853e+38 --[[FLT_MAX]]
local t_step = 1.0 / num_segments
for i_step = 1, num_segments do
local p_current = ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step)
local p_line = ImStd.ImLineClosestPoint(p_last, p_current, p)
local dist2 = ImLengthSqr(p - p_line)
if dist2 < p_closest_dist2 then
p_closest = p_line
p_closest_dist2 = dist2
end
p_last = p_current
end
return ImVec2(p_closest.x, p_closest.y)
end
-- Closely mimics PathBezierToCasteljau() in imgui_draw.cpp
-- [Luau] p_closest / p_last / p_closest_dist2 are C++ references: kept in the `st` table.
local function ImBezierCubicClosestPointCasteljauStep(p, st, x1, y1, x2, y2, x3, y3, x4, y4, max_error_sqr, level)
local dx = x4 - x1
local dy = y4 - y1
local d2 = ((x2 - x4) * dy - (y2 - y4) * dx)
local d3 = ((x3 - x4) * dy - (y3 - y4) * dx)
d2 = (d2 >= 0) and d2 or -d2
d3 = (d3 >= 0) and d3 or -d3
if (d2 + d3) * (d2 + d3) < max_error_sqr * (dx * dx + dy * dy) then
local p_current = ImVec2(x4, y4)
local p_line = ImStd.ImLineClosestPoint(st.p_last, p_current, p)
local dist2 = ImLengthSqr(p - p_line)
if dist2 < st.p_closest_dist2 then
st.p_closest = p_line
st.p_closest_dist2 = dist2
end
st.p_last = p_current
elseif level < 10 then
local x12, y12 = (x1 + x2) * 0.5, (y1 + y2) * 0.5
local x23, y23 = (x2 + x3) * 0.5, (y2 + y3) * 0.5
local x34, y34 = (x3 + x4) * 0.5, (y3 + y4) * 0.5
local x123, y123 = (x12 + x23) * 0.5, (y12 + y23) * 0.5
local x234, y234 = (x23 + x34) * 0.5, (y23 + y34) * 0.5
local x1234, y1234 = (x123 + x234) * 0.5, (y123 + y234) * 0.5
ImBezierCubicClosestPointCasteljauStep(p, st, x1, y1, x12, y12, x123, y123, x1234, y1234, max_error_sqr, level + 1)
ImBezierCubicClosestPointCasteljauStep(p, st, x1234, y1234, x234, y234, x34, y34, x4, y4, max_error_sqr, level + 1)
end
end
-- tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationMaxError
-- Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically.
--- @return ImVec2
--- @nodiscard
function ImBezierCubicClosestPointCasteljau(p1, p2, p3, p4, p, max_error)
assert(max_error > 0.0)
local st = { p_last = p1, p_closest = ImVec2(0, 0), p_closest_dist2 = 3.4028234663853e+38 --[[FLT_MAX]] }
local max_error_sqr = max_error * max_error
ImBezierCubicClosestPointCasteljauStep(p, st, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, max_error_sqr, 0)
return ImVec2(st.p_closest.x, st.p_closest.y)
end
--- @param a ImVec2
--- @param b ImVec2
--- @param p ImVec2
--- @return ImVec2
--- @nodiscard
function ImStd.ImLineClosestPoint(a, b, p)
local ap = p - a
local ab_dir = b - a
local dot = ap.x * ab_dir.x + ap.y * ab_dir.y
if dot < 0.0 then
return a
end
local ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y
if dot > ab_len_sqr then
return b
end
return a + ab_dir * (dot / ab_len_sqr)
end
--- @param a ImVec2
--- @param b ImVec2
--- @param c ImVec2
--- @param p ImVec2
function ImStd.ImTriangleContainsPoint(a, b, c, p)
local b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0
local b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0
local b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0
return (b1 == b2) and (b2 == b3)
end
--- @param a ImVec2
--- @param b ImVec2
--- @param c ImVec2
--- @param p ImVec2
--- @return float, float, float
function ImStd.ImTriangleBarycentricCoords(a, b, c, p)
local v0 = b - a
local v1 = c - a
local v2 = p - a
local denom = v0.x * v1.y - v1.x * v0.y
local out_v = (v2.x * v1.y - v1.x * v2.y) / denom
local out_w = (v0.x * v2.y - v2.x * v0.y) / denom
local out_u = 1.0 - out_v - out_w
return out_u, out_v, out_w
end
--- @param a ImVec2
--- @param b ImVec2
--- @param c ImVec2
--- @param p ImVec2
--- @return ImVec2
--- @nodiscard
function ImStd.ImTriangleClosestPoint(a, b, c, p)
local proj_ab = ImStd.ImLineClosestPoint(a, b, p)
local proj_bc = ImStd.ImLineClosestPoint(b, c, p)
local proj_ca = ImStd.ImLineClosestPoint(c, a, p)
local dist2_ab = ImLengthSqr(p - proj_ab)
local dist2_bc = ImLengthSqr(p - proj_bc)
local dist2_ca = ImLengthSqr(p - proj_ca)
local m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca))
if m == dist2_ab then
return proj_ab
elseif m == dist2_bc then
return proj_bc
else
return proj_ca
end
end
--- @param dst char[]
--- @param dst_pos int
--- @param src char[]
--- @param src_pos int
--- @param count size_t
function ImStd.ImStrncpy(dst, dst_pos, src, src_pos, count)
if count < 1 then
return
end
if count > 1 then
ImStd.memmove(dst, dst_pos, src, src_pos, count - 1)
end
dst[dst_pos + (count - 1)] = 0
end
-- find beginning-of-line
--- @param str char[]
--- @param mid_line int
--- @param begin int
function ImStd.ImStrbol(str, mid_line, begin)
IM_ASSERT_PARANOID(mid_line >= begin and mid_line <= #str)
while mid_line > begin and str[mid_line - 1] ~= 10 do
mid_line = mid_line - 1
end
return mid_line
end
-- Trim str by offsetting contents when there's leading data + writing a \0 at the trailing position. We use this in situation where the cost is negligible.
--- @param buf char[]
function ImStd.ImStrTrimBlanks(buf)
local p = 1 -- buf_begin
while buf[p] == 32 or buf[p] == 9 do -- Leading blanks
p = p + 1
end
local p_start = p
while buf[p] ~= 0 do -- Find end of string
p = p + 1
end
while p > p_start and (buf[p - 1] == 32 or buf[p - 1] == 9) do -- Trailing blanks
p = p - 1
end
if p_start > 1 then -- Copy memory if we had leading blanks
ImStd.memmove(buf, 1, buf, p_start, p - p_start)
end
buf[p - p_start + 1] = 0 -- Zero terminate
end
-------------------------------------------------------------------------------
-- [SECTION] FONTS, TEXTURES
-------------------------------------------------------------------------------
-- Most of the relevant font logic is in imgui_draw.cpp.
-- Those are high-level support functions.
-------------------------------------------------------------------------------
-- - UpdateTexturesNewFrame() [Internal]
-- - UpdateTexturesEndFrame() [Internal]
-- - UpdateFontsNewFrame() [Internal]
-- - UpdateFontsEndFrame() [Internal]
-- - GetDefaultFont() [Internal]
-- - RegisterFontAtlas() [Internal]
-- - UnregisterFontAtlas() [Internal]
-- - SetCurrentFont() [Internal]
-- - UpdateCurrentFontSize() [Internal]
-- - PushFont()
-- - PopFont()
-------------------------------------------------------------------------------
function ImGui.UpdateCurrentFontSize(restore_font_size_after_scaling)
local g = GImGui
local window = g.CurrentWindow
g.Style.FontSizeBase = g.FontSizeBase
-- Early out to avoid hidden window keeping bakes referenced and out of GC reach.
-- - However this leave a pretty subtle and damning error surface area if g.FontBaked was mismatching.
-- Probably needs to be reevaluated into e.g. setting g.FontBaked = nil to mark it as dirty.
-- - Note that 'PushFont(); Begin(); End(); PopFont()' from within any collapsed window is not compromised, because Begin() calls SetCurrentWindow()->...->UpdateCurrentSize()
-- if (window ~= nil and window.SkipItems) then
-- local table = g.CurrentTable
-- if (table == nil or (table.CurrentColumn ~= -1 and table.Columns[table.CurrentColumn].IsSkipItems == false)) then
-- return
-- end
-- end
-- Restoring is pretty much only used by PopFont()
local final_size = (restore_font_size_after_scaling > 0.0) and restore_font_size_after_scaling or 0.0
if final_size == 0.0 then
final_size = g.FontSizeBase
-- Global scale factors
final_size = final_size * g.Style.FontScaleMain -- Main global scale factor
final_size = final_size * g.Style.FontScaleDpi -- Per-monitor/viewport DPI scale factor (in docking branch: automatically updated when io.ConfigDpiScaleFonts is enabled).
-- Window scale (mostly obsolete now)
if window ~= nil then
final_size = final_size * window.FontWindowScale
end
end
-- Round font size
-- - We started rounding in 1.90 WIP (18991) as our layout system currently doesn't support non-rounded font size well yet.
-- - We may support it better later and remove this rounding.
final_size = ImGui.GetRoundedFontSize(final_size)
final_size = ImClamp(final_size, 4.0, 512 --[[IMGUI_FONT_SIZE_MAX]])
if (g.Font ~= nil and bit32.band(g.IO.BackendFlags, 16 --[[ImGuiBackendFlags.RendererHasTextures]]) ~= 0) then
g.Font.CurrentRasterizerDensity = g.CurrentPixelDensity
end
g.FontSize = final_size
g.FontBaked = (g.Font ~= nil and window ~= nil) and g.Font:GetFontBaked(final_size) or nil
g.FontBakedScale = (g.FontBaked ~= nil) and (g.FontSize / g.FontBaked.Size) or 0.0
g.DrawListSharedData.FontSize = g.FontSize
g.DrawListSharedData.FontScale = g.FontBakedScale
end
-- Exposed in case user may want to override setting density.
-- IMPORTANT: Begin()/End() is overriding density. Be considerate of this you change it.
--- @param pixel_density float
function ImGui.SetPixelDensity(pixel_density)
local g = GImGui
assert(bit32.band(g.IO.BackendFlags, 16 --[[ImGuiBackendFlags.RendererHasTextures]]) ~= 0)
if g.CurrentPixelDensity == pixel_density then
return
end
g.CurrentPixelDensity = pixel_density
ImGui.UpdateCurrentFontSize(0.0)
end
-- Use ImDrawList:_SetTexture(), making our shared g.FontStack[] authoritative against window-local ImDrawList.
-- - Whereas ImDrawList:PushTexture()/PopTexture() is not to be used across Begin() calls.
-- - Note that we don't propagate current texture id when e.g. Begin()-ing into a new window, we never really did...
-- - Some code paths never really fully worked with multiple atlas textures.
-- - The right-ish solution may be to remove _SetTexture() and make AddText/RenderText lazily call PushTexture()/PopTexture()
-- the same way AddImage() does, but then all other primitives would also need to? I don't think we should tackle this problem
-- because we have a concrete need and a test bed for multiple atlas textures.
-- FIXME-NEWATLAS-V2: perhaps we can now leverage ImFontAtlasUpdateDrawListsTextures() ?
function ImGui.SetCurrentFont(font, font_size_before_scaling, font_size_after_scaling)
local g = GImGui
g.Font = font
g.FontSizeBase = font_size_before_scaling
ImGui.UpdateCurrentFontSize(font_size_after_scaling)
if font ~= nil then
assert(font and font:IsLoaded()) -- Font Atlas not created. Did you call io.Fonts:GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
local atlas = font.OwnerAtlas
g.DrawListSharedData.FontAtlas = atlas
g.DrawListSharedData.Font = font
ImFontAtlasUpdateDrawListsSharedData(atlas)
if (g.CurrentWindow ~= nil) then
g.CurrentWindow.DrawList:_SetTexture(atlas.TexRef)
end
end
end
-- If you want to scale an existing font size! Read comments in imgui.h!
function ImGui.PushFont(font, font_size_base)
local g = GImGui
if font == nil then -- Before 1.92 (June 2025), PushFont(nil) == PushFont(GetDefaultFont())
font = g.Font
end
assert(font ~= nil)
assert(font_size_base >= 0.0)
g.FontStack:push_back(ImFontStackData(font, g.FontSizeBase, g.FontSize))
if font_size_base == 0.0 then
font_size_base = g.FontSizeBase -- Keep current font size
end
ImGui.SetCurrentFont(font, font_size_base, 0.0)
end
function ImGui.PopFont()
local g = GImGui
if (g.FontStack.Size <= 0) then
IM_ASSERT_USER_ERROR(0, "Calling PopFont() too many times!")
return
end
local font_stack_data = g.FontStack:back()
ImGui.SetCurrentFont(font_stack_data.Font, font_stack_data.FontSizeBeforeScaling, font_stack_data.FontSizeAfterScaling)
g.FontStack:pop_back()
end
function ImGui.UpdateTexturesNewFrame()
-- Cannot update every atlases based on atlas's FrameCount < g.FrameCount, because an atlas may be shared by multiple contexts with different frame count.
local g = GImGui
local has_textures = bit32.band(g.IO.BackendFlags, 16 --[[ImGuiBackendFlags.RendererHasTextures]]) ~= 0
for _, atlas in g.FontAtlases:iter() do
if (atlas.OwnerContext == g) then
ImFontAtlasUpdateNewFrame(atlas, g.FrameCount, has_textures)
else
-- (1) If you manage font atlases yourself, e.g. create a ImFontAtlas yourself you need to call ImFontAtlasUpdateNewFrame() on it.
-- Otherwise, calling ImGui.CreateContext() without parameter will create an atlas owned by the context.
-- (2) If you have multiple font atlases, make sure the 'atlas.RendererHasTextures' as specified in the ImFontAtlasUpdateNewFrame() call matches for that.
-- (3) If you have multiple imgui contexts, they also need to have a matching value for ImGuiBackendFlags.RendererHasTextures.
assert(atlas.Builder ~= nil and atlas.Builder.FrameCount ~= -1)
assert(atlas.RendererHasTextures == has_textures)
end
end
for _, tex in g.UserTextures:iter() do
ImTextureDataUpdateNewFrame(tex)
end
end
-- Build a single texture list
function ImGui.UpdateTexturesEndFrame()
local g = GImGui
g.PlatformIO.Textures:resize(0)
for _, atlas in g.FontAtlases:iter() do
for _, tex in atlas.TexList:iter() do
-- We provide this information so backends can decide whether to destroy textures.
-- This means in practice that if N imgui contexts are created with a shared atlas, we assume all of them have a backend initialized.
tex.RefCount = atlas.RefCount
g.PlatformIO.Textures:push_back(tex)
end
end
for _, tex in g.UserTextures:iter() do
g.PlatformIO.Textures:push_back(tex)
end
end
function ImGui.UpdateFontsNewFrame()
local g = GImGui
if (bit32.band(g.IO.BackendFlags, 16 --[[ImGuiBackendFlags.RendererHasTextures]]) == 0) then
for _, atlas in g.FontAtlases:iter() do
atlas.Locked = true
end
end
if (g.Style._NextFrameFontSizeBase ~= 0.0) then
g.Style.FontSizeBase = g.Style._NextFrameFontSizeBase
g.Style._NextFrameFontSizeBase = 0.0
end
-- Apply default font size the first time
local font = ImGui.GetDefaultFont()
if g.Style.FontSizeBase <= 0.0 then
g.Style.FontSizeBase = ((font.LegacySize > 0.0) and font.LegacySize or FONT_DEFAULT_SIZE_BASE)
end
-- Set initial font
g.Font = font
g.FontSizeBase = g.Style.FontSizeBase
g.FontSize = 0.0
local font_stack_data = ImFontStackData(font, g.Style.FontSizeBase, g.Style.FontSizeBase) -- <--- Will restore FontSize
ImGui.SetCurrentFont(font_stack_data.Font, font_stack_data.FontSizeBeforeScaling, 0.0) -- <--- but use 0.0f to enable scale
g.FontStack:push_back(font_stack_data)
assert(g.Font:IsLoaded())
end
function ImGui.UpdateFontsEndFrame()
ImGui.PopFont()
end
--- @return ImFont
function ImGui.GetDefaultFont()
local g = GImGui
local atlas = g.IO.Fonts
if (atlas.Builder == nil or atlas.Fonts.Size == 0) then
ImFontAtlasBuildMain(atlas)
end
return g.IO.FontDefault and g.IO.FontDefault or atlas.Fonts[1]
end
-- EXPERIMENTAL. Use ImTextureDataQueueUpload() to queue updates. Textures logic will be automatically be updated in NewFrame().
--- @param tex ImTextureData
function ImGui.RegisterUserTexture(tex)
local g = GImGui
tex.RefCount = tex.RefCount + 1
g.UserTextures:push_back(tex)
end
--- @param tex ImTextureData
function ImGui.UnregisterUserTexture(tex)
local g = GImGui
assert(tex.RefCount > 0)
tex.RefCount = tex.RefCount - 1
g.UserTextures:find_erase(tex)
end
--- @param atlas ImFontAtlas
function ImGui.RegisterFontAtlas(atlas)
local g = GImGui
if (g.FontAtlases.Size == 0) then
assert(atlas == g.IO.Fonts)
end
atlas.RefCount = atlas.RefCount + 1
g.FontAtlases:push_back(atlas)
ImFontAtlasAddDrawListSharedData(atlas, g.DrawListSharedData)
for _, tex in atlas.TexList:iter() do
tex.RefCount = atlas.RefCount
end
end
--- @param atlas ImFontAtlas
function ImGui.UnregisterFontAtlas(atlas)
local g = GImGui
assert(atlas.RefCount > 0)
ImFontAtlasRemoveDrawListSharedData(atlas, g.DrawListSharedData)
g.FontAtlases:find_erase(atlas)
atlas.RefCount = atlas.RefCount - 1
for _, tex in atlas.TexList:iter() do
tex.RefCount = atlas.RefCount
end
end
-------------------------------------------------------------------------------
-- [SECTION] INITIALIZATION, SHUTDOWN
-------------------------------------------------------------------------------
-- Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
function ImGui.GetCurrentContext()
return GImGui
end
--- @param ctx ImGuiContext?
function ImGui.SetCurrentContext(ctx)
GImGui = ctx
ImGui._SetCurrentContext_Internal(ctx)
ImGui._SetCurrentContext_Widgets(ctx)
ImGui._SetCurrentContext_Tables(ctx)
end
--- @param alloc_func ImGuiMemAllocFunc
--- @param free_func ImGuiMemFreeFunc
--- @param user_data any
function ImGui.SetAllocatorFunctions(alloc_func, free_func, user_data)
GImAllocatorAllocFunc = alloc_func
GImAllocatorFreeFunc = free_func
GImAllocatorUserData = user_data
end
--- @param key ImGuiLocKey
--- @param text string
--- @return ImGuiLocEntry
--- @nodiscard
--- @package
local function ImGuiLocEntry(key, text)
return { Key = key, Text = text }
end
-- IMPORTANT: interactive elements requires a fixed ###xxx suffix, it must be same in ALL languages to allow for automation.
local GLocalizationEntriesEnUS = {
ImGuiLocEntry(ImGuiLocKey.VersionStr, "Dear ImGui WIP"),
ImGuiLocEntry(ImGuiLocKey.TableSizeOne, "Size column to fit###SizeOne"),
ImGuiLocEntry(ImGuiLocKey.TableSizeAllFit, "Size all columns to fit###SizeAll"),
ImGuiLocEntry(ImGuiLocKey.TableSizeAllDefault, "Size all columns to default###SizeAll"),
ImGuiLocEntry(ImGuiLocKey.TableReset, "Reset"),
--ImGuiLocEntry(ImGuiLocKey.TableResetAll, "Reset to default###ResetAll"),
ImGuiLocEntry(ImGuiLocKey.TableResetOrder, "Reset order###ResetOrder"),
ImGuiLocEntry(ImGuiLocKey.TableResetVisibility, "Reset visibility###ResetVisibility"),
ImGuiLocEntry(ImGuiLocKey.WindowingMainMenuBar, "(Main menu bar)"),
ImGuiLocEntry(ImGuiLocKey.WindowingPopup, "(Popup)"),
ImGuiLocEntry(ImGuiLocKey.WindowingUntitled, "(Untitled)"),
ImGuiLocEntry(ImGuiLocKey.OpenLink_s, "Open '%s'"),
ImGuiLocEntry(ImGuiLocKey.CopyLink, "Copy Link###CopyLink"),
ImGuiLocEntry(ImGuiLocKey.DockingHideTabBar, "Hide tab bar###HideTabBar"),
ImGuiLocEntry(ImGuiLocKey.DockingHoldShiftToDock, "Hold SHIFT to enable Docking window."),
ImGuiLocEntry(ImGuiLocKey.DockingDragToUndockOrMoveNode, "Click and drag to move or undock whole node."),
}
-- [Luau] Default platform handlers (upstream: next to the metrics section; defined here so Initialize() can see the local).
-- Roblox has no shell: IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS path. Default clipboard/IME handlers are OS-specific: left nil
-- (the backends install their own clipboard handlers).
local function Platform_OpenInShellFn_DefaultImpl(ctx, path) return false end
-- Setup default ImGuiPlatformIO clipboard/IME handlers
local function InitializeDefaultPlatformHandlers(platform_io)
platform_io.Platform_OpenInShellFn = Platform_OpenInShellFn_DefaultImpl
end
function ImGui.Initialize()
local g = GImGui
assert(not g.Initialized and not g.SettingsLoaded)
-- Add .ini handle for ImGuiWindow and ImGuiTable types
ImGui.WindowSettingsAddSettingsHandler()
if ImGui.TableSettingsAddSettingsHandler then ImGui.TableSettingsAddSettingsHandler() end
-- Initialize Docking
ImGui.DockContextInitialize(g)
-- Setup default localization table
ImGui.LocalizeRegisterEntries(GLocalizationEntriesEnUS, #GLocalizationEntriesEnUS)
-- Setup default ImGuiPlatformIO clipboard/IME handlers.
InitializeDefaultPlatformHandlers(g.PlatformIO)
-- Setup session starting date
-- [Luau] os.date() exists in Roblox Luau and Lua 5.1/LuaJIT
if not IMGUI_DISABLE_TIME_FUNCTIONS and os ~= nil and os.date ~= nil then
local session_datetime = os.date("*t")
if type(session_datetime) == "table" then
g.PlatformIO.Platform_SessionDate = session_datetime.year * 10000 + session_datetime.month * 100 + session_datetime.day
end
end
-- Create default viewport
local viewport = ImGuiViewportP()
viewport.ID = IMGUI_VIEWPORT_DEFAULT_ID
viewport.Idx = 1
viewport.PlatformWindowCreated = true
viewport.Flags = 4 --[[ImGuiViewportFlags.OwnedByApp]]
g.Viewports:push_back(viewport)
g.ViewportCreatedCount = g.ViewportCreatedCount + 1
g.PlatformIO.Viewports:push_back(g.Viewports.Data[1])
-- Build KeysMayBeCharInput[] lookup table (1 bit per named key)
for key = 512 --[[ImGuiKey.NamedKey_BEGIN]], 667 --[[ImGuiKey.NamedKey_END]] - 1 do
if (key >= 536 --[[ImGuiKey.K0]] and key <= 545 --[[ImGuiKey.K9]]) or (key >= 546 --[[ImGuiKey.A]] and key <= 571 --[[ImGuiKey.Z]]) or (key >= 612 --[[ImGuiKey.Keypad0]] and key <= 621 --[[ImGuiKey.Keypad9]])
or key == 512 --[[ImGuiKey.Tab]] or key == 524 --[[ImGuiKey.Space]] or key == 596 --[[ImGuiKey.Apostrophe]] or key == 597 --[[ImGuiKey.Comma]] or key == 598 --[[ImGuiKey.Minus]] or key == 599 --[[ImGuiKey.Period]]
or key == 600 --[[ImGuiKey.Slash]] or key == 601 --[[ImGuiKey.Semicolon]] or key == 602 --[[ImGuiKey.Equal]] or key == 603 --[[ImGuiKey.LeftBracket]] or key == 605 --[[ImGuiKey.RightBracket]] or key == 606 --[[ImGuiKey.GraveAccent]]
or key == 622 --[[ImGuiKey.KeypadDecimal]] or key == 623 --[[ImGuiKey.KeypadDivide]] or key == 624 --[[ImGuiKey.KeypadMultiply]] or key == 625 --[[ImGuiKey.KeypadSubtract]] or key == 626 --[[ImGuiKey.KeypadAdd]] or key == 628 --[[ImGuiKey.KeypadEqual]] then
g.KeysMayBeCharInput:SetBit(key + 1)
end
end
-- ImDrawList/ImFontAtlas are designed to function without ImGui, and 99% of it works without an ImGui context.
-- But this link allows us to facilitate/handle a few edge cases better.
local atlas = g.IO.Fonts
g.DrawListSharedData.Context = g
ImGui.RegisterFontAtlas(atlas)
g.Initialized = true
end
--- @param shared_font_atlas? ImFontAtlas
function ImGui.CreateContext(shared_font_atlas)
local prev_ctx = ImGui.GetCurrentContext()
local ctx = ImGuiContext(shared_font_atlas)
ImGui.SetCurrentContext(ctx)
ImGui.Initialize()
if prev_ctx ~= nil then
ImGui.SetCurrentContext(prev_ctx) -- Restore previous context if any, else keep new one
end
return ctx
end
--- @param ctx? ImGuiContext
function ImGui.DestroyContext(ctx)
local prev_ctx = ImGui.GetCurrentContext()
if ctx == nil then
ctx = prev_ctx
end
ImGui.SetCurrentContext(ctx)
ImGui.Shutdown()
ImGui.SetCurrentContext((prev_ctx ~= ctx) and prev_ctx or nil)
end
-- This is faster if you are holding on a Window already as we don't need to perform a search.
--- @param window ImGuiWindow
--- @return any
function ImGui.FindWindowSettingsByWindow(window)
local g = GImGui
if window.SettingsOffset ~= -1 then
return g.SettingsWindows[window.SettingsOffset + 1]
end
return ImGui.FindWindowSettingsByID(window.ID)
end
-- We don't provide a FindWindowSettingsByName() because Docking system doesn't always hold on names.
-- This is called once per window .ini entry + once per newly instantiated window.
--- @param id ImGuiID
--- @return any
function ImGui.FindWindowSettingsByID(id)
local g = GImGui
for _, settings in g.SettingsWindows:iter() do
if settings.ID == id and not settings.WantDelete then
return settings
end
end
return nil
end
--- @param window ImGuiWindow
--- @return ImGuiWindow?
function ImGui.FindFrontMostVisibleChildWindow(window)
for n = window.DC.ChildWindows.Size, 1, -1 do
if ImGui.IsWindowActiveAndVisible(window.DC.ChildWindows.Data[n]) then
return ImGui.FindFrontMostVisibleChildWindow(window.DC.ChildWindows.Data[n])
end
end
return window
end
--- @param window ImGuiWindow
--- @param cond ImGuiCond
--- @param allow bool
local function SetWindowConditionAllowFlags(window, cond, allow)
if allow then
window.SetWindowPosAllowFlags = bit32.bor(window.SetWindowPosAllowFlags, cond)
window.SetWindowSizeAllowFlags = bit32.bor(window.SetWindowSizeAllowFlags, cond)
window.SetWindowCollapsedAllowFlags = bit32.bor(window.SetWindowCollapsedAllowFlags, cond)
else
window.SetWindowPosAllowFlags = bit32.band(window.SetWindowPosAllowFlags, bit32.bnot(cond))
window.SetWindowSizeAllowFlags = bit32.band(window.SetWindowSizeAllowFlags, bit32.bnot(cond))
window.SetWindowCollapsedAllowFlags = bit32.band(window.SetWindowCollapsedAllowFlags, bit32.bnot(cond))
end
end
--- @param window ImGuiWindow
--- @param settings ImGuiWindowSettings
local function ApplyWindowSettings(window, settings)
if settings ~= nil then
local main_viewport = ImGui.GetMainViewport()
ImVec2_Copy(window.ViewportPos, main_viewport.Pos)
if (settings.ViewportId ~= 0) then
window.ViewportId = settings.ViewportId
ImVec2_Copy(window.ViewportPos, settings.ViewportPos)
end
window.Pos = ImTrunc(ImVec2(settings.Pos.x + window.ViewportPos.x, settings.Pos.y + window.ViewportPos.y))
if settings.Size.x > 0 and settings.Size.y > 0 then
local size = ImVec2(ImTrunc(settings.Size.x), ImTrunc(settings.Size.y))
ImVec2_Copy(window.Size, size)
ImVec2_Copy(window.SizeFull, size)
window.AutoFitFramesX = 0; window.AutoFitFramesY = 0
end
window.Collapsed = settings.Collapsed
window.DockId = settings.DockId or 0
window.DockOrder = settings.DockOrder or -1
SetWindowConditionAllowFlags(window, 4 --[[ImGuiCond.FirstUseEver]], false)
end
-- So first call to CalcWindowContentSizes() doesn't return crazy values
ImVec2_Copy(window.DC.CursorStartPos, window.Pos)
ImVec2_Copy(window.DC.CursorMaxPos, window.Pos)
ImVec2_Copy(window.DC.IdealMaxPos, window.Pos)
if (bit32.band(window.Flags, 64 --[[ImGuiWindowFlags.AlwaysAutoResize]]) ~= 0) then
window.AutoFitFramesX = 2; window.AutoFitFramesY = 2
window.AutoFitOnlyGrows = false
else
window.AutoFitFramesX = (window.Size.x <= 0.0) and 2 or 0
window.AutoFitFramesY = (window.Size.y <= 0.0) and 2 or 0
window.AutoFitOnlyGrows = (window.AutoFitFramesX > 0) or (window.AutoFitFramesY > 0)
end
end
-- Note that 'settings' may be nil.
-- Sets _Once, _Appearing, _FirstUseEver. _FirstUseEver will be cleared again if there are settings.
--- @param window ImGuiWindow
--- @param settings ImGuiWindowSettings
local function InitOrLoadWindowSettings(window, settings)
-- Initial window state with e.g. default/arbitrary window position
-- Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
local main_viewport = ImGui.GetMainViewport()
ImVec2_Copy(window.Pos, main_viewport.Pos + ImVec2(60, 60))
ImVec2_Copy(window.Size, ImVec2(0, 0))
ImVec2_Copy(window.SizeFull, ImVec2(0, 0))
ImVec2_Copy(window.ViewportPos, main_viewport.Pos)
window.SetWindowPosAllowFlags = bit32.bor(1 --[[ImGuiCond.Always]], 2 --[[ImGuiCond.Once]], 4 --[[ImGuiCond.FirstUseEver]], 8 --[[ImGuiCond.Appearing]])
window.SetWindowSizeAllowFlags = window.SetWindowPosAllowFlags
window.SetWindowCollapsedAllowFlags = window.SetWindowPosAllowFlags
window.SetWindowDockAllowFlags = window.SetWindowPosAllowFlags
ApplyWindowSettings(window, settings)
if settings ~= nil then
settings.LastUsedDate = GImGui.SessionDate:Unpack()
end
end
--- @param name string
--- @param flags ImGuiWindowFlags
--- @return ImGuiWindow
local function CreateNewWindow(name, flags)
-- Create window the first time
--IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags)
local g = GImGui
local window_id = ImHashStr(name)
local window = ImGuiWindow(g, name)
window.ID = window_id
window.Flags = flags
g.WindowsById[window_id] = window
local settings = nil
if bit32.band(window.Flags, 256 --[[ImGuiWindowFlags.NoSavedSettings]]) == 0 then
settings = ImGui.FindWindowSettingsByWindow(window)
if settings ~= nil then
window.SettingsOffset = g.SettingsWindows:index_from_ptr(settings)
end
end
InitOrLoadWindowSettings(window, settings)
if bit32.band(flags, 8192 --[[ImGuiWindowFlags.NoBringToFrontOnFocus]]) ~= 0 then
g.Windows:push_front(window) -- Quite slow but rare and only once
else
g.Windows:push_back(window)
end
return window
end
-------------------------------------------------------------------------------
-- [SECTION] SETTINGS
-- (in-memory .ini; Roblox has no disk)
-------------------------------------------------------------------------------
-- - UpdateSettings() [Internal]
-- - MarkIniSettingsDirty() [Internal]
-- - FindSettingsHandler() [Internal]
-- - ClearIniSettings() [Internal]
-- - LoadIniSettingsFromDisk()
-- - LoadIniSettingsFromMemory()
-- - SaveIniSettingsToDisk()
-- - SaveIniSettingsToMemory()
-------------------------------------------------------------------------------
-- - CreateNewWindowSettings() [Internal]
-- - FindWindowSettingsByID() [Internal]
-- - FindWindowSettingsByWindow() [Internal]
-- - ClearWindowSettings() [Internal]
-- - WindowSettingsHandler_***() [Internal]
-------------------------------------------------------------------------------
-- Called by NewFrame()
function ImGui.UpdateSettings()
-- Load settings on first frame (if not explicitly loaded manually before)
local g = GImGui
if not g.SettingsLoaded then
if g.IO.IniFilename then ImGui.LoadIniSettingsFromDisk(g.IO.IniFilename) end
g.SettingsLoaded = true
end
-- Save settings (with a delay after the last modification, so we don't spam disk too much)
if g.SettingsDirtyTimer > 0.0 then
g.SettingsDirtyTimer = g.SettingsDirtyTimer - g.IO.DeltaTime
if g.SettingsDirtyTimer <= 0.0 then
-- No disk: always let the user know they can call SaveIniSettingsToMemory()
g.IO.WantSaveIniSettings = true -- Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
g.SettingsDirtyTimer = 0.0
end
end
end
--- @param window? ImGuiWindow
function ImGui.MarkIniSettingsDirty(window)
local g = GImGui
if window ~= nil and bit32.band(window.Flags, 256 --[[ImGuiWindowFlags.NoSavedSettings]]) ~= 0 then return end
if g.SettingsDirtyTimer <= 0.0 then
g.SettingsDirtyTimer = g.IO.IniSavingRate
end
end
--- @param handler ImGuiSettingsHandler
function ImGui.AddSettingsHandler(handler)
local g = GImGui
assert(ImGui.FindSettingsHandler(handler.TypeName) == nil)
if handler.TypeHash == 0 or handler.TypeHash == nil then handler.TypeHash = ImHashStr(handler.TypeName) end
g.SettingsHandlers:push_back(handler)
end
function ImGui.RemoveSettingsHandler(type_name)
local g = GImGui
local handler = ImGui.FindSettingsHandler(type_name)
if handler then g.SettingsHandlers:find_erase(handler) end
end
function ImGui.FindSettingsHandler(type_name)
local g = GImGui
local type_hash = ImHashStr(type_name)
for _, handler in g.SettingsHandlers:iter() do
if handler.TypeHash == type_hash then return handler end
end
return nil
end
-- Clear all settings (windows, tables, docking etc.)
function ImGui.ClearIniSettings()
local g = GImGui
g.SettingsIniData = ""
for _, handler in g.SettingsHandlers:iter() do
if handler.ClearAllFn then handler.ClearAllFn(g, handler) end
end
end
function ImGui.CleanupIniSettings(args)
local g = GImGui
if args.DiscardAll then
for _, handler in g.SettingsHandlers:iter() do
if args.TypeHashFilter == 0 or handler.TypeHash == args.TypeHashFilter then
if handler.ClearAllFn then handler.ClearAllFn(g, handler) end
end
end
end
if g.PlatformIO.Platform_SessionDate ~= 0 and args.DiscardOlderThanMonths ~= 0 then
local discard_older_than_date_p = ImGuiPackedDate(g.PlatformIO.Platform_SessionDate)
discard_older_than_date_p:SubtractMonths(args.DiscardOlderThanMonths)
args._DiscardOlderThanDate = discard_older_than_date_p:Unpack()
end
for _, handler in g.SettingsHandlers:iter() do
if args.TypeHashFilter == 0 or handler.TypeHash == args.TypeHashFilter then
if handler.CleanupFn ~= nil then handler.CleanupFn(g, handler, args) end
end
end
end
function ImGui.LoadIniSettingsFromDisk(ini_filename)
local data = ImStd.ImFileLoadToMemory and ImStd.ImFileLoadToMemory(ini_filename, "rb")
if not data or #data == 0 then return end
local parts = {}
for i = 1, #data do parts[i] = string.char(data[i]) end
ImGui.LoadIniSettingsFromMemory(table.concat(parts))
end
-- Zero-tolerance, no error reporting, cheap .ini parsing
-- Set ini_size==0 (or nil) to use the whole string (Lua strings know their length).
function ImGui.LoadIniSettingsFromMemory(ini_data, ini_size)
local g = GImGui
assert(g.Initialized)
--IM_ASSERT(not g.WithinFrameScope, "Cannot be called between NewFrame() and EndFrame()")
--IM_ASSERT(g.SettingsLoaded == false and g.FrameCount == 0)
-- For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
if ini_size and ini_size > 0 then ini_data = ini_data:sub(1, ini_size) end
g.SettingsIniData = ini_data
-- Call pre-read handlers
-- Some types will clear their data (e.g. dock information) some types will allow merge/override (window)
for _, handler in g.SettingsHandlers:iter() do
if handler.ReadInitFn then handler.ReadInitFn(g, handler) end
end
local entry_data, entry_handler = nil, nil
for line in ini_data:gmatch("[^\r\n]+") do
local c = line:sub(1, 1)
if c == ";" then
-- comment
elseif c == "[" and line:sub(-1) == "]" then
-- Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
local type_name, name = line:match("^%[([^%]]*)%]%[(.*)%]$")
if type_name then
entry_handler = ImGui.FindSettingsHandler(type_name)
entry_data = entry_handler and entry_handler.ReadOpenFn(g, entry_handler, name) or nil
else
entry_handler, entry_data = nil, nil
end
elseif entry_handler ~= nil and entry_data ~= nil then
-- Let type handler parse the line
entry_handler.ReadLineFn(g, entry_handler, entry_data, line)
end
end
g.SettingsLoaded = true
-- Call post-read handlers
if g.IO.ConfigIniSettingsAutoDiscardMonths > 0 then
local cleanup_args = ImGuiSettingsCleanupArgs()
cleanup_args.DiscardOlderThanMonths = g.IO.ConfigIniSettingsAutoDiscardMonths
ImGui.CleanupIniSettings(cleanup_args)
end
for _, handler in g.SettingsHandlers:iter() do
if handler.ApplyAllFn then handler.ApplyAllFn(g, handler) end
end
end
function ImGui.SaveIniSettingsToDisk(ini_filename)
local g = GImGui
g.SettingsDirtyTimer = 0.0
-- Roblox: no disk. Call SaveIniSettingsToMemory() and persist the string yourself (e.g. DataStore).
end
-- Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
--- @return string ini_data, int size
function ImGui.SaveIniSettingsToMemory()
local g = GImGui
g.SettingsDirtyTimer = 0.0
local buf = ImGuiTextBuffer()
for _, handler in g.SettingsHandlers:iter() do
handler.WriteAllFn(g, handler, buf)
end
g.SettingsIniData = buf:c_str()
return g.SettingsIniData, #g.SettingsIniData
end
function ImGui.CreateNewWindowSettings(name)
local g = GImGui
-- Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier.
if not g.IO.ConfigDebugIniSettings then
-- ImHashSkipUncontributingPrefix: skip everything up to the last "###"
local p = name:match("^.*()###")
if p then name = name:sub(p) end
end
local settings = ImGuiWindowSettings()
settings.ID = ImHashStr(name)
settings.Name = name
g.SettingsWindows:push_back(settings)
return settings
end
-- This will revert window to its initial state, including enabling the ImGuiCond.FirstUseEver/ImGuiCond.Once conditions once more.
function ImGui.ClearWindowSettings(name)
--IMGUI_DEBUG_LOG("ClearWindowSettings('%s')\n", name)
local window = ImGui.FindWindowByName(name)
if window ~= nil then
window.Flags = bit32.bor(window.Flags, 256 --[[ImGuiWindowFlags.NoSavedSettings]])
InitOrLoadWindowSettings(window, nil)
if window.DockId and window.DockId ~= 0 and ImGui.DockContextProcessUndockWindow then
ImGui.DockContextProcessUndockWindow(GImGui, window, true)
end
end
local settings = window and ImGui.FindWindowSettingsByWindow(window) or ImGui.FindWindowSettingsByID(ImHashStr(name))
if settings then settings.WantDelete = true end
end
local function WindowSettingsHandler_ClearAll(ctx, handler)
local g = ctx
for _, window in g.Windows:iter() do window.SettingsOffset = -1 end
g.SettingsWindows:clear()
end
-- [Luau] settings.LastUsedDate is stored unpacked (YYYYMMDD int, 0 = invalid): ImGuiPackedDate::IsValid() is '~= 0', Unpack() is a no-op.
local function WindowSettingsHandler_Cleanup(ctx, handler, args)
local g = ctx
for _, settings in g.SettingsWindows:iter() do
local is_valid = settings.LastUsedDate ~= 0
if (args._DiscardOlderThanDate ~= 0 and settings.LastUsedDate < args._DiscardOlderThanDate) or (args.DiscardWhenMissingDate and not is_valid) then
settings.WantDelete = true
end
if args.SetCurrentSessionDateToAll or (args.SetCurrentSessionDateWhenMissingDate and not is_valid) then
settings.LastUsedDate = g.SessionDate:Unpack()
end
end
end
local function WindowSettingsHandler_ReadOpen(ctx, handler, name)
local id = ImHashStr(name)
local settings = ImGui.FindWindowSettingsByID(id)
if settings then
-- Clear existing if recycling previous entry
local fresh = ImGuiWindowSettings()
for k, v in pairs(fresh) do settings[k] = v end
settings.Name = name
else
settings = ImGui.CreateNewWindowSettings(name)
end
settings.ID = id
settings.WantApply = true
return settings
end
local function WindowSettingsHandler_ReadLine(ctx, handler, settings, line)
local x, y = line:match("^Pos=(%-?%d+),(%-?%d+)")
if x then ImVec2_CopyV(settings.Pos, tonumber(x), tonumber(y)) return end
x, y = line:match("^Size=(%-?%d+),(%-?%d+)")
if x then ImVec2_CopyV(settings.Size, tonumber(x), tonumber(y)) return end
x = line:match("^ViewportId=0x(%x+)")
if x then settings.ViewportId = tonumber(x, 16) return end
x, y = line:match("^ViewportPos=(%-?%d+),(%-?%d+)")
if x then ImVec2_CopyV(settings.ViewportPos, tonumber(x), tonumber(y)) return end
x = line:match("^Collapsed=(%d+)")
if x then settings.Collapsed = tonumber(x) ~= 0 return end
x = line:match("^IsChild=(%d+)")
if x then settings.IsChild = tonumber(x) ~= 0 return end
x = line:match("^LastUsed=(%d+)")
if x then settings.LastUsedDate = ImGuiPackedDate(tonumber(x)):Unpack() return end -- [Luau] pack + unpack, as upstream stores an ImGuiPackedDate
x, y = line:match("^DockId=0x(%x+),(%-?%d+)")
if x then settings.DockId = tonumber(x, 16); settings.DockOrder = tonumber(y) return end
x = line:match("^DockId=0x(%x+)")
if x then settings.DockId = tonumber(x, 16); settings.DockOrder = -1 return end
x = line:match("^ClassId=0x(%x+)")
if x then settings.ClassId = tonumber(x, 16) return end
end
-- Apply to existing windows (if any)
local function WindowSettingsHandler_ApplyAll(ctx, handler)
local g = ctx
for _, settings in g.SettingsWindows:iter() do
if settings.WantApply then
local window = ImGui.FindWindowByID(settings.ID)
if window then ApplyWindowSettings(window, settings) end
settings.WantApply = false
end
end
end
local function WindowSettingsHandler_WriteAll(ctx, handler, buf)
-- Gather data from windows that were active during this session
-- (if a window wasn't opened in this session we preserve its settings)
local g = ctx
for _, window in g.Windows:iter() do
if bit32.band(window.Flags, 256 --[[ImGuiWindowFlags.NoSavedSettings]]) == 0 then
local settings = ImGui.FindWindowSettingsByWindow(window)
if not settings then
settings = ImGui.CreateNewWindowSettings(window.Name)
settings.LastUsedDate = g.SessionDate:Unpack()
window.SettingsOffset = g.SettingsWindows:index_from_ptr(settings)
end
assert(settings.ID == window.ID)
ImVec2_CopyV(settings.Pos, ImTrunc(window.Pos.x - window.ViewportPos.x), ImTrunc(window.Pos.y - window.ViewportPos.y))
ImVec2_CopyV(settings.Size, ImTrunc(window.SizeFull.x), ImTrunc(window.SizeFull.y))
settings.ViewportId = window.ViewportId or 0
ImVec2_CopyV(settings.ViewportPos, ImTrunc(window.ViewportPos.x), ImTrunc(window.ViewportPos.y))
settings.DockId = window.DockId or 0
settings.ClassId = window.WindowClass and window.WindowClass.ClassId or 0
settings.DockOrder = window.DockOrder or -1
settings.Collapsed = window.Collapsed
settings.IsChild = (window.RootWindow ~= window) -- Cannot rely on ImGuiWindowFlags.ChildWindow here as docked windows have this set.
settings.WantDelete = false
settings.LastUsedDate = g.SessionDate:Unpack()
end
end
-- Write to text buffer
for _, settings in g.SettingsWindows:iter() do
if not settings.WantDelete then
buf:appendf("[%s][%s]\n", handler.TypeName, settings.Name) -- [Window][name]
if settings.IsChild then
buf:appendf("IsChild=1\n")
buf:appendf("Size=%d,%d\n", settings.Size.x, settings.Size.y)
else
if settings.ViewportId ~= 0 and settings.ViewportId ~= IMGUI_VIEWPORT_DEFAULT_ID then
buf:appendf("ViewportPos=%d,%d\n", settings.ViewportPos.x, settings.ViewportPos.y)
buf:appendf("ViewportId=0x%08X\n", settings.ViewportId)
end
if settings.Pos.x ~= 0 or settings.Pos.y ~= 0 or settings.ViewportId == IMGUI_VIEWPORT_DEFAULT_ID then
buf:appendf("Pos=%d,%d\n", settings.Pos.x, settings.Pos.y)
end
if settings.Size.x ~= 0 or settings.Size.y ~= 0 then
buf:appendf("Size=%d,%d\n", settings.Size.x, settings.Size.y)
end
buf:appendf("Collapsed=%d\n", settings.Collapsed and 1 or 0)
if settings.DockId ~= 0 then
--buf:appendf("TabId=0x%08X\n", ImHashStr("#TAB", 4, settings.ID)) -- window.TabId: this is not read back but writing it makes "debugging" the .ini data easier.
if settings.DockOrder == -1 then
buf:appendf("DockId=0x%08X\n", settings.DockId)
else
buf:appendf("DockId=0x%08X,%d\n", settings.DockId, settings.DockOrder)
end
if settings.ClassId ~= 0 then buf:appendf("ClassId=0x%08X\n", settings.ClassId) end
end
end
if g.IO.ConfigIniSettingsSaveLastUsedDate and settings.LastUsedDate ~= 0 then
buf:appendf("LastUsed=%08d\n", settings.LastUsedDate)
end
buf:append("\n")
end
end
end
function ImGui.WindowSettingsAddSettingsHandler()
local ini_handler = ImGuiSettingsHandler()
ini_handler.TypeName = "Window"
ini_handler.TypeHash = ImHashStr("Window")
ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll
ini_handler.CleanupFn = WindowSettingsHandler_Cleanup
ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen
ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine
ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll
ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll
ImGui.AddSettingsHandler(ini_handler)
end
-------------------------------------------------------------------------------
-- [SECTION] LOGGING/CAPTURING
-------------------------------------------------------------------------------
-- All text output from the interface can be captured into tty/file/clipboard.
-- By default, tree nodes are automatically opened during logging.
-------------------------------------------------------------------------------
-- Pass text data straight to log (without being displayed)
local function LogTextV_Internal(g, fmt, ...)
local s = (select("#", ...) > 0) and string.format(fmt, ...) or fmt
if g.LogFile then
print(s) -- TTY (the only "file" available on Roblox)
else
g.LogBuffer:append(s)
end
end
function ImGui.LogText(fmt, ...)
local g = GImGui
if not g.LogEnabled then return end
LogTextV_Internal(g, fmt, ...)
end
ImGui.LogTextV = ImGui.LogText
-- Internal version that takes a position to decide on newline placement and pad items according to their depth.
-- We split text into individual lines to add current tree level padding
-- FIXME: This code is a little complicated perhaps, considering simplifying the whole system.
--- `text` is a Lua string, `text_begin/text_end` optional 1-based byte range (text_end exclusive).
function ImGui.LogRenderedText(ref_pos, text, text_end, text_begin)
local g = GImGui
local window = g.CurrentWindow
local prefix, suffix = g.LogNextPrefix, g.LogNextSuffix
g.LogNextPrefix = nil; g.LogNextSuffix = nil
text_begin = text_begin or 1
if not text_end then text_end = ImGui.FindRenderedTextEnd(text, text_end) end
local log_new_line = ref_pos and (ref_pos.y > g.LogLinePosY + ImMax(g.Style.FramePadding.y, g.Style.ItemSpacing.y) + 1)
if ref_pos then g.LogLinePosY = ref_pos.y end
if log_new_line then
ImGui.LogText("\n")
g.LogLineFirstItem = true
end
if prefix then ImGui.LogRenderedText(ref_pos, prefix, #prefix + 1) end -- Calculate end ourself to ensure "##" are included here.
-- Re-adjust padding if we have popped out of our starting depth
if g.LogDepthRef > window.DC.TreeDepth then g.LogDepthRef = window.DC.TreeDepth end
local tree_depth = window.DC.TreeDepth - g.LogDepthRef
local text_remaining = text_begin
while true do
-- Split the string. Each new line (after a '\n') is followed by indentation corresponding to the current depth of our log entry.
-- We don't add a trailing \n yet to allow a subsequent item on the same line to be captured.
local line_start = text_remaining
local nl = string.find(text, "\n", line_start, true)
local line_end = (nl and nl < text_end) and nl or text_end
local is_last_line = (line_end == text_end)
if line_start ~= line_end or not is_last_line then
local indentation = g.LogLineFirstItem and tree_depth * 4 or 1
ImGui.LogText(string.rep(" ", indentation) .. string.sub(text, line_start, line_end - 1))
g.LogLineFirstItem = false
if string.byte(text, line_end) == 10 then
ImGui.LogText("\n")
g.LogLineFirstItem = true
end
end
if is_last_line then break end
text_remaining = line_end + 1
end
if suffix then ImGui.LogRenderedText(ref_pos, suffix, #suffix + 1) end
end
-- Start logging/capturing text output
function ImGui.LogBegin(flags, auto_open_depth)
local g = GImGui
local window = g.CurrentWindow
assert(g.LogEnabled == false)
g.LogEnabled = true; g.ItemUnclipByLog = true
g.LogFlags = flags
g.LogWindow = window
g.LogNextPrefix = nil; g.LogNextSuffix = nil
g.LogDepthRef = window.DC.TreeDepth
g.LogDepthToExpand = ((auto_open_depth or -1) >= 0) and auto_open_depth or g.LogDepthToExpandDefault
g.LogLinePosY = 3.4028234663853e+38 --[[FLT_MAX]]
g.LogLineFirstItem = true
g.LogBuffer = ImGuiTextBuffer()
end
-- Important: doesn't copy underlying data, use carefully (prefix/suffix must be in scope at the time of the next LogRenderedText)
function ImGui.LogSetNextTextDecoration(prefix, suffix)
local g = GImGui
g.LogNextPrefix = prefix
g.LogNextSuffix = suffix
end
function ImGui.LogToTTY(auto_open_depth)
local g = GImGui
if g.LogEnabled then return end
ImGui.LogBegin(1 --[[ImGuiLogFlags.OutputTTY]], auto_open_depth or -1)
g.LogFile = "stdout"
end
-- Start logging/capturing text output to given file
function ImGui.LogToFile(auto_open_depth, filename)
-- Roblox: no file access. Falls back to TTY (output window).
ImGui.LogToTTY(auto_open_depth)
end
-- Start logging/capturing text output to clipboard
function ImGui.LogToClipboard(auto_open_depth)
local g = GImGui
if g.LogEnabled then return end
ImGui.LogBegin(8 --[[ImGuiLogFlags.OutputClipboard]], auto_open_depth or -1)
end
function ImGui.LogToBuffer(auto_open_depth)
local g = GImGui
if g.LogEnabled then return end
ImGui.LogBegin(4 --[[ImGuiLogFlags.OutputBuffer]], auto_open_depth or -1)
end
function ImGui.LogFinish()
local g = GImGui
if not g.LogEnabled then return end
ImGui.LogText("\n")
local out = bit32.band(g.LogFlags, 15 --[[ImGuiLogFlags.OutputMask_]])
if out == 8 --[[ImGuiLogFlags.OutputClipboard]] then
if not g.LogBuffer:empty() then ImGui.SetClipboardText(g.LogBuffer:c_str()) end
end
g.LogEnabled = false; g.ItemUnclipByLog = false
g.LogFlags = 0 --[[ImGuiLogFlags.None]]
g.LogFile = nil
g.LogBuffer = ImGuiTextBuffer()
end
-- Helper to display logging buttons
-- FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
function ImGui.LogButtons()
local g = GImGui
ImGui.PushID("LogButtons")
local log_to_tty = ImGui.Button("Log To TTY"); ImGui.SameLine()
local log_to_file = ImGui.Button("Log To File"); ImGui.SameLine()
local log_to_clipboard = ImGui.Button("Log To Clipboard"); ImGui.SameLine()
ImGui.PushItemFlag(1 --[[ImGuiItemFlags.NoTabStop]], true)
ImGui.SetNextItemWidth(ImGui.CalcTextSize("999").x)
g.LogDepthToExpandDefault = ImGui.SliderInt("Default Depth", g.LogDepthToExpandDefault, 0, 9, nil)
ImGui.PopItemFlag()
ImGui.PopID()
-- Start logging at the end of the function so that the buttons don't appear in the log
if log_to_tty then ImGui.LogToTTY() end
if log_to_file then ImGui.LogToFile() end
if log_to_clipboard then ImGui.LogToClipboard() end
end
--- @param window ImGuiWindow
--- @return ImGuiWindow?
local function GetWindowForTitleDisplay(window)
if window.DockNodeAsHost then
return window.DockNodeAsHost.VisibleWindow
else
return window
end
end
-- Code not using ItemAdd() may need to call this manually otherwise ActiveId will be cleared. In IMGUI_VERSION_NUM < 18717 this was called by GetID().
--- @param id ImGuiID
@native
function ImGui.KeepAliveID(id)
local g = GImGui
if g.ActiveId == id then
g.ActiveIdIsAlive = id
end
if g.DeactivatedItemData.ID == id then
g.DeactivatedItemData.IsAlive = true
end
end
-- Test if mouse cursor is hovering given rectangle
-- NB- Rectangle is clipped by our current clip setting
-- NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
--- @param r_min ImVec2
--- @param r_max ImVec2
--- @param clip? bool
@native
function ImGui.IsMouseHoveringRect(r_min, r_max, clip)
if clip == nil then clip = true end
local g = GImGui
-- [Luau] scalar version of: rect_clipped = ImRect(r_min, r_max); ClipWith(window.ClipRect); ContainsWithPad(); viewport Overlaps()
-- Clip
local x0, y0, x1, y1 = r_min.x, r_min.y, r_max.x, r_max.y
if clip then
local cr = g.CurrentWindow.ClipRect
local cmin, cmax = cr.Min, cr.Max
x0 = math.max(x0, cmin.x); y0 = math.max(y0, cmin.y)
x1 = math.min(x1, cmax.x); y1 = math.min(y1, cmax.y)
end
-- Hit testing, expanded for touch input
local p, pad = g.IO.MousePos, g.Style.TouchExtraPadding
if not (p.x >= x0 - pad.x and p.y >= y0 - pad.y and p.x < x1 + pad.x and p.y < y1 + pad.y) then
return false
end
local vp = g.MouseViewport
local vx0, vy0 = vp.Pos.x, vp.Pos.y
local vx1, vy1 = vx0 + vp.Size.x, vy0 + vp.Size.y
if not (vx0 <= x1 and vx1 >= x0 and vy0 <= y1 and vy1 >= y0) then
return false
end
return true
end
-- Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.
-- [Internal] This doesn't test if the button is pressed
--- @param button ImGuiMouseButton
--- @param lock_threshold? float
function ImGui.IsMouseDragPastThreshold(button, lock_threshold)
if lock_threshold == nil then lock_threshold = -1.0 end
local g = GImGui
assert(button >= 0 and button < 3) -- IM_COUNTOF(g.IO.MouseDown)
if lock_threshold < 0.0 then
lock_threshold = g.IO.MouseDragThreshold
end
return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold
end
--- @param button ImGuiMouseButton
--- @param lock_threshold? float
function ImGui.IsMouseDragging(button, lock_threshold)
if lock_threshold == nil then lock_threshold = -1.0 end
local g = GImGui
assert(button >= 0 and button < 3) -- IM_COUNTOF(g.IO.MouseDown)
if not g.IO.MouseDown[button] then
return false
end
return ImGui.IsMouseDragPastThreshold(button, lock_threshold)
end
-- This is called TeleportMousePos() and not SetMousePos() to emphasis that setting MousePosPrev will effectively clear mouse delta as well.
-- It is expected you only call this if (io.BackendFlags & ImGuiBackendFlags.HasSetMousePos) is set and supported by backend.
--- @param pos ImVec2
function ImGui.TeleportMousePos(pos)
local g = GImGui
ImVec2_Copy(g.IO.MousePos, pos)
ImVec2_Copy(g.IO.MousePosPrev, pos)
ImVec2_CopyV(g.IO.MouseDelta, 0.0, 0.0)
g.IO.WantSetMousePos = true
-- IMGUI_DEBUG_LOG_IO("TeleportMousePos: (%.1f,%.1f)", io.MousePos.x, io.MousePos.y)
end
-- Called from within ItemAdd: at this point we can read from NextItemData and write to LastItemData
--- @param id ImGuiID
function ImGui.ItemHandleShortcut(id)
local g = GImGui
local flags = g.NextItemData.ShortcutFlags
assert(bit32.band(flags, bit32.bnot(523519 --[[ImGuiInputFlags.SupportedBySetNextItemShortcut]])) == 0) -- Passing flags not supported by SetNextItemShortcut()!
if bit32.band(g.LastItemData.ItemFlags, 64 --[[ImGuiItemFlags.Disabled]]) ~= 0 then
return
end
if bit32.band(flags, 262144 --[[ImGuiInputFlags.Tooltip]]) ~= 0 then
g.LastItemData.StatusFlags = bit32.bor(g.LastItemData.StatusFlags, 1024 --[[ImGuiItemStatusFlags.HasShortcut]])
g.LastItemData.Shortcut = g.NextItemData.Shortcut
end
if not ImGui.Shortcut(g.NextItemData.Shortcut, bit32.band(flags, 261375 --[[ImGuiInputFlags.SupportedByShortcut]]), id) or g.NavActivateId ~= 0 then
return
end
-- FIXME: Generalize Activation queue?
g.NavActivateId = id -- Will effectively disable clipping.
g.NavActivateFlags = bit32.bor(1 --[[ImGuiActivateFlags.PreferInput]], 16 --[[ImGuiActivateFlags.FromShortcut]])
-- if (g.ActiveId == 0 || g.ActiveId == id)
g.NavActivateDownId = id
g.NavActivatePressedId = id
ImGui.NavHighlightActivated(id)
end
-- Declare item bounding box for clipping and interaction.
-- Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
-- declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.
-- THIS IS IN THE PERFORMANCE CRITICAL PATH (UNTIL THE CLIPPING TEST AND EARLY-RETURN)
--- @param bb ImRect
--- @param id ImGuiID
--- @param nav_bb_arg? ImRect
--- @param extra_flags? ImGuiItemFlags
@native
function ImGui.ItemAdd(bb, id, nav_bb_arg, extra_flags)
if extra_flags == nil then extra_flags = 0 end
local g = GImGui
local window = g.CurrentWindow
local last_item_data, next_item_data = g.LastItemData, g.NextItemData -- [Luau] cached field reads
-- Set item data
-- (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags.HasDisplayRect is set)
last_item_data.ID = id
-- [Luau] inline ImRect_Copy(g.LastItemData.Rect, bb); ImRect_Copy(g.LastItemData.NavRect, nav_bb_arg or bb)
local bb_min, bb_max = bb.Min, bb.Max
local r = last_item_data.Rect
r.Min.x = bb_min.x; r.Min.y = bb_min.y; r.Max.x = bb_max.x; r.Max.y = bb_max.y
local nav_bb = nav_bb_arg and nav_bb_arg or bb
r = last_item_data.NavRect
r.Min.x = nav_bb.Min.x; r.Min.y = nav_bb.Min.y; r.Max.x = nav_bb.Max.x; r.Max.y = nav_bb.Max.y
last_item_data.ItemFlags = bit32.bor(g.CurrentItemFlags, next_item_data.ItemFlagsSet, extra_flags)
last_item_data.StatusFlags = 0 --[[ImGuiItemStatusFlags.None]]
-- Note: we don't copy 'g.NextItemData.SelectionUserData' to an hypothetical g.LastItemData.SelectionUserData: since the former is not cleared.
if id ~= 0 then
-- [Luau] inline ImGui.KeepAliveID(id)
if g.ActiveId == id then g.ActiveIdIsAlive = id end
if g.DeactivatedItemData.ID == id then g.DeactivatedItemData.IsAlive = true end
-- Directional navigation processing
-- Runs prior to clipping early-out
-- (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
-- (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
-- unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
-- thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
-- We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
-- to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
-- We intentionally don't check if g.NavWindow ~= nil because g.NavAnyRequest should only be set when it is non nil.
-- If we crash on a nil g.NavWindow we need to fix the bug elsewhere.
if bit32.band(last_item_data.ItemFlags, 2 --[[ImGuiItemFlags.NoNav]]) == 0 then
-- FIXME-NAV: investigate changing the window tests into a simple 'if (g.NavFocusScopeId == g.CurrentFocusScopeId)' test.
window.DC.NavLayersActiveMaskNext = bit32.bor(window.DC.NavLayersActiveMaskNext, bit32.lshift(1, window.DC.NavLayerCurrent))
if g.NavId == id or g.NavAnyRequest then
if g.NavWindow.RootWindowForNav == window.RootWindowForNav then
if window == g.NavWindow or bit32.band(bit32.bor(window.ChildFlags, g.NavWindow.ChildFlags), 256 --[[ImGuiChildFlags.NavFlattened]]) ~= 0 then
ImGui.NavProcessItem()
end
end
end
end
if bit32.band(next_item_data.HasFlags, 4 --[[ImGuiNextItemDataFlags.HasShortcut]]) ~= 0 then
ImGui.ItemHandleShortcut(id)
end
end
-- Lightweight clear of SetNextItemXXX data.
next_item_data.HasFlags = 0 --[[ImGuiNextItemDataFlags.None]]
next_item_data.ItemFlagsSet = 0 --[[ImGuiItemFlags.None]]
-- Clipping test
-- (this is an inline copy of IsClippedEx() so we can reuse the is_rect_visible value, otherwise we'd do 'if (IsClippedEx(bb, id)) return false')
-- g.NavActivateId is not necessarily == g.NavId, in the case of remote activation (e.g. shortcuts)
local bmin, bmax, cr = bb.Min, bb.Max, window.ClipRect
local is_rect_visible = bmin.x <= cr.Max.x and bmax.x >= cr.Min.x and bmin.y <= cr.Max.y and bmax.y >= cr.Min.y
if not is_rect_visible then
if id == 0 or not (id == g.ActiveId or id == g.ActiveIdPreviousFrame or id == g.NavId or id == g.NavActivateId or g.ItemUnclipByLog) then
return false
end
end
-- [DEBUG]
if not IMGUI_DISABLE_DEBUG_TOOLS then
if id ~= 0 then
if id == g.DebugLocateId then
ImGui.DebugLocateItemResolveWithLastItem()
end
-- [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something".
-- Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something".
-- READ THE FAQ: https://dearimgui.com/faq
assert(id ~= window.ID, "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!")
end
end
if id ~= 0 and g.DeactivatedItemData.ID == id then
g.DeactivatedItemData.ElapseFrame = g.FrameCount
end
-- We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
if is_rect_visible then
last_item_data.StatusFlags = bit32.bor(last_item_data.StatusFlags, 256 --[[ImGuiItemStatusFlags.Visible]])
end
if ImGui.IsMouseHoveringRect(bb.Min, bb.Max) then
last_item_data.StatusFlags = bit32.bor(last_item_data.StatusFlags, 1 --[[ImGuiItemStatusFlags.HoveredRect]])
end
return true
end
-------------------------------------------------------------------------------
-- [SECTION] LAYOUT
-------------------------------------------------------------------------------
-- - ItemSize()
-- - SameLine()
-- - GetCursorScreenPos()
-- - SetCursorScreenPos()
-- - GetCursorPos(), GetCursorPosX(), GetCursorPosY()
-- - SetCursorPos(), SetCursorPosX(), SetCursorPosY()
-- - GetCursorStartPos()
-- - Indent()
-- - Unindent()
-- - SetNextItemWidth()
-- - PushItemWidth()
-- - PushMultiItemsWidths()
-- - PopItemWidth()
-- - CalcItemWidth()
-- - CalcItemSize()
-- - GetTextLineHeight()
-- - GetTextLineHeightWithSpacing()
-- - GetFrameHeight()
-- - GetFrameHeightWithSpacing()
-- - GetContentRegionMax()
-- - GetContentRegionAvail(),
-- - BeginGroup()
-- - EndGroup()
-- Also see in imgui_widgets: tab bars, and in imgui_tables: tables, columns.
-------------------------------------------------------------------------------
-- Advance cursor given item size for layout.
-- Register minimum needed size so it can extend the bounding box used for auto-fit calculation.
-- See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different.
-- THIS IS IN THE PERFORMANCE CRITICAL PATH.
--- @param size_or_bb ImVec2|ImRect
--- @param text_baseline_y? float
@native
function ImGui.ItemSize(size_or_bb, text_baseline_y)
if text_baseline_y == nil then text_baseline_y = -1.0 end
local size_x, size_y
local bb_min = rawget(size_or_bb, "Min") -- [Luau] rawget: `.Min` on an ImVec2 would go through IM_VEC2.__index
if bb_min then
--- @cast size_or_bb ImRect
size_x = size_or_bb.Max.x - bb_min.x; size_y = size_or_bb.Max.y - bb_min.y
else
--- @cast size_or_bb ImVec2
size_x = size_or_bb.x; size_y = size_or_bb.y
end
local g = GImGui
local window = g.CurrentWindow
if window.SkipItems then
return
end
local dc = window.DC
local mathMax = math.max
-- We increase the height in this function to accommodate for baseline offset.
-- In theory we should be offsetting the starting position (window.DC.CursorPos), that will be the topic of a larger refactor,
-- but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
local offset_to_match_baseline_y
if text_baseline_y >= 0 then
offset_to_match_baseline_y = mathMax(0, dc.CurrLineTextBaseOffset - text_baseline_y)
else
offset_to_match_baseline_y = 0
end
local cursor, prev_line, max_pos = dc.CursorPos, dc.CursorPosPrevLine, dc.CursorMaxPos
local line_y1
if dc.IsSameLine then
line_y1 = prev_line.y
else
line_y1 = cursor.y
end
local line_height = mathMax(dc.CurrLineSize.y, cursor.y - line_y1 + size_y + offset_to_match_baseline_y)
local spacing_y = g.Style.ItemSpacing.y
-- Always align ourselves on pixel boundaries
-- if (g.IO.KeyAlt) window.DrawList:AddRect(window.DC.CursorPos, window.DC.CursorPos + ImVec2(size.x, line_height), IM_COL32(255,0,0,200)) -- [DEBUG]
prev_line.x = cursor.x + size_x
prev_line.y = line_y1
cursor.x = math.floor(window.Pos.x + dc.Indent.x + dc.ColumnsOffset.x) -- Next line
cursor.y = math.floor(line_y1 + line_height + spacing_y) -- Next line
max_pos.x = mathMax(max_pos.x, prev_line.x)
max_pos.y = mathMax(max_pos.y, cursor.y - spacing_y)
-- if (g.IO.KeyAlt) window.DrawList:AddCircle(window.DC.CursorMaxPos, 3.0, IM_COL32(255,0,0,255), 4) -- [DEBUG]
dc.PrevLineSize.y = line_height
dc.CurrLineSize.y = 0
dc.PrevLineTextBaseOffset = mathMax(dc.CurrLineTextBaseOffset, text_baseline_y)
dc.CurrLineTextBaseOffset = 0
dc.IsSetPos = false
dc.IsSameLine = false
-- Horizontal layout mode
if (dc.LayoutType == 0 --[[ImGuiLayoutType.Horizontal]]) then
ImGui.SameLine()
end
end
-- Gets back to previous line and continue with horizontal layout
-- offset_from_start_x == 0 : follow right after previous item
-- offset_from_start_x != 0 : align to specified x position (relative to window/group left)
-- spacing_w < 0 : use default spacing if offset_from_start_x == 0, no spacing if offset_from_start_x != 0
-- spacing_w >= 0 : enforce spacing amount
--- @param offset_from_start_x float?
--- @param spacing_w float?
@native
function ImGui.SameLine(offset_from_start_x, spacing_w)
if offset_from_start_x == nil then offset_from_start_x = 0.0 end
if spacing_w == nil then spacing_w = -1.0 end
local g = GImGui
local window = g.CurrentWindow
if window.SkipItems then
return
end
local dc = window.DC -- [Luau] cached field read
if offset_from_start_x ~= 0.0 then
if spacing_w < 0.0 then spacing_w = 0.0 end
dc.CursorPos.x = window.Pos.x - window.Scroll.x + offset_from_start_x + spacing_w + dc.GroupOffset.x + dc.ColumnsOffset.x
dc.CursorPos.y = dc.CursorPosPrevLine.y
else
if spacing_w < 0.0 then spacing_w = g.Style.ItemSpacing.x end
dc.CursorPos.x = dc.CursorPosPrevLine.x + spacing_w
dc.CursorPos.y = dc.CursorPosPrevLine.y
end
dc.CurrLineSize.x = dc.PrevLineSize.x; dc.CurrLineSize.y = dc.PrevLineSize.y -- [Luau] inline ImVec2_Copy()
dc.CurrLineTextBaseOffset = dc.PrevLineTextBaseOffset
dc.IsSameLine = true
end
--- @param indent_w? float
function ImGui.Indent(indent_w)
if indent_w == nil then indent_w = 0.0 end
local g = GImGui
local window = ImGui.GetCurrentWindow()
if indent_w ~= 0.0 then
window.DC.Indent.x = window.DC.Indent.x + indent_w
else
window.DC.Indent.x = window.DC.Indent.x + g.Style.IndentSpacing
end
window.DC.CursorPos.x = window.Pos.x + window.DC.Indent.x + window.DC.ColumnsOffset.x
end
--- @param indent_w? float
function ImGui.Unindent(indent_w)
if indent_w == nil then indent_w = 0.0 end
local g = GImGui
local window = ImGui.GetCurrentWindow()
if indent_w ~= 0.0 then
window.DC.Indent.x = window.DC.Indent.x - indent_w
else
window.DC.Indent.x = window.DC.Indent.x - g.Style.IndentSpacing
end
window.DC.CursorPos.x = window.Pos.x + window.DC.Indent.x + window.DC.ColumnsOffset.x
end
-- Affect large frame+labels widgets only.
--- @param item_width float
function ImGui.SetNextItemWidth(item_width)
local g = GImGui
g.NextItemData.HasFlags = bit32.bor(g.NextItemData.HasFlags, 1 --[[ImGuiNextItemDataFlags.HasWidth]])
g.NextItemData.Width = item_width
end
-- FIXME: Remove the == 0.0f behavior?
--- @param item_width float
function ImGui.PushItemWidth(item_width)
local g = GImGui
local window = g.CurrentWindow
-- [Luau] inline window.DC.ItemWidthStack:push_back(window.DC.ItemWidth) -- Backup current width
local stack = window.DC.ItemWidthStack
local n = stack.Size + 1
if n > stack.Capacity then stack:push_back(window.DC.ItemWidth) else stack.Data[n] = window.DC.ItemWidth; stack.Size = n end
window.DC.ItemWidth = (item_width == 0.0) and window.DC.ItemWidthDefault or item_width
g.NextItemData.HasFlags = bit32.band(g.NextItemData.HasFlags, bit32.bnot(1 --[[ImGuiNextItemDataFlags.HasWidth]]))
end
function ImGui.PopItemWidth()
local g = GImGui
local window = g.CurrentWindow
if window.DC.ItemWidthStack.Size <= 0 then
IM_ASSERT_USER_ERROR(false, "Calling PopItemWidth() too many times!")
return
end
local stack = window.DC.ItemWidthStack
window.DC.ItemWidth = stack.Data[stack.Size] -- [Luau] inline back()/pop_back()
stack.Size = stack.Size - 1
end
--- @param components int
--- @param w_full float
function ImGui.PushMultiItemsWidths(components, w_full)
local g = GImGui
local window = g.CurrentWindow
assert(components > 0)
local style = g.Style
window.DC.ItemWidthStack:push_back(window.DC.ItemWidth) -- Backup current width
local w_items = w_full - style.ItemInnerSpacing.x * (components - 1)
local prev_split = w_items
for i = components - 1, 1, -1 do
local next_split = IM_TRUNC(w_items * i / components)
window.DC.ItemWidthStack:push_back(ImMax(prev_split - next_split, 1.0))
prev_split = next_split
end
window.DC.ItemWidth = ImMax(prev_split, 1.0)
g.NextItemData.HasFlags = bit32.band(g.NextItemData.HasFlags, bit32.bnot(1 --[[ImGuiNextItemDataFlags.HasWidth]]))
end
--- @return float
function ImGui.GetFrameHeight()
local g = GImGui
return g.FontSize + g.Style.FramePadding.y * 2.0
end
--- @return ImVec2
--- @nodiscard
@native
function ImGui.GetContentRegionAvail()
local g = GImGui
local window = g.CurrentWindow
local mx
if window.DC.CurrentColumns or g.CurrentTable then
mx = window.WorkRect.Max
else
mx = window.ContentRegionRect.Max
end
return ImVec2(mx.x - window.DC.CursorPos.x, mx.y - window.DC.CursorPos.y)
end
--- @return ImVec2
--- @nodiscard
function ImGui.GetCursorScreenPos()
local window = ImGui.GetCurrentWindowRead()
local ret = ImVec2()
ImVec2_Copy(ret, window.DC.CursorPos)
return ret
end
--- @param pos ImVec2
function ImGui.SetCursorScreenPos(pos)
local window = ImGui.GetCurrentWindow()
ImVec2_Copy(window.DC.CursorPos, pos)
-- window.DC.CursorMaxPos = ImMax(window.DC.CursorMaxPos, window.DC.CursorPos)
window.DC.IsSetPos = true
end
-- User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
-- Conversion happens as we pass the value to user, but it makes our naming convention confusing because GetCursorPos() == (DC.CursorPos - window.Pos). May want to rename 'DC.CursorPos'.
--- @return ImVec2
function ImGui.GetCursorPos()
local window = ImGui.GetCurrentWindowRead()
return ImVec2(window.DC.CursorPos.x - window.Pos.x + window.Scroll.x, window.DC.CursorPos.y - window.Pos.y + window.Scroll.y)
end
function ImGui.GetCursorPosX()
local window = ImGui.GetCurrentWindowRead()
return window.DC.CursorPos.x - window.Pos.x + window.Scroll.x
end
function ImGui.GetCursorPosY()
local window = ImGui.GetCurrentWindowRead()
return window.DC.CursorPos.y - window.Pos.y + window.Scroll.y
end
--- @param local_pos ImVec2
function ImGui.SetCursorPos(local_pos)
local window = ImGui.GetCurrentWindow()
ImVec2_CopyV(window.DC.CursorPos, window.Pos.x - window.Scroll.x + local_pos.x, window.Pos.y - window.Scroll.y + local_pos.y)
-- window.DC.CursorMaxPos = ImMax(window.DC.CursorMaxPos, window.DC.CursorPos)
window.DC.IsSetPos = true
end
--- @param x float
function ImGui.SetCursorPosX(x)
local window = ImGui.GetCurrentWindow()
window.DC.CursorPos.x = window.Pos.x - window.Scroll.x + x
-- window.DC.CursorMaxPos.x = ImMax(window.DC.CursorMaxPos.x, window.DC.CursorPos.x)
window.DC.IsSetPos = true
end
--- @param y float
function ImGui.SetCursorPosY(y)
local window = ImGui.GetCurrentWindow()
window.DC.CursorPos.y = window.Pos.y - window.Scroll.y + y
-- window.DC.CursorMaxPos.y = ImMax(window.DC.CursorMaxPos.y, window.DC.CursorPos.y)
window.DC.IsSetPos = true
end
--- @return ImVec2
function ImGui.GetCursorStartPos()
local window = ImGui.GetCurrentWindowRead()
return window.DC.CursorStartPos - window.Pos
end
function ImGui.GetFrameHeightWithSpacing()
local g = GImGui
return g.FontSize + g.Style.FramePadding.y * 2.0 + g.Style.ItemSpacing.y
end
function ImGui.GetTextLineHeightWithSpacing()
local g = GImGui
return g.FontSize + g.Style.ItemSpacing.y
end
-- You should never need those functions. Always use GetCursorScreenPos() and GetContentRegionAvail()!
-- They are bizarre local-coordinates which don't play well with scrolling.
--- @return ImVec2
function ImGui.GetContentRegionMax()
local window = GImGui.CurrentWindow
return ImGui.GetContentRegionAvail() + window.DC.CursorPos - window.Pos
end
--- @return ImVec2
function ImGui.GetWindowContentRegionMin()
local window = GImGui.CurrentWindow
return window.ContentRegionRect.Min - window.Pos
end
--- @return ImVec2
function ImGui.GetWindowContentRegionMax()
local window = GImGui.CurrentWindow
return window.ContentRegionRect.Max - window.Pos
end
-- Overloads: IsRectVisible(size) / IsRectVisible(rect_min, rect_max)
function ImGui.IsRectVisible(a, b)
local window = GImGui.CurrentWindow
if b == nil then
return window.ClipRect:Overlaps(ImRect(window.DC.CursorPos, window.DC.CursorPos + a))
end
return window.ClipRect:Overlaps(ImRect(a, b))
end
-- Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)
-- Groups are currently a mishmash of functionalities which should perhaps be clarified and separated.
-- FIXME-OPT: Could we safely early out on ->SkipItems?
@native
function ImGui.BeginGroup()
local g = GImGui
local window = g.CurrentWindow
local group_data = ImGuiGroupData()
group_data.WindowID = window.ID
ImVec2_Copy(group_data.BackupCursorPos, window.DC.CursorPos)
ImVec2_Copy(group_data.BackupCursorPosPrevLine, window.DC.CursorPosPrevLine)
ImVec2_Copy(group_data.BackupCursorMaxPos, window.DC.CursorMaxPos)
ImVec1_Copy(group_data.BackupIndent, window.DC.Indent)
ImVec1_Copy(group_data.BackupGroupOffset, window.DC.GroupOffset)
ImVec2_Copy(group_data.BackupCurrLineSize, window.DC.CurrLineSize)
group_data.BackupCurrLineTextBaseOffset = window.DC.CurrLineTextBaseOffset
group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive
group_data.BackupHoveredIdIsAlive = (g.HoveredId ~= 0)
group_data.BackupIsSameLine = window.DC.IsSameLine
group_data.BackupAnyIdHasBeenEditedThisFrame = g.AnyIdHasBeenEditedThisFrame
group_data.BackupDeactivatedIdIsAlive = g.DeactivatedItemData.IsAlive
group_data.EmitItem = true
g.GroupStack:push_back(group_data)
window.DC.GroupOffset.x = window.DC.CursorPos.x - window.Pos.x - window.DC.ColumnsOffset.x
ImVec1_Copy(window.DC.Indent, window.DC.GroupOffset)
ImVec2_Copy(window.DC.CursorMaxPos, window.DC.CursorPos)
ImVec2_CopyV(window.DC.CurrLineSize, 0.0, 0.0)
if (g.LogEnabled) then
g.LogLinePosY = -3.4028234663853e+38 --[[FLT_MAX]] -- To enforce a carriage return
end
end
@native
function ImGui.EndGroup()
local g = GImGui
local window = g.CurrentWindow
assert(g.GroupStack.Size > 0) -- Mismatched BeginGroup()/EndGroup() calls
local group_data = g.GroupStack:back()
assert(group_data.WindowID == window.ID) -- EndGroup() in wrong window?
if (window.DC.IsSetPos) then
-- TODO: ImGui.ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
end
-- Include LastItemData.Rect.Max as a workaround for e.g. EndTable() undershooting with CursorMaxPos report. (#7543)
local group_bb = ImRect(group_data.BackupCursorPos, ImMax(ImMax(window.DC.CursorMaxPos, g.LastItemData.Rect.Max), group_data.BackupCursorPos))
ImVec2_Copy(window.DC.CursorPos, group_data.BackupCursorPos)
ImVec2_Copy(window.DC.CursorPosPrevLine, group_data.BackupCursorPosPrevLine)
ImVec2_Copy(window.DC.CursorMaxPos, ImMax(group_data.BackupCursorMaxPos, group_bb.Max))
ImVec1_Copy(window.DC.Indent, group_data.BackupIndent)
ImVec1_Copy(window.DC.GroupOffset, group_data.BackupGroupOffset)
ImVec2_Copy(window.DC.CurrLineSize, group_data.BackupCurrLineSize)
window.DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset
window.DC.IsSameLine = group_data.BackupIsSameLine
if (g.LogEnabled) then
g.LogLinePosY = -3.4028234663853e+38 --[[FLT_MAX]] -- To enforce a carriage return
end
if (not group_data.EmitItem) then
g.GroupStack:pop_back()
return
end
window.DC.CurrLineTextBaseOffset = ImMax(window.DC.PrevLineTextBaseOffset, group_data.BackupCurrLineTextBaseOffset) -- FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now.
ImGui.ItemSize(group_bb:GetSize())
ImGui.ItemAdd(group_bb, 0, nil, 1 --[[ImGuiItemFlags.NoTabStop]])
-- If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive(), IsItemDeactivated() etc. will be functional on the entire group.
-- It would be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets.
-- Also if you grep for LastItemId you'll notice it is only used in that context.
-- (The two tests not the same because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.)
local group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive ~= g.ActiveId) and (g.ActiveIdIsAlive == g.ActiveId) and g.ActiveId
local group_contains_deactivated_id = (group_data.BackupDeactivatedIdIsAlive == false) and (g.DeactivatedItemData.IsAlive == true)
if group_contains_curr_active_id then
g.LastItemData.ID = g.ActiveId
elseif group_contains_deactivated_id then
g.LastItemData.ID = g.DeactivatedItemData.ID
end
ImRect_Copy(g.LastItemData.Rect, group_bb)
-- Forward Hovered flag
local group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) and g.HoveredId ~= 0
if group_contains_curr_hovered_id then
g.LastItemData.StatusFlags = bit32.bor(g.LastItemData.StatusFlags, 128 --[[ImGuiItemStatusFlags.HoveredWindow]])
end
-- Forward Edited flag
if g.AnyIdHasBeenEditedThisFrame and not group_data.BackupAnyIdHasBeenEditedThisFrame then
g.LastItemData.StatusFlags = bit32.bor(g.LastItemData.StatusFlags, 4 --[[ImGuiItemStatusFlags.Edited]])
end
-- Forward Deactivated flag
g.LastItemData.StatusFlags = bit32.bor(g.LastItemData.StatusFlags, 32 --[[ImGuiItemStatusFlags.HasDeactivated]])
if group_contains_deactivated_id then
g.LastItemData.StatusFlags = bit32.bor(g.LastItemData.StatusFlags, 64 --[[ImGuiItemStatusFlags.Deactivated]])
end
g.GroupStack:pop_back()
if g.DebugShowGroupRects then
window.DrawList:AddRect(group_bb.Min, group_bb.Max, IM_COL32(255, 0, 255, 255)) -- [Debug]
end
end
-- Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
-- The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
@native
function ImGui.CalcItemWidth()
local g = GImGui
local window = g.CurrentWindow
local w
if bit32.band(g.NextItemData.HasFlags, 1 --[[ImGuiNextItemDataFlags.HasWidth]]) ~= 0 then
w = g.NextItemData.Width
else
w = window.DC.ItemWidth
end
if w < 0.0 then
-- [Luau] scalar GetContentRegionAvail().x, no temporary ImVec2
local mx = (window.DC.CurrentColumns or g.CurrentTable) and window.WorkRect.Max or window.ContentRegionRect.Max
local region_avail_x = mx.x - window.DC.CursorPos.x
w = math.max(1.0, region_avail_x + w)
end
w = IM_TRUNC(w)
return w
end
-- [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
-- Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
-- Note that only CalcItemWidth() is publicly exposed.
-- The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable)
--- @param size ImVec2
--- @param default_w float
--- @param default_h float
--- @return ImVec2
function ImGui.CalcItemSize(size, default_w, default_h)
return ImVec2(ImGui._CalcItemSizeXY(size.x, size.y, default_w, default_h)) -- [Luau] 'size' is by value upstream: never written to
end
-- [Luau] scalar CalcItemSize(): callers that build 'size' themselves skip both ImVec2 allocations
--- @return float, float
function ImGui._CalcItemSizeXY(size_x, size_y, default_w, default_h)
local avail_x, avail_y
if size_x < 0.0 or size_y < 0.0 then
-- [Luau] scalar GetContentRegionAvail(), no temporary ImVec2
local g = GImGui
local window = g.CurrentWindow
local mx = (window.DC.CurrentColumns or g.CurrentTable) and window.WorkRect.Max or window.ContentRegionRect.Max
avail_x = mx.x - window.DC.CursorPos.x; avail_y = mx.y - window.DC.CursorPos.y
end
if size_x == 0.0 then
size_x = default_w
elseif size_x < 0.0 then
size_x = math.max(4.0, avail_x + size_x) -- <-- size.x is negative here so we are subtracting
end
if size_y == 0.0 then
size_y = default_h
elseif size_y < 0.0 then
size_y = math.max(4.0, avail_y + size_y) -- <-- size.y is negative here so we are subtracting
end
return size_x, size_y
end
function ImGui.GetTextLineHeight()
local g = GImGui
return g.FontSize
end
function ImGui.IsItemActive()
local g = GImGui
if g.ActiveId ~= 0 then
return g.ActiveId == g.LastItemData.ID
end
return false
end
-- == (GetItemID() == GetFocusID() && GetFocusID() != 0)
function ImGui.IsItemFocused()
local g = GImGui
if g.NavId ~= g.LastItemData.ID or g.NavId == 0 then
return false
end
-- Special handling for the dummy item after Begin() which represent the title bar or tab.
-- When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
local window = g.CurrentWindow
if g.LastItemData.ID == window.ID and window.WriteAccessed then
return false
end
return true
end
function ImGui.IsItemDeactivated()
local g = GImGui
if bit32.band(g.LastItemData.StatusFlags, 32 --[[ImGuiItemStatusFlags.HasDeactivated]]) ~= 0 then
return bit32.band(g.LastItemData.StatusFlags, 64 --[[ImGuiItemStatusFlags.Deactivated]]) ~= 0
end
return g.DeactivatedItemData.ID == g.LastItemData.ID and g.LastItemData.ID ~= 0 and g.DeactivatedItemData.ElapseFrame >= g.FrameCount
end
-- Since 1.92.9: consider using NoLiveEdit flags if all you need to that values are not written bo backing variable while typing.
function ImGui.IsItemDeactivatedAfterEdit()
local g = GImGui
return ImGui.IsItemDeactivated() and g.DeactivatedItemData.HasBeenEditedBefore
end
function ImGui.IsItemActivated()
local g = GImGui
if g.ActiveId ~= 0 then
if g.ActiveId == g.LastItemData.ID and g.ActiveIdPreviousFrame ~= g.LastItemData.ID then
return true
end
end
return false
end
-- Important: this can be useful but it is NOT equivalent to the behavior of e.g.Button()!
-- Most widgets have specific reactions based on mouse-up/down state, mouse position etc.
--- @param mouse_button? ImGuiMouseButton
function ImGui.IsItemClicked(mouse_button)
if mouse_button == nil then mouse_button = 0 end
return ImGui.IsMouseClicked(mouse_button) and ImGui.IsItemHovered(0 --[[ImGuiHoveredFlags.None]])
end
function ImGui.IsItemToggledOpen()
return bit32.band(GImGui.LastItemData.StatusFlags, 16 --[[ImGuiItemStatusFlags.ToggledOpen]]) ~= 0
end
-- Call after a Selectable() or TreeNode() items inside a BeginMultiSelect()/EndMultiSelect() scope.
-- - Useful if you need the per-item information before reaching EndMultiSelect(), e.g. for rendering purpose.
-- Outside of a multi-select block:
-- - It would be misleading/ambiguous to report this signal, as widgets return e.g. a pressed event,
-- and user code is in charge of altering selection in ways we cannot predict.
-- Prefer using 'if (IsItemClicked() && !IsItemToggledOpen())' for a manual reimplementation of selection.
function ImGui.IsItemToggledSelection()
local g = GImGui
assert(g.CurrentMultiSelect ~= nil) -- Can only be used inside a BeginMultiSelect()/EndMultiSelect()
return bit32.band(g.LastItemData.StatusFlags, 8 --[[ImGuiItemStatusFlags.ToggledSelection]]) ~= 0
end
-- IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,
-- you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!
-- Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
function ImGui.IsAnyItemHovered()
local g = GImGui
return g.HoveredId ~= 0 or g.HoveredIdPreviousFrame ~= 0
end
function ImGui.IsAnyItemActive()
return GImGui.ActiveId ~= 0
end
function ImGui.IsAnyItemFocused()
local g = GImGui
return g.NavId ~= 0 and g.NavCursorVisible
end
function ImGui.IsItemVisible()
return bit32.band(GImGui.LastItemData.StatusFlags, 256 --[[ImGuiItemStatusFlags.Visible]]) ~= 0
end
function ImGui.IsItemEdited()
return bit32.band(GImGui.LastItemData.StatusFlags, 4 --[[ImGuiItemStatusFlags.Edited]]) ~= 0
end
-- Allow next item to be overlapped by subsequent items.
-- This works by requiring HoveredId to match for two subsequent frames,
-- so if a following items overwrite it our interactions will naturally be disabled.
function ImGui.SetNextItemAllowOverlap()
local g = GImGui
g.NextItemData.ItemFlagsSet = bit32.bor(g.NextItemData.ItemFlagsSet, 16384 --[[ImGuiItemFlags.AllowOverlap]])
end
function ImGui.GetItemID()
return GImGui.LastItemData.ID
end
function ImGui.GetItemFlags()
return GImGui.LastItemData.ItemFlags
end
--- @return ImVec2
function ImGui.GetItemRectSize()
return GImGui.LastItemData.Rect:GetSize()
end
function ImGui.GetHoveredID()
local g = GImGui
return (g.HoveredId ~= 0) and g.HoveredId or g.HoveredIdPreviousFrame
end
-------------------------------------------------------------------------------
-- [SECTION] WINDOW FOCUS
-------------------------------------------------------------------------------
-- - SetWindowFocus()
-- - SetNextWindowFocus()
-- - IsWindowFocused()
-- - UpdateWindowInFocusOrderList() [Internal]
-- - BringWindowToFocusFront() [Internal]
-- - BringWindowToDisplayFront() [Internal]
-- - BringWindowToDisplayBack() [Internal]
-- - BringWindowToDisplayBehind() [Internal]
-- - FindWindowDisplayIndex() [Internal]
-- - FocusWindow() [Internal]
-- - FocusTopMostWindowUnderOne() [Internal]
-------------------------------------------------------------------------------
-- Moving window to front of display and set focus (which happens to be back of our sorted list)
--- @param window? ImGuiWindow
--- @param flags? ImGuiFocusRequestFlags
function ImGui.FocusWindow(window, flags)
if flags == nil then flags = 0 end
local g = GImGui
-- Modal check?
if bit32.band(flags, 2 --[[ImGuiFocusRequestFlags.UnlessBelowModal]]) ~= 0 and g.NavWindow ~= window then -- Early out in common case.
local blocking_modal = ImGui.FindBlockingModal(window)
if blocking_modal then
-- This block would typically be reached in two situations:
-- - API call to FocusWindow() with a window under a modal and ImGuiFocusRequestFlags.UnlessBelowModal flag.
-- - User clicking on void or anything behind a modal while a modal is open (window == nil)
-- IMGUI_DEBUG_LOG_FOCUS("[focus] FocusWindow(\"%s\", UnlessBelowModal): prevented by \"%s\".", window ? window->Name : "<NULL>", blocking_modal->Name)
if window and window == window.RootWindow and bit32.band(window.Flags, 8192 --[[ImGuiWindowFlags.NoBringToFrontOnFocus]]) == 0 then
ImGui.BringWindowToDisplayBehind(window, blocking_modal) -- Still bring right under modal. (FIXME: Could move in focus list too?)
end
ImGui.ClosePopupsOverWindow(ImGui.GetTopMostPopupModal(), false) -- Note how we need to use GetTopMostPopupModal() aad NOT blocking_modal, to handle nested modals
return
end
end
-- Find last focused child (if any) and focus it instead.
if bit32.band(flags, 1 --[[ImGuiFocusRequestFlags.RestoreFocusedChild]]) ~= 0 and window ~= nil then
window = ImGui.NavRestoreLastChildNavWindow(window)
end
-- Apply focus
if g.NavWindow ~= window then
ImGui.SetNavWindow(window)
if window and g.NavHighlightItemUnderNav then
g.NavMousePosDirty = true
end
g.NavId = window and window.NavLastIds[0] or 0 -- Restore NavId
g.NavLayer = 0 --[[ImGuiNavLayer.Main]]
ImGui.SetNavFocusScope(window and window.NavRootFocusScopeId or 0)
g.NavIdIsAlive = false
g.NavLastValidSelectionUserData = (-1) --[[ImGuiSelectionUserData_Invalid]]
-- Close popups if any
ImGui.ClosePopupsOverWindow(window, false)
end
-- Move the root window to the top of the pile
assert(window == nil or window.RootWindowDockTree ~= nil)
local focus_front_window = window and window.RootWindow or nil
local display_front_window = window and window.RootWindowDockTree or nil
local dock_node = window and window.DockNode or nil
local active_id_window_is_dock_node_host = g.ActiveIdWindow and dock_node and dock_node.HostWindow == g.ActiveIdWindow
-- Steal active widgets. Some of the cases it triggers includes:
-- - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run.
-- - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId)
-- - Using dock host items (tab, collapse button) can trigger this before we redirect the ActiveIdWindow toward the child window.
if g.ActiveId ~= 0 and g.ActiveIdWindow and g.ActiveIdWindow.RootWindow ~= focus_front_window then
if not g.ActiveIdNoClearOnFocusLoss and not active_id_window_is_dock_node_host then
ImGui.ClearActiveID()
end
end
-- Passing nil allow to disable keyboard focus
if not window then
return
end
window.LastFrameJustFocused = g.FrameCount
-- Select in dock node
-- For #2304 we avoid applying focus immediately before the tabbar is visible.
-- if (dock_node && dock_node->TabBar)
-- dock_node->TabBar->SelectedTabId = dock_node->TabBar->NextSelectedTabId = window->TabId;
-- Bring to front
ImGui.BringWindowToFocusFront(focus_front_window)
if bit32.band(bit32.bor(window.Flags, focus_front_window.Flags, display_front_window.Flags), 8192 --[[ImGuiWindowFlags.NoBringToFrontOnFocus]]) == 0 then
ImGui.BringWindowToDisplayFront(display_front_window)
end
end
--- @param window ImGuiWindow
--- @return int
function ImGui.FindWindowFocusIndex(window)
local g = GImGui
-- IM_UNUSED(g)
local order = window.FocusOrder
assert(window.RootWindow == window) -- No child window (not testing _ChildWindow because of docking)
assert(g.WindowsFocusOrder.Data[order] == window)
return order
end
-- Can we focus this window with Ctrl+Tab (or PadMenu + PadFocusPrev/PadFocusNext)
-- Note that NoNavFocus makes the window not reachable with Ctrl+Tab but it can still be focused with mouse or programmatically.
-- If you want a window to never be focused, you may use the e.g. NoInputs flag.
--- @param window ImGuiWindow
--- @return bool
function ImGui.IsWindowNavFocusable(window)
return window.WasActive and window == window.RootWindow and bit32.band(window.Flags, 131072 --[[ImGuiWindowFlags.NoNavFocus]]) == 0
end
--- @param i_start int
--- @param i_stop int
--- @param dir int
--- @return ImGuiWindow?
function ImGui.FindWindowNavFocusable(i_start, i_stop, dir) -- FIXME-OPT O(N)
local g = GImGui
local i = i_start
while i >= 1 and i <= g.WindowsFocusOrder.Size and i ~= i_stop do
local window = g.WindowsFocusOrder.Data[i]
if ImGui.IsWindowNavFocusable(window) then
return window
end
i = i + dir
end
return nil
end
function ImGui.UpdateWindowInFocusOrderList(window, just_created, new_flags)
local g = GImGui
local new_is_explicit_child = (bit32.band(new_flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0) and ((bit32.band(new_flags, 67108864 --[[ImGuiWindowFlags.Popup]]) == 0) or (bit32.band(new_flags, 268435456 --[[ImGuiWindowFlags.ChildMenu]]) ~= 0))
local child_flag_changed = (new_is_explicit_child ~= window.IsExplicitChild)
if (just_created or child_flag_changed) and not new_is_explicit_child then
assert(not g.WindowsFocusOrder:contains(window))
g.WindowsFocusOrder:push_back(window)
window.FocusOrder = g.WindowsFocusOrder.Size
elseif not just_created and child_flag_changed and new_is_explicit_child then
assert(g.WindowsFocusOrder.Data[window.FocusOrder] == window)
for n = window.FocusOrder + 1, g.WindowsFocusOrder.Size do
g.WindowsFocusOrder.Data[n].FocusOrder = g.WindowsFocusOrder.Data[n].FocusOrder - 1
end
g.WindowsFocusOrder:erase(window.FocusOrder)
window.FocusOrder = -1
end
window.IsExplicitChild = new_is_explicit_child
end
--- @param window? ImGuiWindow
function ImGui.BringWindowToFocusFront(window)
local g = GImGui
assert(window == window.RootWindow)
local cur_order = window.FocusOrder
assert(g.WindowsFocusOrder.Data[cur_order] == window)
if g.WindowsFocusOrder:back() == window then
return
end
local new_order = g.WindowsFocusOrder.Size
for n = cur_order, new_order - 1 do
g.WindowsFocusOrder.Data[n] = g.WindowsFocusOrder.Data[n + 1]
g.WindowsFocusOrder.Data[n].FocusOrder = g.WindowsFocusOrder.Data[n].FocusOrder - 1
assert(g.WindowsFocusOrder.Data[n].FocusOrder == n)
end
g.WindowsFocusOrder.Data[new_order] = window
window.FocusOrder = new_order
end
-- Note technically focus related but rather adjacent and close to BringWindowToFocusFront()
-- FIXME-FOCUS: Could opt-in/opt-out enable modal check like in FocusWindow().
--- @param window? ImGuiWindow
function ImGui.BringWindowToDisplayFront(window)
local g = GImGui
local current_front_window = g.Windows:back()
if current_front_window == window or current_front_window.RootWindowDockTree == window then -- Cheap early out (could be better)
return
end
for i, this_window in g.Windows:iter() do
if this_window == window then
g.Windows:erase(i)
break
end
end
g.Windows:push_back(window)
end
--- @param window ImGuiWindow
--- @param behind_window ImGuiWindow
function ImGui.BringWindowToDisplayBehind(window, behind_window)
assert(window ~= nil and behind_window ~= nil)
local g = GImGui
window = window.RootWindow
behind_window = behind_window.RootWindow
-- TODO:
end
--- @param under_this_window? ImGuiWindow
--- @param ignore_window? ImGuiWindow
--- @param filter_viewport? ImGuiViewport
--- @param flags ImGuiFocusRequestFlags
function ImGui.FocusTopMostWindowUnderOne(under_this_window, ignore_window, filter_viewport, flags)
local g = GImGui
local start_idx = g.WindowsFocusOrder.Size
if under_this_window ~= nil then
-- Aim at root window behind us, if we are in a child window that's our own root (see #4640)
local offset = -1
while bit32.band(under_this_window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0 do
under_this_window = under_this_window.ParentWindow
offset = 0
end
--- @cast under_this_window ImGuiWindow
start_idx = ImGui.FindWindowFocusIndex(under_this_window) + offset
end
for i = start_idx, 1, -1 do
repeat
-- We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user.
local window = g.WindowsFocusOrder.Data[i]
if window == ignore_window or not window.WasActive then
break
end
if filter_viewport ~= nil and window.Viewport ~= filter_viewport then
break
end
if bit32.band(window.Flags, bit32.bor(512 --[[ImGuiWindowFlags.NoMouseInputs]], 65536 --[[ImGuiWindowFlags.NoNavInputs]])) ~= bit32.bor(512 --[[ImGuiWindowFlags.NoMouseInputs]], 65536 --[[ImGuiWindowFlags.NoNavInputs]]) then
-- FIXME-DOCK: When ImGuiFocusRequestFlags.RestoreFocusedChild is set...
-- This is failing (lagging by one frame) for docked windows.
-- If A and B are docked into window and B disappear, at the NewFrame() call site window.NavLastChildNavWindow will still point to B.
-- We might leverage the tab order implicitly stored in window.DockNodeAsHost.TabBar (essentially the 'most_recently_selected_tab' code in tab bar will do that but on next update)
-- to tell which is the "previous" window. Or we may leverage 'LastFrameFocused/LastFrameJustFocused' and have this function handle child window itself?
ImGui.FocusWindow(window, flags)
return
end
until true
end
ImGui.FocusWindow(nil, flags)
end
--- @param id ImGuiID
--- @param window ImGuiWindow
function ImGui.SetFocusID(id, window)
local g = GImGui
assert(id ~= 0)
if g.NavWindow ~= window then
ImGui.SetNavWindow(window)
end
-- Assume that SetFocusID() is called in the context where its window.DC.NavLayerCurrent and g.CurrentFocusScopeId are valid.
-- Note that window may be ~= g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)
local nav_layer = window.DC.NavLayerCurrent
g.NavId = id
g.NavLayer = nav_layer
ImGui.SetNavFocusScope(g.CurrentFocusScopeId)
window.NavLastIds[nav_layer] = id
if g.LastItemData.ID == id then
ImRect_Copy(window.NavRectRel[nav_layer], ImGui.WindowRectAbsToRel(window, g.LastItemData.NavRect))
end
g.NavIdItemFlags = (g.LastItemData.ID == id) and g.LastItemData.ItemFlags or 0 --[[ImGuiItemFlags.None]]
if id == g.ActiveIdIsAlive then
g.NavIdIsAlive = true
end
if g.ActiveIdSource == 2 --[[ImGuiInputSource.Keyboard]] or g.ActiveIdSource == 3 --[[ImGuiInputSource.Gamepad]] then
g.NavHighlightItemUnderNav = true
elseif g.IO.ConfigNavCursorVisibleAuto then
g.NavCursorVisible = false
end
-- Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
ImGui.NavClearPreferredPosForAxis(1 --[[ImGuiAxis.X]])
ImGui.NavClearPreferredPosForAxis(2 --[[ImGuiAxis.Y]])
end
-- This is not 100% symmetric with StartMouseMovingWindow().
-- We do NOT clear ActiveID, because:
-- - It would lead to rather confusing recursive code paths. Caller can call ClearActiveID() if desired.
-- - Some code intentionally cancel moving but keep the ActiveID to lock inputs (e.g. code path taken when clicking a disabled item).
function ImGui.StopMouseMovingWindow()
local g = GImGui
local window = g.MovingWindow
-- Ref commits 6b7766817, 36055213c for some partial history on checking if viewport ~= nil.
if window and window.Viewport then
-- Try to merge the window back into the main viewport.
-- This works because MouseViewport should be ~= MovingWindow.Viewport on release (as per code in UpdateViewports)
if bit32.band(g.ConfigFlagsCurrFrame, 1024 --[[ImGuiConfigFlags.ViewportsEnable]]) ~= 0 then
ImGui.UpdateTryMergeWindowIntoHostViewport(window.RootWindowDockTree, g.MouseViewport)
end
-- Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button.
if (not ImGui.IsDragDropPayloadBeingAccepted()) then
g.MouseViewport = window.Viewport
end
-- Clear the NoInputs window flag set by the Viewport system in AddUpdateViewport()
local window_can_use_inputs = bit32.band(window.Flags, 512 --[[ImGuiWindowFlags.NoMouseInputs]]) == 0 or bit32.band(window.Flags, 65536 --[[ImGuiWindowFlags.NoNavInputs]]) == 0
if window_can_use_inputs then
window.Viewport.Flags = bit32.band(window.Viewport.Flags, bit32.bnot(128 --[[ImGuiViewportFlags.NoInputs]]))
end
end
g.MovingWindow = nil
end
--- @param id ImGuiID
--- @param window? ImGuiWindow
function ImGui.SetActiveID(id, window)
local g = GImGui
-- Clear previous active id
if g.ActiveId ~= 0 then
-- Store deactivate data
local deactivated_data = g.DeactivatedItemData
deactivated_data.ID = g.ActiveId
deactivated_data.ElapseFrame = (g.LastItemData.ID == g.ActiveId) and g.FrameCount or g.FrameCount + 1 -- FIXME: OK to use LastItemData?
deactivated_data.HasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore
deactivated_data.IsAlive = (g.ActiveIdIsAlive == g.ActiveId)
-- This could be written in a more general way (e.g associate a hook to ActiveId),
-- but since this is currently quite an exception we'll leave it as is.
-- One common scenario leading to this is: pressing Key ->NavMoveRequestApplyResult() -> ClearActiveID()
if g.InputTextState.ID == g.ActiveId then
ImGui.InputTextDeactivateHook(g.ActiveId)
end
-- While most behaved code would make an effort to not steal active id during window move/drag operations,
-- we at least need to be resilient to it. Canceling the move is rather aggressive and users of 'master' branch
-- may prefer the weird ill-defined half working situation ('docking' did assert), so may need to rework that.
if g.MovingWindow and (g.ActiveId == g.MovingWindow.MoveId) then
print("SetActiveID() cancel MovingWindow")
ImGui.StopMouseMovingWindow()
end
end
-- Set active id
g.ActiveIdIsJustActivated = (g.ActiveId ~= id)
if (g.ActiveIdIsJustActivated) then
-- IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() 0x%08X in \"%s\"%*s(previously 0x%08X in \"%s\")\n", id, window ? window->Name : "",
-- ImMax(0, 20 - (int)(window ? strlen(window->Name) : 0)), "", g.ActiveId, g.ActiveIdWindow ? g.ActiveIdWindow->Name : "");
g.ActiveIdTimer = 0.0
g.ActiveIdHasBeenPressedBefore = false
g.ActiveIdHasBeenEditedBefore = false
g.ActiveIdMouseButton = -1
if id ~= 0 then
g.LastActiveId = id
g.LastActiveIdTimer = 0.0
end
end
g.ActiveId = id
g.ActiveIdAllowOverlap = false
g.ActiveIdNoClearOnFocusLoss = false
g.ActiveIdWindow = window
g.ActiveIdHasBeenEditedThisFrame = false
g.ActiveIdFromShortcut = false
g.ActiveIdDisabledId = 0
if id ~= 0 then
g.ActiveIdIsAlive = id
if g.NavActivateId == id or g.NavJustMovedToId == id then
g.ActiveIdSource = g.NavInputSource
else
g.ActiveIdSource = 1 --[[ImGuiInputSource.Mouse]]
end
assert(g.ActiveIdSource ~= 0 --[[ImGuiInputSource.None]])
end
-- Clear declaration of inputs claimed by the widget
-- (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
g.ActiveIdUsingNavDirMask = 0x00
g.ActiveIdUsingAllKeyboardKeys = false
end
function ImGui.ClearActiveID()
ImGui.SetActiveID(0, nil) -- g.ActiveId = 0
end
-------------------------------------------------------------------------------
-- [SECTION] ID STACK
-------------------------------------------------------------------------------
--- @param str_id string|int
@native
function ImGui.PushID(str_id)
local g = GImGui
local window = g.CurrentWindow
local id = window:GetID(str_id)
-- [Luau] inline window.IDStack:push_back(id)
local stack = window.IDStack
local n = stack.Size + 1
if n > stack.Capacity then stack:push_back(id) else stack.Data[n] = id; stack.Size = n end
end
-- Push a given id value ignoring the ID stack as a seed.
--- @param id ImGuiID
function ImGui.PushOverrideID(id)
local g = GImGui
local window = g.CurrentWindow
if g.DebugHookIdInfoId == id and id ~= 0 then
ImGui.DebugHookIdInfo(id, 12 --[[ImGuiDataType.ID]], nil)
end
-- [Luau] inline window.IDStack:push_back(id)
local stack = window.IDStack
local n = stack.Size + 1
if n > stack.Capacity then stack:push_back(id) else stack.Data[n] = id; stack.Size = n end
end
@native
function ImGui.PopID()
local window = GImGui.CurrentWindow
local stack = window.IDStack
IM_ASSERT_USER_ERROR_RET(stack.Size > 1, "Calling PopID() too many times!")
stack.Size = stack.Size - 1 -- [Luau] inline stack:pop_back()
end
-- This is one of the very rare legacy case where we use ImGuiWindow methods,
-- it should ideally be flattened at some point but it's been used a lots by widgets.
--- @param id string|int
--- @return ImGuiID
@native
function MT.ImGuiWindow:GetID(id)
local stack = self.IDStack
local seed = stack.Data[stack.Size] -- [Luau] inline self.IDStack:back()
local h
if type(id) == "string" then
h = ImHashStr(id, nil, seed)
else --- @cast id int
h = ImHashData(id, -1, seed)
end
local g = GImGui
if g.DebugHookIdInfoId == h and h ~= 0 then
ImGui.DebugHookIdInfo(h, (type(id) == "string") and 9 --[[ImGuiDataType.String]] or 4 --[[ImGuiDataType.S32]], id)
end
return h
end
-- This is only used in rare/specific situations to manufacture an ID out of nowhere.
-- FIXME: Consider instead storing last non-zero ID + count of successive zero-ID, and combine those?
--- @param p_abs ImVec2
--- @return ImGuiID
function MT.ImGuiWindow:GetIDFromPos(p_abs)
local seed = self.IDStack:back()
local p_rel = ImGui.WindowPosAbsToRel(self, p_abs)
return ImHashData(p_rel, 2, seed)
end
-- "
--- @param r_abs ImRect
--- @return ImGuiID
function MT.ImGuiWindow:GetIDFromRectangle(r_abs)
local seed = self.IDStack:back()
local r_rel = ImGui.WindowRectAbsToRel(self, r_abs)
return ImHashData({ math.floor(r_rel.Min.x), math.floor(r_rel.Min.y), math.floor(r_rel.Max.x), math.floor(r_rel.Max.y) }, 4, seed)
end
-- Overloads: GetID(str_id) / GetID(str_id_begin, str_id_end) / GetID(int_id)
--- @param str_or_int string|int
--- @param str_end? int # exclusive end (1-based byte position)
--- @return ImGuiID
function ImGui.GetID(str_or_int, str_end)
local window = GImGui.CurrentWindow
if str_end ~= nil and type(str_or_int) == "string" then
return ImHashStr(str_or_int, str_end - 1, window.IDStack:back())
end
return window:GetID(str_or_int)
end
-- Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call
-- (note that when using this pattern, ID Stack Tool will tend to not display the intermediate stack level.
-- for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more)
-- Overloads: GetIDWithSeed(str, str_end, seed) / GetIDWithSeed(n, seed)
function ImGui.GetIDWithSeed(a, b, c)
local id
if type(a) == "number" then
id = ImHashData(a, -1, b)
else
id = ImHashStr(a, b and (b - 1) or nil, c)
end
if GImGui.DebugHookIdInfoId == id and id ~= 0 then
ImGui.DebugHookIdInfo(id, (type(a) == "number") and 4 --[[ImGuiDataType.S32]] or 9 --[[ImGuiDataType.String]], (b and type(a) == "string") and string.sub(a, 1, b - 1) or a)
end
return id
end
--- @param id ImGuiID
@native
function ImGui.SetHoveredID(id)
local g = GImGui
g.HoveredId = id
g.HoveredIdAllowOverlap = false
if id ~= 0 and g.HoveredIdPreviousFrame ~= id then
g.HoveredIdTimer = 0.0
g.HoveredIdNotActiveTimer = 0.0
end
end
--- @param flags ImGuiHoveredFlags
--- @return float
local function CalcDelayFromHoveredFlags(flags)
local g = GImGui
if bit32.band(flags, 65536 --[[ImGuiHoveredFlags.DelayNormal]]) ~= 0 then
return g.Style.HoverDelayNormal
end
if bit32.band(flags, 32768 --[[ImGuiHoveredFlags.DelayShort]]) ~= 0 then
return g.Style.HoverDelayShort
end
return 0.0
end
--- @param user_flags ImGuiHoveredFlags
--- @param shared_flags ImGuiHoveredFlags
local function ApplyHoverFlagsForTooltip(user_flags, shared_flags)
-- Allow instance flags to override shared flags
if bit32.band(user_flags, bit32.bor(16384 --[[ImGuiHoveredFlags.DelayNone]], 32768 --[[ImGuiHoveredFlags.DelayShort]], 65536 --[[ImGuiHoveredFlags.DelayNormal]])) ~= 0 then
shared_flags = bit32.band(shared_flags, bit32.bnot(bit32.bor(16384 --[[ImGuiHoveredFlags.DelayNone]], 32768 --[[ImGuiHoveredFlags.DelayShort]], 65536 --[[ImGuiHoveredFlags.DelayNormal]])))
end
return bit32.bor(user_flags, shared_flags)
end
--- @param id ImGuiID
function ImGui.MarkItemEdited(id)
-- This marking is to be able to provide info for IsItemDeactivatedAfterEdit().
-- ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need to fill the data.
local g = GImGui
-- IM_ASSERT(g.LastItemData.ID == id) -- Failing cases include: "widgets_inputtext_scrolling", "widgets_inputtext_multiline_status", "widgets_selectable_input" = case of e.g TempInputText() overlayed manually with different ID (#2718)
g.LastItemData.StatusFlags = bit32.bor(g.LastItemData.StatusFlags, 2048 --[[ImGuiItemStatusFlags.EditedInternal]])
if bit32.band(g.LastItemData.ItemFlags, 65536 --[[ImGuiItemFlags.NoMarkEdited]]) ~= 0 then
return
end
g.LastItemData.StatusFlags = bit32.bor(g.LastItemData.StatusFlags, 4 --[[ImGuiItemStatusFlags.Edited]])
if g.ActiveId == id or g.ActiveId == 0 then
-- FIXME: Can't we fully rely on LastItemData yet?
g.AnyIdHasBeenEditedThisFrame = true
g.ActiveIdHasBeenEditedThisFrame = true
g.ActiveIdHasBeenEditedBefore = true
end
if g.DeactivatedItemData.ID == id then
g.DeactivatedItemData.HasBeenEditedBefore = true
end
-- We accept a MarkItemEdited() on drag and drop targets (see https://github.com/ocornut/imgui/issues/1875#issuecomment-978243343)
-- We accept 'ActiveIdPreviousFrame == id' for InputText() returning an edit after it has been taken ActiveId away (#4714)
-- FIXME: This assert is getting a bit meaningless over time. It helped detect some unusual use cases but eventually it is becoming an unnecessary restriction.
assert(g.DragDropActive or g.ActiveId == id or g.ActiveId == 0 or g.DeactivatedItemData.ID == id or g.ActiveIdPreviousFrame == id or g.NavJustMovedToId or (g.CurrentMultiSelect ~= nil and g.BoxSelectState.IsActive))
-- IM_ASSERT(g.CurrentWindow.DC.LastItemId == id)
g.LastItemData.StatusFlags = bit32.bor(g.LastItemData.StatusFlags, 4 --[[ImGuiItemStatusFlags.Edited]])
end
--- @param window ImGuiWindow
--- @param flags ImGuiHoveredFlags
function ImGui.IsWindowContentHoverable(window, flags)
-- An active popup disable hovering on other windows (apart from its own children)
-- FIXME-OPT: This could be cached/stored within the window.
local g = GImGui
if g.NavWindow then
local focused_root_window = g.NavWindow.RootWindowDockTree
if focused_root_window.WasActive and focused_root_window ~= window.RootWindowDockTree then
-- For the purpose of those flags we differentiate "standard popup" from "modal popup"
-- NB: The 'else' is important because Modal windows are also Popups.
local want_inhibit = false
if bit32.band(focused_root_window.Flags, 134217728 --[[ImGuiWindowFlags.Modal]]) ~= 0 then
want_inhibit = true
elseif (bit32.band(focused_root_window.Flags, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0) and (bit32.band(flags, 32 --[[ImGuiHoveredFlags.AllowWhenBlockedByPopup]]) == 0) then
want_inhibit = true
end
-- Inhibit hover unless the window is within the stack of our modal/popup
if want_inhibit then
if not ImGui.IsWindowWithinBeginStackOf(window.RootWindow, focused_root_window) then
return false
end
end
end
end
-- Filter by viewport
if (window.Viewport ~= g.MouseViewport) then
if (g.MovingWindow == nil or window.RootWindowDockTree ~= g.MovingWindow.RootWindowDockTree) then
return false
end
end
return true
end
-- This is roughly matching the behavior of internal-facing ItemHoverable()
-- - we allow hovering to be true when ActiveId==window.MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered()
-- - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
--- @param flags? ImGuiHoveredFlags
@native
function ImGui.IsItemHovered(flags)
if flags == nil then flags = 0 end
local g = GImGui
local window = g.CurrentWindow
IM_ASSERT_USER_ERROR(bit32.band(flags, bit32.bnot(262048 --[[ImGuiHoveredFlags.AllowedMaskForIsItemHovered]])) == 0, "Invalid flags for IsItemHovered()!")
if g.NavHighlightItemUnderNav and g.NavCursorVisible and bit32.band(flags, 2048 --[[ImGuiHoveredFlags.NoNavOverride]]) == 0 then
if not ImGui.IsItemFocused() then
return false
end
if bit32.band(g.LastItemData.ItemFlags, 64 --[[ImGuiItemFlags.Disabled]]) ~= 0 and bit32.band(flags, 1024 --[[ImGuiHoveredFlags.AllowWhenDisabled]]) == 0 then
return false
end
if bit32.band(flags, 4096 --[[ImGuiHoveredFlags.ForTooltip]]) ~= 0 then
flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipNav)
end
else
-- Test for bounding box overlap, as updated as ItemAdd()
local status_flags = g.LastItemData.StatusFlags
if bit32.band(status_flags, 1 --[[ImGuiItemStatusFlags.HoveredRect]]) == 0 then
return false
end
if bit32.band(flags, 4096 --[[ImGuiHoveredFlags.ForTooltip]]) ~= 0 then
flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse)
end
-- Done with rectangle culling so we can perform heavier checks now
-- Test if we are hovering the right window (our window could be behind another window)
-- [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851)
-- [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable
-- to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was
-- the test that has been running for a long while.
if g.HoveredWindow ~= window and bit32.band(status_flags, 128 --[[ImGuiItemStatusFlags.HoveredWindow]]) == 0 then
if bit32.band(flags, 512 --[[ImGuiHoveredFlags.AllowWhenOverlappedByWindow]]) == 0 then
return false
end
end
-- Test if another item is active (e.g. being dragged)
local id = g.LastItemData.ID
if bit32.band(flags, 128 --[[ImGuiHoveredFlags.AllowWhenBlockedByActiveItem]]) == 0 then
if g.ActiveId ~= 0 and g.ActiveId ~= id and not g.ActiveIdAllowOverlap and not g.ActiveIdFromShortcut then
-- When ActiveId == MoveId it means that either:
-- - (1) user clicked on void _or_ an item with no id, which triggers moving window (ActiveId is set even when window has _NoMove flag)
-- - the (id == 0) test handles it, however, IsItemHovered() will leak between id==0 items (mostly visible when using _NoMove). -- FIXME: May be fixed.
-- - (2) user clicked a disabled item. UpdateMouseMovingWindowEndFrame() uses ActiveId == MoveId to avoid interference with item logic + sets ActiveIdDisabledId.
local cancel_is_hovered = true
if g.ActiveId == window.MoveId and (id == 0 or g.ActiveIdDisabledId == id) then
cancel_is_hovered = false
end
-- When ActiveId == TabId it means user clicked docking tab for the window.
if g.ActiveId == window.TabId then
cancel_is_hovered = false
end
if cancel_is_hovered then
return false
end
end
end
-- Test if interactions on this window are blocked by an active popup or modal.
-- The ImGuiHoveredFlags.AllowWhenBlockedByPopup flag will be tested here.
if not ImGui.IsWindowContentHoverable(window, flags) and bit32.band(g.LastItemData.ItemFlags, 8192 --[[ImGuiItemFlags.NoWindowHoverableCheck]]) == 0 then
return false
end
-- Test if the item is disabled
if bit32.band(g.LastItemData.ItemFlags, 64 --[[ImGuiItemFlags.Disabled]]) ~= 0 and bit32.band(flags, 1024 --[[ImGuiHoveredFlags.AllowWhenDisabled]]) == 0 then
return false
end
-- Special handling for calling after Begin() which represent the title bar or tab.
-- When the window is skipped/collapsed (SkipItems==true) that last item (always .MoveId submitted by Begin)
-- will never be overwritten so we need to detect the case.
if id == window.MoveId and window.WriteAccessed then
return false
end
-- Test if using AllowOverlap and overlapped
if bit32.band(g.LastItemData.ItemFlags, 16384 --[[ImGuiItemFlags.AllowOverlap]]) ~= 0 and id ~= 0 then
if bit32.band(flags, 256 --[[ImGuiHoveredFlags.AllowWhenOverlappedByItem]]) == 0 then
if g.HoveredIdPreviousFrame ~= g.LastItemData.ID then
return false
end
end
end
end
-- Handle hover delay
-- (some ideas: https://www.nngroup.com/articles/timing-exposing-content)
local delay = CalcDelayFromHoveredFlags(flags)
if delay > 0.0 or bit32.band(flags, 8192 --[[ImGuiHoveredFlags.Stationary]]) ~= 0 then
local hover_delay_id
if g.LastItemData.ID ~= 0 then
hover_delay_id = g.LastItemData.ID
else
hover_delay_id = window:GetIDFromPos(g.LastItemData.Rect.Min)
end
if bit32.band(flags, 131072 --[[ImGuiHoveredFlags.NoSharedDelay]]) ~= 0 and g.HoverItemDelayIdPreviousFrame ~= hover_delay_id then
g.HoverItemDelayTimer = 0.0
end
g.HoverItemDelayId = hover_delay_id
-- When changing hovered item we requires a bit of stationary delay before activating hover timer,
-- but once unlocked on a given item we also moving.
-- if (g.HoverDelayTimer >= delay && (g.HoverDelayTimer - g.IO.DeltaTime < delay || g.MouseStationaryTimer - g.IO.DeltaTime < g.Style.HoverStationaryDelay)) { IMGUI_DEBUG_LOG("HoverDelayTimer = %f/%f, MouseStationaryTimer = %f\n", g.HoverDelayTimer, delay, g.MouseStationaryTimer); }
if bit32.band(flags, 8192 --[[ImGuiHoveredFlags.Stationary]]) ~= 0 and g.HoverItemUnlockedStationaryId ~= hover_delay_id then
return false
end
if g.HoverItemDelayTimer < delay then
return false
end
end
return true
end
-- Internal facing ItemHoverable() used when submitting widgets. THIS IS A SUBMISSION NOT A HOVER CHECK.
-- Returns whether the item was hovered, logic differs slightly from IsItemHovered().
-- (this does not rely on LastItemData it can be called from a ButtonBehavior() call not following an ItemAdd() call)
-- FIXME-LEGACY: the 'ImGuiItemFlags item_flags' parameter was added on 2023-06-28.
-- If you used this in your legacy/custom widgets code:
-- - Commonly: if your ItemHoverable() call comes after an ItemAdd() call: pass 'item_flags = g.LastItemData.ItemFlags'.
-- - Rare: otherwise you may pass 'item_flags = 0' (ImGuiItemFlags.None) unless you want to benefit from special behavior handled by ItemHoverable.
--- @param bb ImRect
--- @param id ImGuiID
--- @param item_flags ImGuiItemFlags
@native
function ImGui.ItemHoverable(bb, id, item_flags)
local g = GImGui
local window = g.CurrentWindow
-- Detect ID conflicts
-- (this is specifically done here by comparing on hover because it allows us a detection of duplicates that is algorithmically extra cheap, 1 u32 compare per item. No O(log N) lookup whatsoever)
if not IMGUI_DISABLE_DEBUG_TOOLS then
if id ~= 0 and g.HoveredIdPreviousFrame == id and bit32.band(item_flags, 32 --[[ImGuiItemFlags.AllowDuplicateId]]) == 0 then
g.HoveredIdPreviousFrameItemCount = g.HoveredIdPreviousFrameItemCount + 1
if g.DebugDrawIdConflictsId == id then
window.DrawList:AddRect(bb.Min - ImVec2(1, 1), bb.Max + ImVec2(1, 1), IM_COL32(255, 0, 0, 255), 0.0, 2.0, 0 --[[ImDrawFlags.None]])
end
end
end
if g.HoveredWindow ~= window then
return false
end
if not ImGui.IsMouseHoveringRect(bb.Min, bb.Max) then
return false
end
if g.HoveredId ~= 0 and g.HoveredId ~= id and not g.HoveredIdAllowOverlap then
return false
end
if g.ActiveId ~= 0 and g.ActiveId ~= id and not g.ActiveIdAllowOverlap then
if not g.ActiveIdFromShortcut then
return false
end
end
-- We are done with rectangle culling so we can perform heavier checks now.
if (bit32.band(item_flags, 8192 --[[ImGuiItemFlags.NoWindowHoverableCheck]]) == 0) and not ImGui.IsWindowContentHoverable(window, 0 --[[ImGuiHoveredFlags.None]]) then
g.HoveredIdIsDisabled = true
return false
end
-- We exceptionally allow this function to be called with id==0 to allow using it for easy high-level
-- hover test in widgets code. We could also decide to split this function is two.
if id ~= 0 then
-- Drag source doesn't report as hovered
if g.DragDropActive and g.DragDropPayload.SourceId == id and (bit32.band(g.DragDropSourceFlags, 2 --[[ImGuiDragDropFlags.SourceNoDisableHover]]) == 0) then
return false
end
ImGui.SetHoveredID(id)
-- AllowOverlap mode (rarely used) requires previous frame HoveredId to be nil or to match.
-- This allows using patterns where a later submitted widget overlaps a previous one. Generally perceived as a front-to-back hit-test.
if bit32.band(item_flags, 16384 --[[ImGuiItemFlags.AllowOverlap]]) ~= 0 then
g.HoveredIdAllowOverlap = true
if g.HoveredIdPreviousFrame ~= id then
return false
end
end
-- Display shortcut (only works with mouse)
-- (ImGuiItemStatusFlags.HasShortcut in LastItemData denotes we want a tooltip)
if id == g.LastItemData.ID and (bit32.band(g.LastItemData.StatusFlags, 1024 --[[ImGuiItemStatusFlags.HasShortcut]]) ~= 0) and g.ActiveId ~= id then
if ImGui.IsItemHovered(bit32.bor(4096 --[[ImGuiHoveredFlags.ForTooltip]], 65536 --[[ImGuiHoveredFlags.DelayNormal]])) then
ImGui.SetTooltip("%s", ImGui.GetKeyChordName(g.LastItemData.Shortcut))
end
end
end
-- When disabled we'll return false but still set HoveredId
if bit32.band(item_flags, 64 --[[ImGuiItemFlags.Disabled]]) ~= 0 then
-- Release active id if turning disabled
if g.ActiveId == id and id ~= 0 then
ImGui.ClearActiveID()
end
g.HoveredIdIsDisabled = true
return false
end
if not IMGUI_DISABLE_DEBUG_TOOLS then
if id ~= 0 then
-- [DEBUG] Item Picker tool!
-- We perform the check here because reaching is path is rare (1~ time a frame),
-- making the cost of this tool near-zero! We could get better call-stack and support picking non-hovered
-- items if we performed the test in ItemAdd(), but that would incur a bigger runtime cost.
if g.DebugItemPickerActive and g.HoveredIdPreviousFrame == id then
ImGui.GetForegroundDrawList():AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255))
end
if g.DebugItemPickerBreakId == id then
IM_DEBUG_BREAK()
end
end
end
if g.NavHighlightItemUnderNav and (bit32.band(item_flags, 32768 --[[ImGuiItemFlags.NoNavDisableMouseHover]]) == 0) then
return false
end
return true
end
-- FIXME: This is inlined/duplicated in ItemAdd()
-- FIXME: The id ~= 0 path is not used by our codebase, may get rid of it?
--- @param bb ImRect
--- @param id ImGuiID
--- @return bool
function ImGui.IsClippedEx(bb, id)
local g = GImGui
local window = g.CurrentWindow
-- [Luau] inline bb:Overlaps(window.ClipRect)
local bmin, bmax, cr = bb.Min, bb.Max, window.ClipRect
if not (bmin.x <= cr.Max.x and bmax.x >= cr.Min.x and bmin.y <= cr.Max.y and bmax.y >= cr.Min.y) then
if id == 0 or (id ~= g.ActiveId and id ~= g.ActiveIdPreviousFrame and id ~= g.NavId and id ~= g.NavActivateId) then
if not g.ItemUnclipByLog then
return true
end
end
end
return false
end
-- This is also inlined in ItemAdd()
-- Note: if ImGuiItemStatusFlags.HasDisplayRect is set, user needs to set g.LastItemData.DisplayRect.
--- @param item_id ImGuiID
--- @param item_flags ImGuiItemFlags
--- @param status_flags ImGuiItemStatusFlags
--- @param item_rect ImRect
function ImGui.SetLastItemData(item_id, item_flags, status_flags, item_rect)
local g = GImGui
g.LastItemData.ID = item_id
g.LastItemData.ItemFlags = item_flags
g.LastItemData.StatusFlags = status_flags
ImRect_Copy(g.LastItemData.Rect, item_rect)
ImRect_Copy(g.LastItemData.NavRect, item_rect)
end
--- @param window ImGuiWindow
--- @param rect ImRect
function ImGui.SetLastItemDataForWindow(window, rect)
local g = GImGui
if window.DockIsActive then
ImGui.SetLastItemData(window.MoveId, g.CurrentItemFlags, window.DC.DockTabItemStatusFlags, window.DC.DockTabItemRect)
else
ImGui.SetLastItemData(window.MoveId, g.CurrentItemFlags, window.DC.WindowItemStatusFlags, rect)
end
end
-- Pass in translated ASCII characters for text input.
-- - with glfw you can get those from the callback set in glfwSetCharCallback()
-- - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
-- FIXME: Should in theory be called "AddCharacterEvent()" to be consistent with new API
--- @param c unsigned_int
function MT.ImGuiIO:AddInputCharacter(c)
assert(self.Ctx ~= nil)
local g = self.Ctx
if c == 0 or not self.AppAcceptingEvents then
return
end
local e = ImGuiInputEvent()
e.Type = 6 --[[ImGuiInputEventType.Text]]
e.Source = 2 --[[ImGuiInputSource.Keyboard]]
e.EventId = g.InputEventsNextEventId
g.InputEventsNextEventId = g.InputEventsNextEventId + 1
e.Text = ImGuiInputEventText()
e.Text.Char = c
g.InputEventsQueue:push_back(e)
end
-- UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so
-- we should save the high surrogate.
--- @param c ImWchar16
function MT.ImGuiIO:AddInputCharacterUTF16(c)
if (c == 0 and self.InputQueueSurrogate == 0) or not self.AppAcceptingEvents then
return
end
if bit32.band(c, 0xFC00) == 0xD800 then -- High surrogate, must save
if self.InputQueueSurrogate ~= 0 then
self:AddInputCharacter(65533 --[[IM_UNICODE_CODEPOINT_INVALID]])
end
self.InputQueueSurrogate = c
return
end
local cp = c
if self.InputQueueSurrogate ~= 0 then
if bit32.band(c, 0xFC00) ~= 0xDC00 then -- Invalid low surrogate
self:AddInputCharacter(65533 --[[IM_UNICODE_CODEPOINT_INVALID]])
else
-- #if IM_UNICODE_CODEPOINT_MAX == 0xFFFF
cp = 65533 --[[IM_UNICODE_CODEPOINT_INVALID]] -- Codepoint will not fit in ImWchar
-- #endif
end
self.InputQueueSurrogate = 0
end
self:AddInputCharacter(cp)
end
--- @param str string
function MT.ImGuiIO:AddInputCharactersUTF8(str)
if not self.AppAcceptingEvents then
return
end
local p, str_end = 1, #str + 1
while p < str_end and string.byte(str, p) ~= 0 do
local len, c = ImStd.ImTextCharFromUtf8(str, p, str_end)
p = p + len
self:AddInputCharacter(c)
end
end
-- Clear all incoming events.
function MT.ImGuiIO:ClearEventsQueue()
assert(self.Ctx ~= nil)
local g = GImGui
g.InputEventsQueue:clear()
end
-- Clear current keyboard/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons.
function MT.ImGuiIO:ClearInputKeys()
local g = self.Ctx
for key = 512 --[[ImGuiKey.NamedKey_BEGIN]], 667 --[[ImGuiKey.NamedKey_END]] - 1 do
if not ImGui.IsMouseKey(key) then
local key_data = g.IO.KeysData[key - 512 --[[ImGuiKey.NamedKey_BEGIN]]]
key_data.Down = false
key_data.DownDuration = -1.0
key_data.DownDurationPrev = -1.0
end
end
self.KeyCtrl = false
self.KeyShift = false
self.KeyAlt = false
self.KeySuper = false
self.KeyMods = 0 --[[ImGuiMod_None]]
self.InputQueueCharacters:resize(0) -- Behavior of old ClearInputCharacters().
end
function MT.ImGuiIO:ClearInputMouse()
for key = 656 --[[ImGuiKey_Mouse_BEGIN]], 663 --[[ImGuiKey_Mouse_END]] - 1 do
local key_data = self.KeysData[key - 512 --[[ImGuiKey.NamedKey_BEGIN]]]
if key_data then
key_data.Down = false
key_data.DownDuration = -1.0
key_data.DownDurationPrev = -1.0
end
end
self.MousePos = ImVec2(-3.4028234663853e+38 --[[FLT_MAX]], -3.4028234663853e+38 --[[FLT_MAX]])
for n = 0, 2 do -- IM_COUNTOF(MouseDown)
self.MouseDown[n] = false
self.MouseDownDuration[n] = -1.0
self.MouseDownDurationPrev[n] = -1.0
end
self.MouseWheel = 0
self.MouseWheelH = 0
end
--- @param ctx ImGuiContext
--- @param type ImGuiInputEventType
--- @param arg? int
--- @return ImGuiInputEvent?
local function FindLatestInputEvent(ctx, type, arg)
if arg == nil then arg = -1 end
local g = ctx
for n = g.InputEventsQueue.Size, 1, -1 do
local e = g.InputEventsQueue.Data[n]
if e.Type ~= type then
elseif type == 5 --[[ImGuiInputEventType.Key]] and e.Key.Key ~= arg then
elseif type == 3 --[[ImGuiInputEventType.MouseButton]] and e.MouseButton.Button ~= arg then
else
return e
end
end
return nil
end
-- Queue a new key down/up event.
-- - ImGuiKey key: Translated key (as in, generally ImGuiKey.A matches the key end-user would use to emit an 'A' character)
-- - bool down: Is the key down? use false to signify a key release.
-- - float analog_value: 0.0f..1.0f
-- IMPORTANT: THIS FUNCTION AND OTHER "ADD" GRABS THE CONTEXT FROM OUR INSTANCE.
-- WE NEED TO ENSURE THAT ALL FUNCTION CALLS ARE FULFILLING THIS, WHICH IS WHY GetKeyData() HAS AN EXPLICIT CONTEXT.
--- @param key ImGuiKey
--- @param down bool
--- @param analog_value float
function MT.ImGuiIO:AddKeyAnalogEvent(key, down, analog_value)
-- if (e->Down) { IMGUI_DEBUG_LOG_IO("AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\n", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); }
assert(self.Ctx ~= nil)
if key == 0 --[[ImGuiKey.None]] or not self.AppAcceptingEvents then
return
end
local g = self.Ctx
assert(ImGui.IsNamedKeyOrMod(key)) -- Backend needs to pass a valid ImGuiKey constant. 0..511 values are legacy native key codes which are not accepted by this API.
assert(ImGui.IsAliasKey(key) == false) -- Backend cannot submit ImGuiKey.MouseXXX values they are automatically inferred from AddMouseXXX() events.
-- MacOS: swap Cmd(Super) and Ctrl
if (g.IO.ConfigMacOSXBehaviors) then
if (key == 32768 --[[ImGuiMod_Super]]) then key = 4096 --[[ImGuiMod_Ctrl]]
elseif (key == 4096 --[[ImGuiMod_Ctrl]]) then key = 32768 --[[ImGuiMod_Super]]
elseif (key == 530 --[[ImGuiKey.LeftSuper]]) then key = 527 --[[ImGuiKey.LeftCtrl]]
elseif (key == 534 --[[ImGuiKey.RightSuper]]) then key = 531 --[[ImGuiKey.RightCtrl]]
elseif (key == 527 --[[ImGuiKey.LeftCtrl]]) then key = 530 --[[ImGuiKey.LeftSuper]]
elseif (key == 531 --[[ImGuiKey.RightCtrl]]) then key = 534 --[[ImGuiKey.RightSuper]]
end
end
-- Filter duplicate (in particular: key mods and gamepad analog values are commonly spammed)
local latest_event = FindLatestInputEvent(g, 5 --[[ImGuiInputEventType.Key]], key)
local key_data = ImGui.GetKeyData(g, key)
local latest_key_down = latest_event and latest_event.Key.Down or key_data.Down
local latest_key_analog = latest_event and latest_event.Key.AnalogValue or key_data.AnalogValue
if latest_key_down == down and latest_key_analog == analog_value then
return
end
-- Add event
local e = ImGuiInputEvent()
e.Type = 5 --[[ImGuiInputEventType.Key]]
e.Source = ImGui.IsGamepadKey(key) and 3 --[[ImGuiInputSource.Gamepad]] or 2 --[[ImGuiInputSource.Keyboard]]
e.EventId = g.InputEventsNextEventId
g.InputEventsNextEventId = g.InputEventsNextEventId + 1
e.Key = ImGuiInputEventKey()
e.Key.Key = key
e.Key.Down = down
e.Key.AnalogValue = analog_value
g.InputEventsQueue:push_back(e)
end
--- @param key ImGuiKey
--- @param down bool
function MT.ImGuiIO:AddKeyEvent(key, down)
if not self.AppAcceptingEvents then
return
end
self:AddKeyAnalogEvent(key, down, (down and 1.0 or 0.0))
end
-- [Optional] Call after AddKeyEvent().
-- Specify native keycode, scancode + Specify index for legacy <1.87 IsKeyXXX() functions with native indices.
-- If you are writing a backend in 2022 or don't use IsKeyXXX() with native values that are not ImGuiKey values, you can avoid calling this.
--- @param key ImGuiKey
--- @param native_keycode int
--- @param native_scancode int
--- @param native_legacy_index? int
function MT.ImGuiIO:SetKeyEventNativeData(key, native_keycode, native_scancode, native_legacy_index)
if native_legacy_index == nil then native_legacy_index = -1 end
if key == 0 --[[ImGuiKey.None]] then
return
end
assert(ImGui.IsNamedKey(key)) -- >= 512
assert(native_legacy_index == -1 or (native_legacy_index >= 0 and native_legacy_index < 512)) -- [Luau] inline IsLegacyKey(): >= 0 && <= 511
-- Yet unused: key, native_keycode, native_scancode, native_legacy_index
end
-- Set master flag for accepting key/mouse/text events (default to true). Useful if you have native dialog boxes that are interrupting your application loop/refresh, and you want to disable events being queued while your app is frozen.
--- @param accepting_events bool
function MT.ImGuiIO:SetAppAcceptingEvents(accepting_events)
self.AppAcceptingEvents = accepting_events
end
-- This is not a real event, the data is latched in order to be stored in actual Mouse events.
-- This is so that duplicate events (e.g. Windows sending extraneous WM_MOUSEMOVE) gets filtered and are not leading to actual source changes.
--- @param source ImGuiMouseSource
function MT.ImGuiIO:AddMouseSourceEvent(source)
assert(self.Ctx ~= nil)
local g = self.Ctx
g.InputEventsNextMouseSource = source
end
--- @param viewport_id ImGuiID
function MT.ImGuiIO:AddMouseViewportEvent(viewport_id)
assert(self.Ctx ~= nil)
local g = self.Ctx
-- IM_ASSERT(g.IO.BackendFlags & ImGuiBackendFlags.HasMouseHoveredViewport)
if (not self.AppAcceptingEvents) then
return
end
-- Filter duplicate
local latest_event = FindLatestInputEvent(g, 4 --[[ImGuiInputEventType.MouseViewport]])
local latest_viewport_id
if latest_event then
latest_viewport_id = latest_event.MouseViewport.HoveredViewportID
else
latest_viewport_id = g.IO.MouseHoveredViewport
end
if (latest_viewport_id == viewport_id) then
return
end
local e = ImGuiInputEvent()
e.Type = 4 --[[ImGuiInputEventType.MouseViewport]]
e.Source = 1 --[[ImGuiInputSource.Mouse]]
e.MouseViewport = ImGuiInputEventMouseViewport()
e.MouseViewport.HoveredViewportID = viewport_id
g.InputEventsQueue:push_back(e)
end
--- @param focused bool
function MT.ImGuiIO:AddFocusEvent(focused)
assert(self.Ctx ~= nil)
local g = self.Ctx
-- Filter duplicate
local latest_event = FindLatestInputEvent(g, 7 --[[ImGuiInputEventType.Focus]])
local latest_focused = latest_event and latest_event.AppFocused.Focused or (not g.IO.AppFocusLost)
if latest_focused == focused or (self.ConfigDebugIgnoreFocusLoss and not focused) then
return
end
local e = ImGuiInputEvent()
e.Type = 7 --[[ImGuiInputEventType.Focus]]
e.EventId = g.InputEventsNextEventId
g.InputEventsNextEventId = g.InputEventsNextEventId + 1
e.AppFocused = ImGuiInputEventAppFocused()
e.AppFocused.Focused = focused
g.InputEventsQueue:push_back(e)
end
-- Queue a mouse move event
--- @param x float
--- @param y float
function MT.ImGuiIO:AddMousePosEvent(x, y)
assert(self.Ctx ~= nil)
if not self.AppAcceptingEvents then
return
end
local g = self.Ctx
-- Apply same flooring as UpdateMouseInputs()
local x_val = x
if x > -3.4028234663853e+38 --[[FLT_MAX]] then
x_val = ImFloor(x)
end
local y_val = y
if y > -3.4028234663853e+38 --[[FLT_MAX]] then
y_val = ImFloor(y)
end
-- Filter duplicate
local latest_event = FindLatestInputEvent(g, 1 --[[ImGuiInputEventType.MousePos]])
local latest_pos = latest_event and ImVec2(latest_event.MousePos.PosX, latest_event.MousePos.PosY) or self.MousePos
if latest_pos.x == x_val and latest_pos.y == y_val then
return
end
local e = ImGuiInputEvent()
e.Type = 1 --[[ImGuiInputEventType.MousePos]]
e.Source = 1 --[[ImGuiInputSource.Mouse]]
e.EventId = g.InputEventsNextEventId
g.InputEventsNextEventId = g.InputEventsNextEventId + 1
e.MousePos = ImGuiInputEventMousePos()
e.MousePos.PosX = x_val
e.MousePos.PosY = y_val
e.MousePos.MouseSource = g.InputEventsNextMouseSource
g.InputEventsQueue:push_back(e)
end
--- @param mouse_button ImGuiMouseButton
--- @param down bool
function MT.ImGuiIO:AddMouseButtonEvent(mouse_button, down)
assert(self.Ctx ~= nil)
local g = self.Ctx
assert(mouse_button >= 0 and mouse_button < 5 --[[ImGuiMouseButton.COUNT]])
if not self.AppAcceptingEvents then
return
end
-- On MacOS X: Convert Ctrl(Super)+Left click into Right-click: handle held button.
if self.ConfigMacOSXBehaviors and mouse_button == 0 and g.IO.MouseCtrlLeftAsRightClick then
-- Order of both statements matters: this event will still release mouse button 1
mouse_button = 1
if not down then
self.MouseCtrlLeftAsRightClick = false
end
end
-- Filter duplicate
local latest_event = FindLatestInputEvent(g, 3 --[[ImGuiInputEventType.MouseButton]], mouse_button)
local latest_button_down = latest_event and latest_event.MouseButton.Down or self.MouseDown[mouse_button]
if latest_button_down == down then
return
end
-- On MacOS X: Convert Ctrl(Super)+Left click into Right-click.
-- - Note that this is actual physical Ctrl which is ImGuiMod_Super for us.
-- - At this point we want from !down to down, so this is handling the initial press.
if self.ConfigMacOSXBehaviors and mouse_button == 0 and down then
local latest_super_event = FindLatestInputEvent(g, 5 --[[ImGuiInputEventType.Key]], 32768 --[[ImGuiMod_Super]])
if latest_super_event and latest_super_event.Key.Down or self.KeySuper then
-- IMGUI_DEBUG_LOG_IO("[io] Super+Left Click aliased into Right Click\n")
self.MouseCtrlLeftAsRightClick = true
self:AddMouseButtonEvent(1, true) -- This is just quicker to write that passing through, as we need to filter duplicate again.
return
end
end
local e = ImGuiInputEvent()
e.Type = 3 --[[ImGuiInputEventType.MouseButton]]
e.Source = 1 --[[ImGuiInputSource.Mouse]]
e.EventId = g.InputEventsNextEventId
g.InputEventsNextEventId = g.InputEventsNextEventId + 1
e.MouseButton = ImGuiInputEventMouseButton()
e.MouseButton.Button = mouse_button
e.MouseButton.Down = down
e.MouseButton.MouseSource = g.InputEventsNextMouseSource
g.InputEventsQueue:push_back(e)
end
-- Queue a mouse wheel event (some mouse/API may only have a Y component)
--- @param wheel_x float
--- @param wheel_y float
function MT.ImGuiIO:AddMouseWheelEvent(wheel_x, wheel_y)
assert(self.Ctx ~= nil)
local g = self.Ctx
-- Filter duplicate (unlike most events, wheel values are relative and easy to filter)
if not self.AppAcceptingEvents or (wheel_x == 0 and wheel_y == 0) then
return
end
local e = ImGuiInputEvent()
e.Type = 2 --[[ImGuiInputEventType.MouseWheel]]
e.Source = 1 --[[ImGuiInputSource.Mouse]]
e.EventId = g.InputEventsNextEventId
g.InputEventsNextEventId = g.InputEventsNextEventId + 1
e.MouseWheel = ImGuiInputEventMouseWheel()
e.MouseWheel.WheelX = wheel_x
e.MouseWheel.WheelY = wheel_y
e.MouseWheel.MouseSource = g.InputEventsNextMouseSource
g.InputEventsQueue:push_back(e)
end
-------------------------------------------------------------------------------
-- [SECTION] INPUTS
-------------------------------------------------------------------------------
-- - GetModForLRModKey() [Internal]
-- - FixupKeyChord() [Internal]
-- - GetKeyData() [Internal]
-- - GetKeyIndex() [Internal]
-- - GetKeyName()
-- - GetKeyChordName() [Internal]
-- - CalcTypematicRepeatAmount() [Internal]
-- - GetTypematicRepeatRate() [Internal]
-- - GetKeyPressedAmount() [Internal]
-- - GetKeyMagnitude2d() [Internal]
-------------------------------------------------------------------------------
-- - UpdateKeyRoutingTable() [Internal]
-- - GetRoutingIdFromOwnerId() [Internal]
-- - GetShortcutRoutingData() [Internal]
-- - CalcRoutingScore() [Internal]
-- - SetShortcutRouting() [Internal]
-- - TestShortcutRouting() [Internal]
-------------------------------------------------------------------------------
-- - IsKeyDown()
-- - IsKeyPressed()
-- - IsKeyReleased()
-------------------------------------------------------------------------------
-- - IsMouseDown()
-- - IsMouseClicked()
-- - IsMouseReleased()
-- - IsMouseDoubleClicked()
-- - GetMouseClickedCount()
-- - GetItemClickedCountWithSingleClickDelay()
-- - IsMouseHoveringRect() [Internal]
-- - IsMouseDragPastThreshold() [Internal]
-- - IsMouseDragging()
-- - GetMousePos()
-- - SetMousePos() [Internal]
-- - GetMousePosOnOpeningCurrentPopup()
-- - IsMousePosValid()
-- - IsAnyMouseDown()
-- - GetMouseDragDelta()
-- - ResetMouseDragDelta()
-- - GetMouseCursor()
-- - SetMouseCursor()
-------------------------------------------------------------------------------
-- - UpdateAliasKey()
-- - GetMergedModsFromKeys()
-- - UpdateKeyboardInputs()
-- - UpdateMouseInputs()
-------------------------------------------------------------------------------
-- - LockWheelingWindow [Internal]
-- - FindBestWheelingWindow [Internal]
-- - UpdateMouseWheel() [Internal]
-------------------------------------------------------------------------------
-- - SetNextFrameWantCaptureKeyboard()
-- - SetNextFrameWantCaptureMouse()
-------------------------------------------------------------------------------
-- - GetInputSourceName() [Internal]
-- - DebugPrintInputEvent() [Internal]
-- - UpdateInputEvents() [Internal]
-------------------------------------------------------------------------------
-- - GetKeyOwner() [Internal]
-- - TestKeyOwner() [Internal]
-- - SetKeyOwner() [Internal]
-- - SetItemKeyOwner() [Internal]
-- - Shortcut() [Internal]
-------------------------------------------------------------------------------
--- @param key ImGuiKey
local function GetModForLRModKey(key)
if key == 527 --[[ImGuiKey.LeftCtrl]] or key == 531 --[[ImGuiKey.RightCtrl]] then
return 4096 --[[ImGuiMod_Ctrl]]
end
if key == 528 --[[ImGuiKey.LeftShift]] or key == 532 --[[ImGuiKey.RightShift]] then
return 8192 --[[ImGuiMod_Shift]]
end
if key == 529 --[[ImGuiKey.LeftAlt]] or key == 533 --[[ImGuiKey.RightAlt]] then
return 16384 --[[ImGuiMod_Alt]]
end
if key == 530 --[[ImGuiKey.LeftSuper]] or key == 534 --[[ImGuiKey.RightSuper]] then
return 32768 --[[ImGuiMod_Super]]
end
return 0 --[[ImGuiMod_None]]
end
--- @param key_chord ImGuiKeyChord
function ImGui.FixupKeyChord(key_chord)
-- Add ImGuiMod_XXXX when a corresponding ImGuiKey.LeftXXX/ImGuiKey.RightXXX is specified.
local key = bit32.band(key_chord, bit32.bnot(61440 --[[ImGuiMod_Mask_]]))
if ImGui.IsLRModKey(key) then
key_chord = bit32.bor(key_chord, GetModForLRModKey(key))
end
return key_chord
end
--- @param ctx? ImGuiContext
--- @param key ImGuiKey
--- @return ImGuiKeyData, int
function ImGui.GetKeyData(ctx, key)
if ctx == nil then ctx = GImGui end
-- Special storage location for mods
if bit32.band(key, 61440 --[[ImGuiMod_Mask_]]) ~= 0 then
key = ImGui.ConvertSingleModFlagToKey(key)
end
assert(ImGui.IsNamedKey(key), "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code.")
local idx = key - 512 --[[ImGuiKey.NamedKey_BEGIN]]
return ctx.IO.KeysData[idx], idx
end
-- Return 2D vector representing the combination of four cardinal direction, with analog value support (for e.g. ImGuiKey.GamepadLStick* values).
--- @param key_left ImGuiKey
--- @param key_right ImGuiKey
--- @param key_up ImGuiKey
--- @param key_down ImGuiKey
function ImGui.GetKeyMagnitude2d(key_left, key_right, key_up, key_down)
return ImVec2(
ImGui.GetKeyData(nil, key_right).AnalogValue - ImGui.GetKeyData(nil, key_left).AnalogValue,
ImGui.GetKeyData(nil, key_down).AnalogValue - ImGui.GetKeyData(nil, key_up).AnalogValue
)
end
-- TestKeyOwner(..., ID) : (owner == None || owner == ID)
-- TestKeyOwner(..., None) : (owner == None)
-- TestKeyOwner(..., Any) : no owner test
-- All paths are also testing for key not being locked, for the rare cases that key have been locked with using ImGuiInputFlags.LockXXX flags.
--- @param key ImGuiKey
--- @param owner_id ImGuiID
function ImGui.TestKeyOwner(key, owner_id)
if not ImGui.IsNamedKeyOrMod(key) then
return true
end
local g = GImGui --- @cast g ImGuiContext
if g.ActiveIdUsingAllKeyboardKeys and owner_id ~= g.ActiveId and owner_id ~= 0 --[[ImGuiKeyOwner_Any]] then
if key >= 512 --[[ImGuiKey_Keyboard_BEGIN]] and key < 632 --[[ImGuiKey_Keyboard_END]] then
return false
end
end
local owner_data = ImGui.GetKeyOwnerData(g, key)
if owner_id == 0 --[[ImGuiKeyOwner_Any]] then
return not owner_data.LockThisFrame
end
-- Note: SetKeyOwner() sets OwnerCurr. It is not strictly required for most mouse routing overlap (because of ActiveId/HoveredId
-- are acting as filter before this has a chance to filter), but sane as soon as user tries to look into things.
-- Setting OwnerCurr in SetKeyOwner() is more consistent than testing OwnerNext here: would be inconsistent with getter and other functions.
if owner_data.OwnerCurr ~= owner_id then
if owner_data.LockThisFrame then
return false
end
if owner_data.OwnerCurr ~= 4294967295 --[[ImGuiKeyOwner_NoOwner]] then
return false
end
end
return true
end
-- Note that Dear ImGui doesn't know the meaning/semantic of ImGuiKey from 0..511: they are legacy native keycodes.
-- Consider transitioning from 'IsKeyDown(MY_ENGINE_KEY_A)' (<1.87) to IsKeyDown(ImGuiKey.A) (>= 1.87)
--- @param key ImGuiKey
--- @param owner_id? ImGuiID
function ImGui.IsKeyDown(key, owner_id)
if owner_id == nil then owner_id = 0 --[[ImGuiKeyOwner_Any]] end
local key_data = ImGui.GetKeyData(nil, key)
if not key_data.Down then
return false
end
if not ImGui.TestKeyOwner(key, owner_id) then
return false
end
return true
end
--- @param key ImGuiKey
--- @param is_repeat? bool
function ImGui.IsKeyPressed(key, is_repeat)
if is_repeat == nil then is_repeat = true end
return ImGui.IsKeyPressedEx(key, is_repeat and 1 --[[ImGuiInputFlags.Repeat]] or 0 --[[ImGuiInputFlags.None]], 0 --[[ImGuiKeyOwner_Any]])
end
--- @param key ImGuiKey
--- @param owner_id? ImGuiID
--- @return bool
function ImGui.IsKeyReleased(key, owner_id)
if owner_id == nil then owner_id = 0 --[[ImGuiKeyOwner_Any]] end
local key_data = ImGui.GetKeyData(nil, key)
if key_data.DownDurationPrev < 0.0 or key_data.Down then
return false
end
if not ImGui.TestKeyOwner(key, owner_id) then
return false
end
return true
end
-- Important: unlike legacy IsKeyPressed(ImGuiKey, bool repeat=true) which DEFAULT to repeat, this requires EXPLICIT repeat.
--- @param key ImGuiKey
--- @param flags ImGuiInputFlags
--- @param owner_id? ImGuiID
function ImGui.IsKeyPressedEx(key, flags, owner_id)
if owner_id == nil then owner_id = 0 end
local g = GImGui
local key_data = ImGui.GetKeyData(g, key)
if not key_data.Down then -- In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)
return false
end
local t = key_data.DownDuration
if t < 0.0 then
return false
end
assert(bit32.band(flags, bit32.bnot(255 --[[ImGuiInputFlags.SupportedByIsKeyPressed]])) == 0) -- Passing flags not supported by this function!
if bit32.band(flags, bit32.bor(14 --[[ImGuiInputFlags.RepeatRateMask_]], 240 --[[ImGuiInputFlags.RepeatUntilMask_]])) ~= 0 then -- Setting any _RepeatXXX option enables _Repeat
flags = bit32.bor(flags, 1 --[[ImGuiInputFlags.Repeat]])
end
local pressed = (t == 0.0)
if not pressed and bit32.band(flags, 1 --[[ImGuiInputFlags.Repeat]]) ~= 0 then
local repeat_delay, repeat_rate = ImGui.GetTypematicRepeatRate(flags)
pressed = (t > repeat_delay) and (ImGui.GetKeyPressedAmount(key, repeat_delay, repeat_rate) > 0)
if pressed and bit32.band(flags, 240 --[[ImGuiInputFlags.RepeatUntilMask_]]) ~= 0 then
-- Slightly bias 'key_pressed_time' as DownDuration is an accumulation of DeltaTime which we compare to an absolute time value.
-- Ideally we'd replace DownDuration with KeyPressedTime but it would break user's code.
local key_pressed_time = g.Time - t + 0.00001
if bit32.band(flags, 32 --[[ImGuiInputFlags.RepeatUntilKeyModsChange]]) ~= 0 and g.LastKeyModsChangeTime > key_pressed_time then
pressed = false
end
if bit32.band(flags, 64 --[[ImGuiInputFlags.RepeatUntilKeyModsChangeFromNone]]) ~= 0 and g.LastKeyModsChangeFromNoneTime > key_pressed_time then
pressed = false
end
if bit32.band(flags, 128 --[[ImGuiInputFlags.RepeatUntilOtherKeyPress]]) ~= 0 and g.LastKeyboardKeyPressTime > key_pressed_time then
pressed = false
end
end
end
if not pressed then
return false
end
if not ImGui.TestKeyOwner(key, owner_id) then
return false
end
return true
end
-- TODO:
-- Return untranslated names: on macOS, Cmd key will show as Ctrl, Ctrl key will show as super.
function ImGui.GetKeyChordName(key_chord)
local key = bit32.band(key_chord, bit32.bnot(61440 --[[ImGuiMod_Mask_]]))
if ImGui.IsLRModKey(key) then
key_chord = bit32.band(key_chord, bit32.bnot(GetModForLRModKey(key))) -- Return "Ctrl+LeftShift" instead of "Ctrl+Shift+LeftShift"
end
local s = ((bit32.band(key_chord, 4096 --[[ImGuiMod_Ctrl]]) ~= 0) and "Ctrl+" or "")
.. ((bit32.band(key_chord, 8192 --[[ImGuiMod_Shift]]) ~= 0) and "Shift+" or "")
.. ((bit32.band(key_chord, 16384 --[[ImGuiMod_Alt]]) ~= 0) and "Alt+" or "")
.. ((bit32.band(key_chord, 32768 --[[ImGuiMod_Super]]) ~= 0) and "Super+" or "")
.. ((key ~= 0 --[[ImGuiKey.None]] or key_chord == 0 --[[ImGuiKey.None]]) and ImGui.GetKeyName(key) or "")
if key == 0 --[[ImGuiKey.None]] and key_chord ~= 0 and #s > 0 then
s = string.sub(s, 1, #s - 1) -- Remove trailing '+'
end
return s
end
-- Those names are provided for debugging purpose and are not meant to be saved persistently nor compared.
local GKeyNames = {
"Tab", "LeftArrow", "RightArrow", "UpArrow", "DownArrow", "PageUp", "PageDown",
"Home", "End", "Insert", "Delete", "Backspace", "Space", "Enter", "Escape",
"LeftCtrl", "LeftShift", "LeftAlt", "LeftSuper", "RightCtrl", "RightShift", "RightAlt", "RightSuper", "Menu",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H",
"I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
"F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", "F24",
"Apostrophe", "Comma", "Minus", "Period", "Slash", "Semicolon", "Equal", "LeftBracket",
"Backslash", "RightBracket", "GraveAccent", "CapsLock", "ScrollLock", "NumLock", "PrintScreen",
"Pause", "Keypad0", "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6",
"Keypad7", "Keypad8", "Keypad9", "KeypadDecimal", "KeypadDivide", "KeypadMultiply",
"KeypadSubtract", "KeypadAdd", "KeypadEnter", "KeypadEqual",
"AppBack", "AppForward", "Oem102",
"GamepadStart", "GamepadBack",
"GamepadFaceLeft", "GamepadFaceRight", "GamepadFaceUp", "GamepadFaceDown",
"GamepadDpadLeft", "GamepadDpadRight", "GamepadDpadUp", "GamepadDpadDown",
"GamepadL1", "GamepadR1", "GamepadL2", "GamepadR2", "GamepadL3", "GamepadR3",
"GamepadLStickLeft", "GamepadLStickRight", "GamepadLStickUp", "GamepadLStickDown",
"GamepadRStickLeft", "GamepadRStickRight", "GamepadRStickUp", "GamepadRStickDown",
"MouseLeft", "MouseRight", "MouseMiddle", "MouseX1", "MouseX2", "MouseWheelX", "MouseWheelY",
"ModCtrl", "ModShift", "ModAlt", "ModSuper", -- ReservedForModXXX are showing the ModXXX names.
}
--- @param key ImGuiKey
--- @return string
function ImGui.GetKeyName(key)
if key == 0 --[[ImGuiKey.None]] then
return "None"
end
assert(ImGui.IsNamedKeyOrMod(key), "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code.")
if bit32.band(key, 61440 --[[ImGuiMod_Mask_]]) ~= 0 then
key = ImGui.ConvertSingleModFlagToKey(key)
end
if not ImGui.IsNamedKey(key) then
return "Unknown"
end
return GKeyNames[key - 512 --[[ImGuiKey.NamedKey_BEGIN]] + 1] or "Unknown"
end
--- @param key ImGuiKey
--- @return ImGuiID
function ImGui.GetKeyOwner(key)
if not ImGui.IsNamedKeyOrMod(key) then
return 4294967295 --[[ImGuiKeyOwner_NoOwner]]
end
local g = GImGui
local owner_data = ImGui.GetKeyOwnerData(g, key)
local owner_id = owner_data.OwnerCurr
if g.ActiveIdUsingAllKeyboardKeys and owner_id ~= g.ActiveId and owner_id ~= 0 --[[ImGuiKeyOwner_Any]] then
if key >= 512 --[[ImGuiKey_Keyboard_BEGIN]] and key < 632 --[[ImGuiKey_Keyboard_END]] then
return 4294967295 --[[ImGuiKeyOwner_NoOwner]]
end
end
return owner_id
end
-- t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
-- t1 = current time (e.g.: g.Time)
-- An event is triggered at:
-- t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N
--- @param t0 float
--- @param t1 float
--- @param repeat_delay float
--- @param repeat_rate float
function ImGui.CalcTypematicRepeatAmount(t0, t1, repeat_delay, repeat_rate)
if t1 == 0.0 then return 1 end
if t0 >= t1 then return 0 end
if repeat_rate <= 0.0 then
if t0 < repeat_delay and t1 >= repeat_delay then
return 1
else
return 0
end
end
local count_t0
if t0 < repeat_delay then
count_t0 = -1
else
count_t0 = math.floor((t0 - repeat_delay) / repeat_rate)
end
local count_t1
if t1 < repeat_delay then
count_t1 = -1
else
count_t1 = math.floor((t1 - repeat_delay) / repeat_rate)
end
return count_t1 - count_t0
end
--- @param flags ImGuiInputFlags
--- @return float repeat_delay
--- @return float repeat_rate
function ImGui.GetTypematicRepeatRate(flags)
local g = GImGui
flags = bit32.band(flags, 14 --[[ImGuiInputFlags.RepeatRateMask_]])
if flags == 4 --[[ImGuiInputFlags.RepeatRateNavMove]] then repeat_delay = g.IO.KeyRepeatDelay * 0.72; repeat_rate = g.IO.KeyRepeatRate * 0.80; return repeat_delay, repeat_rate
elseif flags == 8 --[[ImGuiInputFlags.RepeatRateNavTweak]] then repeat_delay = g.IO.KeyRepeatDelay * 0.72; repeat_rate = g.IO.KeyRepeatRate * 0.30; return repeat_delay, repeat_rate
else repeat_delay = g.IO.KeyRepeatDelay * 1.00; repeat_rate = g.IO.KeyRepeatRate * 1.00; return repeat_delay, repeat_rate end
end
-- Return value representing the number of presses in the last time period, for the given repeat rate
-- (most often returns 0 or 1. The result is generally only >1 when RepeatRate is smaller than DeltaTime, aka large DeltaTime or fast RepeatRate)
--- @param key ImGuiKey
--- @param repeat_delay float
--- @param repeat_rate float
function ImGui.GetKeyPressedAmount(key, repeat_delay, repeat_rate)
local g = GImGui
local key_data = ImGui.GetKeyData(g, key)
if not key_data.Down then -- In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)
return 0
end
local t = key_data.DownDuration
return ImGui.CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate)
end
-- Rewrite routing data buffers to strip old entries + sort by key to make queries not touch scattered data.
-- Entries D,A,B,B,A,C,B --> A,A,B,B,B,C,D
-- Index A:1 B:2 C:5 D:0 --> A:0 B:2 C:5 D:6
-- See 'Metrics->Key Owners & Shortcut Routing' to visualize the result of that operation.
--- @param rt ImGuiKeyRoutingTable
function ImGui.UpdateKeyRoutingTable(rt)
local g = GImGui --[[@as ImGuiContext]]
rt.EntriesNext:resize(0)
for key = 512 --[[ImGuiKey.NamedKey_BEGIN]], 667 --[[ImGuiKey.NamedKey_END]] - 1 do
local new_routing_start_idx = rt.EntriesNext.Size + 1
local routing_entry
local old_routing_idx = rt.Index[key - 512 --[[ImGuiKey.NamedKey_BEGIN]] + 1]
while old_routing_idx ~= -1 do
repeat
routing_entry = rt.Entries[old_routing_idx]
routing_entry.RoutingCurrScore = routing_entry.RoutingNextScore
routing_entry.RoutingCurr = routing_entry.RoutingNext -- Update entry
routing_entry.RoutingNext = 4294967295 --[[ImGuiKeyOwner_NoOwner]]
routing_entry.RoutingNextScore = 0
if routing_entry.RoutingCurr == 4294967295 --[[ImGuiKeyOwner_NoOwner]] then
break
end
rt.EntriesNext:push_back(routing_entry) -- Write alive ones into new buffer
-- Apply routing to owner if there's no owner already (RoutingCurr == None at this point)
-- This is the result of previous frame's SetShortcutRouting() call.
if routing_entry.Mods == g.IO.KeyMods then
local owner_data = ImGui.GetKeyOwnerData(g, key)
if owner_data.OwnerCurr == 4294967295 --[[ImGuiKeyOwner_NoOwner]] then
owner_data.OwnerCurr = routing_entry.RoutingCurr
-- IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X) via Routing\n", GetKeyName(key), routing_entry->RoutingCurr);
end
end
until true
old_routing_idx = routing_entry.NextEntryIndex
end
-- Rewrite linked-list
rt.Index[key - 512 --[[ImGuiKey.NamedKey_BEGIN]] + 1] = (new_routing_start_idx <= rt.EntriesNext.Size and new_routing_start_idx or -1)
for n = new_routing_start_idx, rt.EntriesNext.Size do
rt.EntriesNext[n].NextEntryIndex = (n + 1 <= rt.EntriesNext.Size) and (n + 1) or -1
end
end
rt.Entries:swap(rt.EntriesNext) -- Swap new and old indexes
end
-- owner_id may be None/Any, but routing_id needs to be always be set, so we default to GetCurrentFocusScope().
--- @param owner_id ImGuiID
local function GetRoutingIdFromOwnerId(owner_id)
local g = GImGui
return (owner_id ~= 4294967295 --[[ImGuiKeyOwner_NoOwner]] and owner_id ~= 0 --[[ImGuiKeyOwner_Any]]) and owner_id or g.CurrentFocusScopeId
end
--- @param key_chord ImGuiKeyChord
--- @return ImGuiKeyRoutingData
function ImGui.GetShortcutRoutingData(key_chord)
-- Majority of shortcuts will be Key + any number of Mods
-- We accept _Single_ mod with ImGuiKey.None.
-- - Shortcut(ImGuiKey.S | ImGuiMod_Ctrl) -- Legal
-- - Shortcut(ImGuiKey.S | ImGuiMod_Ctrl | ImGuiMod_Shift) -- Legal
-- - Shortcut(ImGuiMod_Ctrl) -- Legal
-- - Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift) -- Not legal
local g = GImGui
local rt = g.KeysRoutingTable
local key = bit32.band(key_chord, bit32.bnot(61440 --[[ImGuiMod_Mask_]]))
local mods = bit32.band(key_chord, 61440 --[[ImGuiMod_Mask_]])
if key == 0 --[[ImGuiKey.None]] then
key = ImGui.ConvertSingleModFlagToKey(mods)
end
assert(ImGui.IsNamedKey(key))
-- Get (in the majority of case, the linked list will have one element so this should be 2 reads.
-- Subsequent elements will be contiguous in memory as list is sorted/rebuilt in NewFrame).
local routing_data
local idx = rt.Index[key - 512 --[[ImGuiKey.NamedKey_BEGIN]] + 1]
while idx ~= -1 do
routing_data = rt.Entries.Data[idx]
if routing_data.Mods == mods then
return routing_data
end
idx = routing_data.NextEntryIndex
end
-- Add to linked-list
local routing_data_idx = rt.Entries.Size + 1
rt.Entries:push_back(ImGuiKeyRoutingData())
routing_data = rt.Entries[routing_data_idx]
routing_data.Mods = mods
routing_data.NextEntryIndex = rt.Index[key - 512 --[[ImGuiKey.NamedKey_BEGIN]] + 1] -- Setup linked list
rt.Index[key - 512 --[[ImGuiKey.NamedKey_BEGIN]] + 1] = routing_data_idx
return routing_data
end
-- Current score encoding
-- - 0: Never route
-- - 1: ImGuiInputFlags.RouteGlobal (lower priority)
-- - 100..199: ImGuiInputFlags.RouteFocused (if window in focus-stack)
-- 200: ImGuiInputFlags.RouteGlobal | ImGuiInputFlags.RouteOverFocused
-- 300: ImGuiInputFlags.RouteActive or ImGuiInputFlags.RouteFocused (if item active)
-- 400: ImGuiInputFlags.RouteGlobal | ImGuiInputFlags.RouteOverActive
-- - 500..599: ImGuiInputFlags.RouteFocused | ImGuiInputFlags.RouteOverActive (if window in focus-stack) (higher priority)
-- 'flags' should include an explicit routing policy
--- @param focus_scope_id ImGuiID
--- @param owner_id ImGuiID
--- @param flags ImGuiInputFlags
local function CalcRoutingScore(focus_scope_id, owner_id, flags)
local g = GImGui
if bit32.band(flags, 2048 --[[ImGuiInputFlags.RouteFocused]]) ~= 0 then
-- ActiveID gets high priority
-- (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it)
if owner_id ~= 0 and g.ActiveId == owner_id then
return 300
end
-- Score based on distance to focused window (lower is better)
-- Assuming both windows are submitting a routing request,
-- - When Window....... is focused -> Window scores 3 (best), Window/ChildB scores 255 (no match)
-- - When Window/ChildB is focused -> Window scores 4, Window/ChildB scores 3 (best)
-- Assuming only WindowA is submitting a routing request,
-- - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score.
-- This essentially follow the window.ParentWindowForFocusRoute chain.
if focus_scope_id == 0 then
return 0
end
for index_in_focus_path = 1, g.NavFocusRoute.Size do
if g.NavFocusRoute.Data[index_in_focus_path].ID == focus_scope_id then
if bit32.band(flags, 32768 --[[ImGuiInputFlags.RouteOverActive]]) ~= 0 then -- && g.ActiveId != 0 && g.ActiveId != owner_id)
return 599 - (index_in_focus_path - 1)
else
return 199 - (index_in_focus_path - 1)
end
end
end
return 0
elseif bit32.band(flags, 1024 --[[ImGuiInputFlags.RouteActive]]) ~= 0 then
if owner_id ~= 0 and g.ActiveId == owner_id then
return 300
end
return 0
elseif bit32.band(flags, 4096 --[[ImGuiInputFlags.RouteGlobal]]) ~= 0 then
if bit32.band(flags, 32768 --[[ImGuiInputFlags.RouteOverActive]]) ~= 0 then
return 400
end
if owner_id ~= 0 and g.ActiveId == owner_id then
return 300
end
if bit32.band(flags, 16384 --[[ImGuiInputFlags.RouteOverFocused]]) ~= 0 then
return 200
end
return 1
end
assert(false)
return 0
end
-- - We need this to filter some Shortcut() routes when an item e.g. an InputText() is active
-- e.g. ImGuiKey.G won't be considered a shortcut when item is active, but ImGuiMod|ImGuiKey.G can be.
-- - This is also used by UpdateInputEvents() to avoid trickling in the most common case of e.g. pressing ImGuiKey.G also emitting a G character.
--- @param key_chord ImGuiKeyChord
local function IsKeyChordPotentiallyCharInput(key_chord)
-- Mimic 'ignore_char_inputs' logic in InputText()
local g = GImGui
-- When the right mods are pressed it cannot be a char input so we won't filter the shortcut out.
local mods = bit32.band(key_chord, 61440 --[[ImGuiMod_Mask_]])
local ignore_char_inputs = ((bit32.band(mods, 4096 --[[ImGuiMod_Ctrl]]) ~= 0) and (bit32.band(mods, 16384 --[[ImGuiMod_Alt]]) == 0)) or (g.IO.ConfigMacOSXBehaviors and (bit32.band(mods, 4096 --[[ImGuiMod_Ctrl]]) ~= 0))
if ignore_char_inputs then
return false
end
-- Return true for A-Z, 0-9 and other keys associated to char inputs. Other keys such as F1-F12 won't be filtered.
local key = bit32.band(key_chord, bit32.bnot(61440 --[[ImGuiMod_Mask_]]))
if key == 0 --[[ImGuiKey.None]] then
return false
end
return g.KeysMayBeCharInput:TestBit(key + 1) -- TestBit requires 1-based index
end
-- Request a desired route for an input chord (key + mods).
-- Return true if the route is available this frame.
-- - Routes and key ownership are attributed at the beginning of next frame based on best score and mod state.
-- (Conceptually this does a "Submit for next frame" + "Test for current frame".
-- As such, it could be called TrySetXXX or SubmitXXX, or the Submit and Test operations should be separate.)
--- @param key_chord ImGuiKeyChord
--- @param flags ImGuiInputFlags
--- @param owner_id ImGuiID
function ImGui.SetShortcutRouting(key_chord, flags, owner_id)
local g = GImGui
if bit32.band(flags, 15360 --[[ImGuiInputFlags.RouteTypeMask_]]) == 0 then
flags = bit32.bor(flags, 4096 --[[ImGuiInputFlags.RouteGlobal]], 16384 --[[ImGuiInputFlags.RouteOverFocused]], 32768 --[[ImGuiInputFlags.RouteOverActive]]) -- IMPORTANT: This is the default for SetShortcutRouting() but NOT Shortcut()
else
assert(ImIsPowerOfTwo(bit32.band(flags, 15360 --[[ImGuiInputFlags.RouteTypeMask_]]))) -- Check that only 1 routing flag is used
end
assert(owner_id ~= 0 --[[ImGuiKeyOwner_Any]] and owner_id ~= 4294967295 --[[ImGuiKeyOwner_NoOwner]])
if bit32.band(flags, bit32.bor(16384 --[[ImGuiInputFlags.RouteOverFocused]], 65536 --[[ImGuiInputFlags.RouteUnlessBgFocused]])) ~= 0 then
assert(bit32.band(flags, 4096 --[[ImGuiInputFlags.RouteGlobal]]) ~= 0)
end
if bit32.band(flags, 32768 --[[ImGuiInputFlags.RouteOverActive]]) ~= 0 then
assert(bit32.band(flags, bit32.bor(4096 --[[ImGuiInputFlags.RouteGlobal]], 2048 --[[ImGuiInputFlags.RouteFocused]])) ~= 0)
end
-- Add ImGuiMod_XXXX when a corresponding ImGuiKey.LeftXXX/ImGuiKey.RightXXX is specified.
key_chord = ImGui.FixupKeyChord(key_chord)
-- [DEBUG] Debug break requested by user
if g.DebugBreakInShortcutRouting == key_chord then
IM_DEBUG_BREAK()
end
if bit32.band(flags, 65536 --[[ImGuiInputFlags.RouteUnlessBgFocused]]) ~= 0 then
if g.NavWindow == nil then
return false
end
end
-- Note how ImGuiInputFlags.RouteAlways won't set routing and thus won't set owner. May want to rework this?
if bit32.band(flags, 8192 --[[ImGuiInputFlags.RouteAlways]]) ~= 0 then
-- IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> always, no register", ImGui.GetKeyChordName(key_chord), flags, owner_id)
return true
end
-- Specific culling when there's an active item.
if g.ActiveId ~= 0 and g.ActiveId ~= owner_id then
if bit32.band(flags, 1024 --[[ImGuiInputFlags.RouteActive]]) ~= 0 then
return false
end
-- Cull shortcuts with no modifiers when it could generate a character.
-- e.g. Shortcut(ImGuiKey.G) also generates 'g' character, should not trigger when InputText() is active.
-- but Shortcut(Ctrl+G) should generally trigger when InputText() is active.
-- TL;DR: lettered shortcut with no mods or with only Alt mod will not trigger while an item reading text input is active.
-- (We cannot filter based on io.InputQueueCharacters[] contents because of trickling and key<>chars submission order are undefined)
if g.IO.WantTextInput and IsKeyChordPotentiallyCharInput(key_chord) then
-- IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> filtered as potential char input", ImGui.GetKeyChordName(key_chord), flags, owner_id)
return false
end
-- ActiveIdUsingAllKeyboardKeys trumps all for ActiveId
if bit32.band(flags, 32768 --[[ImGuiInputFlags.RouteOverActive]]) == 0 and g.ActiveIdUsingAllKeyboardKeys then
local key = bit32.band(key_chord, bit32.bnot(61440 --[[ImGuiMod_Mask_]]))
if key == 0 --[[ImGuiKey.None]] then
key = ImGui.ConvertSingleModFlagToKey(bit32.band(key_chord, 61440 --[[ImGuiMod_Mask_]]))
end
if key >= 512 --[[ImGuiKey_Keyboard_BEGIN]] and key < 632 --[[ImGuiKey_Keyboard_END]] then
return false
end
end
end
-- Where do we evaluate route for?
local focus_scope_id = g.CurrentFocusScopeId
if bit32.band(flags, 131072 --[[ImGuiInputFlags.RouteFromRootWindow]]) ~= 0 then
focus_scope_id = g.CurrentWindow.RootWindow.ID -- See PushFocusScope() call in Begin()
end
local score = CalcRoutingScore(focus_scope_id, owner_id, flags)
-- IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> score %d", ImGui.GetKeyChordName(key_chord), flags, owner_id, score)
if score == 0 then
return false
end
-- Submit routing for NEXT frame (assuming score is sufficient)
-- FIXME: Could expose a way to use a "serve last" policy for same score resolution (using >= instead of >).
local routing_data = ImGui.GetShortcutRoutingData(key_chord)
-- const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score >= routing_data->RoutingNextScore) : (score > routing_data->RoutingNextScore);
if score > routing_data.RoutingNextScore then
routing_data.RoutingNext = owner_id
routing_data.RoutingNextScore = score
end
-- Return routing state for CURRENT frame
if routing_data.RoutingCurr == owner_id then
-- IMGUI_DEBUG_LOG_INPUTROUTING("--> granting current route")
end
return routing_data.RoutingCurr == owner_id
end
-- Currently unused by core (but used by tests)
-- Note: this cannot be turned into GetShortcutRouting() because we do the owner_id->routing_id translation, name would be more misleading.
--- @param key_chord ImGuiKeyChord
--- @param owner_id ImGuiID
--- @return bool
function ImGui.TestShortcutRouting(key_chord, owner_id)
local routing_id = GetRoutingIdFromOwnerId(owner_id)
key_chord = ImGui.FixupKeyChord(key_chord)
local routing_data = ImGui.GetShortcutRoutingData(key_chord) -- FIXME: Could avoid creating entry.
return routing_data.RoutingCurr == routing_id
end
-- We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
--- @param mouse_pos? ImVec2
function ImGui.IsMousePosValid(mouse_pos)
local MOUSE_INVALID = -256000.0
local p
if mouse_pos then p = mouse_pos else p = GImGui.IO.MousePos end
return p.x >= MOUSE_INVALID and p.y >= MOUSE_INVALID
end
-- [WILL OBSOLETE] This was designed for backends, but prefer having backend maintain a mask of held mouse buttons, because upcoming input queue system will make this invalid.
--- @return bool
function ImGui.IsAnyMouseDown()
local g = GImGui
for n = 0, 2 do -- IM_COUNTOF(g.IO.MouseDown) - 1
if g.IO.MouseDown[n] then
return true
end
end
return false
end
--- @param button ImGuiMouseButton
--- @param owner_id? ImGuiID
function ImGui.IsMouseDown(button, owner_id)
if owner_id == nil then owner_id = 0 --[[ImGuiKeyOwner_Any]] end
local g = GImGui
assert(button >= 0 and button < 3) -- IM_COUNTOF(g.IO.MouseDown)
return g.IO.MouseDown[button] and ImGui.TestKeyOwner(ImGui.MouseButtonToKey(button), owner_id) -- Should be same as IsKeyDown(MouseButtonToKey(button), owner_id), but this allows legacy code hijacking the io.Mousedown[] array.
end
--- @param button ImGuiMouseButton
--- @param is_repeat? bool
function ImGui.IsMouseClicked(button, is_repeat)
if is_repeat == nil then is_repeat = false end
return ImGui.IsMouseClickedEx(button, is_repeat and 1 --[[ImGuiInputFlags.Repeat]] or 0 --[[ImGuiInputFlags.None]], 0 --[[ImGuiKeyOwner_Any]])
end
--- @param button ImGuiMouseButton
--- @param flags ImGuiInputFlags
--- @param owner_id? ImGuiID
@native
function ImGui.IsMouseClickedEx(button, flags, owner_id)
if owner_id == nil then owner_id = 0 --[[ImGuiKeyOwner_Any]] end
local g = GImGui
assert(button >= 0 and button < 3) -- IM_COUNTOF(g.IO.MouseDown)
if not g.IO.MouseDown[button] then -- In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)
return false
end
local t = g.IO.MouseDownDuration[button]
if t < 0.0 then
return false
end
assert(bit32.band(flags, bit32.bnot(1 --[[ImGuiInputFlags.SupportedByIsMouseClicked]])) == 0) -- Passing flags not supported by this function! -- FIXME: Could support RepeatRate and RepeatUntil flags here.
local is_repeat = (bit32.band(flags, 1 --[[ImGuiInputFlags.Repeat]]) ~= 0)
local pressed = (t == 0.0) or (is_repeat and t > g.IO.KeyRepeatDelay and ImGui.CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0)
if not pressed then
return false
end
if not ImGui.TestKeyOwner(ImGui.MouseButtonToKey(button), owner_id) then
return false
end
return true
end
--- @param button ImGuiMouseButton
--- @param owner_id? ImGuiID
function ImGui.IsMouseReleased(button, owner_id)
if owner_id == nil then owner_id = 0 --[[ImGuiKeyOwner_Any]] end
local g = GImGui
assert(button >= 0 and button < 3) -- IM_COUNTOF(g.IO.MouseDown)
return g.IO.MouseReleased[button] and ImGui.TestKeyOwner(ImGui.MouseButtonToKey(button), owner_id) -- Should be same as IsKeyReleased(MouseButtonToKey(button), owner_id)
end
-- Prefer higher-level helper GetItemClickedCountWithSingleClickDelay()
-- Use if you absolutely need to distinguish single-click from double-click by introducing a delay.
-- Generally use with 'delay >= io.MouseDoubleClickTime' + combined with a 'io.MouseClickedLastCount == 1' test.
-- This is a very rarely used UI idiom, but some apps use this: e.g. MS Explorer single click on an icon to rename.
--- @param button ImGuiMouseButton
--- @param delay? float
function ImGui.IsMouseReleasedWithDelay(button, delay)
if delay == nil then delay = -1.0 end
local g = GImGui
assert(button >= 0 and button < 3)
if ImGui.IsMouseDown(button) then
return false
end
if delay < 0.0 then
delay = g.IO.MouseSingleClickDelay or 0.50
end
local time_since_release = g.Time - g.IO.MouseReleasedTime[button]
return (time_since_release - g.IO.DeltaTime < delay) and (time_since_release >= delay)
end
--- @param button ImGuiMouseButton
--- @param owner_id? ImGuiID
function ImGui.IsMouseDoubleClicked(button, owner_id)
if owner_id == nil then owner_id = 0 --[[ImGuiKeyOwner_Any]] end
local g = GImGui
assert(button >= 0 and button < 3)
return g.IO.MouseClickedCount[button] == 2 and ImGui.TestKeyOwner(ImGui.MouseButtonToKey(button), owner_id)
end
--- @param button ImGuiMouseButton
function ImGui.GetMouseClickedCount(button)
local g = GImGui
assert(button >= 0 and button < 3)
return g.IO.MouseClickedCount[button]
end
-- FIXME: This is close to what BeginDragDropSource() is doing, maybe rework.
--- @param mouse_button ImGuiMouseButton
local function LastItemOverlayButtonForNullId(mouse_button)
local g = GImGui
assert(g.LastItemData.ID == 0)
local window = g.CurrentWindow
local id = window:GetIDFromRectangle(g.LastItemData.Rect)
if g.IO.MouseClicked[mouse_button] and ImGui.ItemHoverable(g.LastItemData.Rect, id, g.LastItemData.ItemFlags) then
ImGui.SetActiveID(id, window)
ImGui.FocusWindow(window)
elseif g.ActiveId == id then
ImGui.KeepAliveID(id)
if not g.IO.MouseDown[mouse_button] then
ImGui.ClearActiveID()
end
end
return id
end
-- [BETA] Building block for disambiguation between single-click and double-click.
-- - Returns 1 on single-click but delayed by io.MouseSingleClickDelay (which is always > io.MouseDoubleClickTime) after mouse release.
-- - Returns 2+ on double-click and subsequent repeated clicks.
-- In order use that to replicate Windows Explorer's "click on label to rename after a delay",
-- When the function returns 1 for a delayed single click, you can add with further tests:
-- - If you want to test that the mouse position AT THE TIME of the click (before the delay): you can use the 'io.MouseClickedPos[mouse_button]' position.
-- - If you want to test that the mouse position is still over the item at the end of delay: you can use '&& IsItemHovered()'.
-- - If you want to test that the item was selected or the sole selection AT THE TIME of the click (before the delay): you can test for 'g.LastActiveIdWasSelected' or 'g.LastActiveIdWasSoleSelected'.
-- e.g.
-- local click_count = ImGui.GetItemClickedCountWithSingleClickDelay(mouse_button)
-- if click_count == 1 and ImGui.GetCurrentContext().LastActiveIdWasSoleSelected then
-- StartRename()
-- end
-- if click_count == 2 then
-- Launch()
-- end
--- @param mouse_button? ImGuiMouseButton
--- @param delay? float
--- @return int
function ImGui.GetItemClickedCountWithSingleClickDelay(mouse_button, delay)
if mouse_button == nil then mouse_button = 0 end
if delay == nil then delay = -1.0 end
-- Action: double-click and subsequent clicks
local g = GImGui
if g.IO.MouseClickedCount[mouse_button] >= 2 and ImGui.IsItemClicked(mouse_button) then
return g.IO.MouseClickedCount[mouse_button]
end
-- Action: second click, delayed
local id = g.LastItemData.ID
if id == 0 then
id = LastItemOverlayButtonForNullId(mouse_button)
end
if g.LastActiveId == id then
if delay >= 0.0 then
delay = ImMax(delay, g.IO.MouseDoubleClickTime + 0.01)
end
if ImGui.IsMouseReleasedWithDelay(mouse_button, delay) and g.IO.MouseClickedLastCount[mouse_button] == 1 then
return 1
end
end
return 0
end
--- @return ImVec2 # Ref
function ImGui.GetMousePos()
return GImGui.IO.MousePos
end
--- @return ImVec2
function ImGui.GetMousePosOnOpeningCurrentPopup()
local g = GImGui
if g.BeginPopupStack.Size > 0 then
return g.BeginPopupStack.Data[g.BeginPopupStack.Size].OpenMousePos
end
return g.IO.MousePos
end
-- Return the delta from the initial clicking position while the mouse button is clicked or was just released.
-- This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
-- NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window.
--- @param button? ImGuiMouseButton
--- @param lock_threshold? float
--- @return ImVec2
function ImGui.GetMouseDragDelta(button, lock_threshold)
if button == nil then button = 0 end
if lock_threshold == nil then lock_threshold = -1.0 end
local g = GImGui
assert(button >= 0 and button < 3)
if lock_threshold < 0.0 then
lock_threshold = g.IO.MouseDragThreshold
end
if g.IO.MouseDown[button] or g.IO.MouseReleased[button] then
if g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold then
if ImGui.IsMousePosValid(g.IO.MousePos) and ImGui.IsMousePosValid(g.IO.MouseClickedPos[button]) then
return g.IO.MousePos - g.IO.MouseClickedPos[button]
end
end
end
return ImVec2(0.0, 0.0)
end
--- @param button? ImGuiMouseButton
function ImGui.ResetMouseDragDelta(button)
if button == nil then button = 0 end
local g = GImGui
assert(button >= 0 and button < 3)
-- NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
ImVec2_Copy(g.IO.MouseClickedPos[button], g.IO.MousePos)
end
--- @param source ImGuiInputSource
--- @return string
local function GetInputSourceName(source)
local input_source_names = { "None", "Mouse", "Keyboard", "Gamepad" }
assert(#input_source_names == 4 --[[ImGuiInputSource.COUNT]])
if source < 0 or source >= 4 --[[ImGuiInputSource.COUNT]] then
return "Unknown"
end
return input_source_names[source + 1]
end
--- @param source ImGuiMouseSource
--- @return string
local function GetMouseSourceName(source)
local mouse_source_names = { "Mouse", "TouchScreen", "Pen" }
assert(#mouse_source_names == 3 --[[ImGuiMouseSource.COUNT]])
if source < 0 or source >= 3 --[[ImGuiMouseSource.COUNT]] then
return "Unknown"
end
return mouse_source_names[source + 1]
end
--- @param prefix string
--- @param e ImGuiInputEvent
local function DebugPrintInputEvent(prefix, e)
local T = ImGuiInputEventType
if e.Type == T.MousePos then if e.MousePos.PosX == -3.4028234663853e+38 --[[FLT_MAX]] and e.MousePos.PosY == -3.4028234663853e+38 --[[FLT_MAX]] then IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (-FLT_MAX, -FLT_MAX)\n", prefix) else IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (%.1f, %.1f) (%s)\n", prefix, e.MousePos.PosX, e.MousePos.PosY, GetMouseSourceName(e.MousePos.MouseSource)) end return end
if e.Type == T.MouseButton then IMGUI_DEBUG_LOG_IO("[io] %s: MouseButton %d %s (%s)\n", prefix, e.MouseButton.Button, e.MouseButton.Down and "Down" or "Up", GetMouseSourceName(e.MouseButton.MouseSource)) return end
if e.Type == T.MouseWheel then IMGUI_DEBUG_LOG_IO("[io] %s: MouseWheel (%.3f, %.3f) (%s)\n", prefix, e.MouseWheel.WheelX, e.MouseWheel.WheelY, GetMouseSourceName(e.MouseWheel.MouseSource)) return end
if e.Type == T.MouseViewport then IMGUI_DEBUG_LOG_IO("[io] %s: MouseViewport (0x%08X)\n", prefix, e.MouseViewport.HoveredViewportID) return end
if e.Type == T.Key then IMGUI_DEBUG_LOG_IO("[io] %s: Key \"%s\" %s\n", prefix, ImGui.GetKeyName(e.Key.Key), e.Key.Down and "Down" or "Up") return end
if e.Type == T.Text then IMGUI_DEBUG_LOG_IO("[io] %s: Text: '%s' (U+%08X)\n", prefix, utf8.char(e.Text.Char), e.Text.Char) return end -- [Luau] utf8.char() instead of ImTextCharToUtf8()
if e.Type == T.Focus then IMGUI_DEBUG_LOG_IO("[io] %s: AppFocused %d\n", prefix, e.AppFocused.Focused and 1 or 0) return end
end
do
local key_changed_mask = ImBitArray(ImGuiKey.NamedKey_COUNT)
-- Process input queue
-- We always call this with the value of 'bool g.IO.ConfigInputTrickleEventQueue'.
-- - trickle_fast_inputs = false : process all events, turn into flattened input state (e.g. successive down/up/down/up will be lost)
-- - trickle_fast_inputs = true : process as many events as possible (successive down/up/down/up will be trickled over several frames so nothing is lost) (new feature in 1.87)
--- @param trickle_fast_inputs bool
function ImGui.UpdateInputEvents(trickle_fast_inputs)
local g = GImGui
local io = g.IO
-- Only trickle chars<>key when working with InputText()
-- FIXME: InputText() could parse event trail?
-- FIXME: Could specialize chars<>keys trickling rules for control keys (those not typically associated to characters)
local trickle_interleaved_nonchar_keys_and_text = trickle_fast_inputs and g.WantTextInputNextFrame == 1
local mouse_moved = false
local mouse_wheeled = false
local key_changed = false
local key_changed_nonchar = false
local text_inputted = false
local mouse_button_changed = 0x00
key_changed_mask:ClearAllBits()
local event_n = 1
local stop_3231 = false
while event_n <= g.InputEventsQueue.Size do
repeat
local e = g.InputEventsQueue.Data[event_n]
if e.Type == 1 --[[ImGuiInputEventType.MousePos]] then
if g.IO.WantSetMousePos then
break
end
-- Trickling Rule: Stop processing queued events if we already handled a mouse button change
if trickle_fast_inputs and (mouse_button_changed ~= 0 or mouse_wheeled or key_changed or text_inputted) then
stop_3231 = true; break
end
local event_pos = ImVec2(e.MousePos.PosX, e.MousePos.PosY)
io.MousePos = event_pos
io.MouseSource = e.MousePos.MouseSource
mouse_moved = true
elseif e.Type == 3 --[[ImGuiInputEventType.MouseButton]] then
-- Trickling Rule: Stop processing queued events if we got multiple action on the same button
local button = e.MouseButton.Button
assert(button >= 0 and button < 5 --[[ImGuiMouseButton.COUNT]])
if trickle_fast_inputs and ((bit32.band(mouse_button_changed, bit32.lshift(1, button)) ~= 0) or mouse_wheeled) then
stop_3231 = true; break
end
if trickle_fast_inputs and e.MouseButton.MouseSource == 1 --[[ImGuiMouseSource.TouchScreen]] and mouse_moved then -- #2702: TouchScreen have no initial hover.
stop_3231 = true; break
end
io.MouseDown[button] = e.MouseButton.Down
io.MouseSource = e.MouseButton.MouseSource
mouse_button_changed = bit32.bor(mouse_button_changed, bit32.lshift(1, button))
elseif e.Type == 2 --[[ImGuiInputEventType.MouseWheel]] then
-- Trickling Rule: Stop processing queued events if we got multiple action on the event
if trickle_fast_inputs and (mouse_moved or mouse_button_changed ~= 0) then
stop_3231 = true; break
end
io.MouseWheelH = io.MouseWheelH + e.MouseWheel.WheelX
io.MouseWheel = io.MouseWheel + e.MouseWheel.WheelY
io.MouseSource = e.MouseWheel.MouseSource
mouse_wheeled = true
elseif e.Type == 4 --[[ImGuiInputEventType.MouseViewport]] then
io.MouseHoveredViewport = e.MouseViewport.HoveredViewportID
elseif e.Type == 5 --[[ImGuiInputEventType.Key]] then
-- Trickling Rule: Stop processing queued events if we got multiple action on the same button
if bit32.band(io.ConfigFlags, 64 --[[ImGuiConfigFlags.NoKeyboard]]) ~= 0 then
break
end
local key = e.Key.Key
assert(key ~= 0 --[[ImGuiKey.None]])
-- TODO: Also gets 0-based index here, consider making key_data storage 1-based
local key_data, key_data_index = ImGui.GetKeyData(g, key)
if trickle_fast_inputs and key_data.Down ~= e.Key.Down and (key_changed_mask:TestBit(key_data_index + 1) or mouse_button_changed ~= 0) then
stop_3231 = true; break
end
local key_is_potentially_for_char_input = IsKeyChordPotentiallyCharInput(bit32.bor(ImGui.GetMergedModsFromKeys(), key))
if trickle_interleaved_nonchar_keys_and_text and (text_inputted and not key_is_potentially_for_char_input) then
stop_3231 = true; break
end
if key_data.Down ~= e.Key.Down then -- Analog change only do not trigger this, so it won't block e.g. further mouse pos events testing key_changed.
key_changed = true
key_changed_mask:SetBit(key_data_index + 1)
if trickle_interleaved_nonchar_keys_and_text and not key_is_potentially_for_char_input then
key_changed_nonchar = true
end
end
key_data.Down = e.Key.Down
key_data.AnalogValue = e.Key.AnalogValue
elseif e.Type == 6 --[[ImGuiInputEventType.Text]] then
if bit32.band(io.ConfigFlags, 64 --[[ImGuiConfigFlags.NoKeyboard]]) ~= 0 then
break
end
-- Trickling Rule: Stop processing queued events if keys/mouse have been interacted with
if trickle_fast_inputs and (mouse_button_changed ~= 0 or mouse_moved or mouse_wheeled) then
stop_3231 = true; break
end
if trickle_interleaved_nonchar_keys_and_text and key_changed_nonchar then
stop_3231 = true; break
end
local c = e.Text.Char
io.InputQueueCharacters:push_back((c <= 65535 --[[IM_UNICODE_CODEPOINT_MAX]]) and c or 65533 --[[IM_UNICODE_CODEPOINT_INVALID]])
if trickle_interleaved_nonchar_keys_and_text then
text_inputted = true
end
elseif e.Type == 7 --[[ImGuiInputEventType.Focus]] then
-- We intentionally overwrite this and process in NewFrame(), in order to give a chance
-- to multi-viewports backends to queue AddFocusEvent(false) + AddFocusEvent(true) in same frame.
local focus_lost = not e.AppFocused.Focused
io.AppFocusLost = focus_lost
else
assert(false, "Unknown event!")
end
until true
if stop_3231 then break end
event_n = event_n + 1
end
-- Record trail (for domain-specific applications wanting to access a precise trail)
for n = 1, event_n - 1 do
g.InputEventsTrail:push_back(g.InputEventsQueue.Data[n])
end
-- [DEBUG]
if not IMGUI_DISABLE_DEBUG_TOOLS then
if event_n ~= 1 and bit32.band(g.DebugLogFlags, 128 --[[ImGuiDebugLogFlags.EventIO]]) ~= 0 then -- [Luau] event_n is the 1-based next index: processed count = event_n - 1
for n = 1, g.InputEventsQueue.Size do
DebugPrintInputEvent((n < event_n) and "Processed" or "Remaining", g.InputEventsQueue.Data[n])
end
end
end
-- Remaining events will be processed on the next frame
-- FIXME-MULTITHREADING: io.AddKeyEvent() etc. calls are mostly thread-safe apart from the fact they push to this
-- queue which may be resized here. Could potentially rework this to narrow down the section needing a mutex? (#5772)
if event_n == g.InputEventsQueue.Size + 1 then
g.InputEventsQueue:resize(0)
else
for i = 1, event_n - 1 do
g.InputEventsQueue:erase(1)
end
end
-- Clear buttons state when focus is lost
-- - this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle.
-- - we clear in EndFrame() and not now in order allow application/user code polling this flag
-- (e.g. custom backend may want to clear additional data, custom widgets may want to react with a "canceling" event).
if g.IO.AppFocusLost then
g.IO:ClearInputKeys()
g.IO:ClearInputMouse()
end
end
end
-- _LockXXX flags are useful to lock keys away from code which is not input-owner aware.
-- When using _LockXXX flags, you can use ImGuiKeyOwner_Any to lock keys from everyone.
-- - SetKeyOwner(..., None) : clears owner
-- - SetKeyOwner(..., Any, !Lock) : illegal (assert)
-- - SetKeyOwner(..., Any or None, Lock) : set lock
-- Ownership is automatically released on the frame after a release, see code in UpdateKeyboardInputs().
--- @param key ImGuiKey
--- @param owner_id ImGuiID
--- @param flags? ImGuiInputFlags
function ImGui.SetKeyOwner(key, owner_id, flags)
if flags == nil then flags = 0 end
local g = GImGui --- @cast g ImGuiContext
assert(ImGui.IsNamedKeyOrMod(key) and (owner_id ~= 0 --[[ImGuiKeyOwner_Any]] or (bit32.band(flags, bit32.bor(1048576 --[[ImGuiInputFlags.LockThisFrame]], 2097152 --[[ImGuiInputFlags.LockUntilRelease]])) ~= 0)), "Can only use _Any with _LockXXX flags (to eat a key away without an ID to retrieve it)")
assert(bit32.band(flags, bit32.bnot(3145728 --[[ImGuiInputFlags.SupportedBySetKeyOwner]])) == 0, "Passing flags not supported by this function!")
-- IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X, flags=%08X)\n", GetKeyName(key), owner_id, flags)
local owner_data = ImGui.GetKeyOwnerData(g, key)
owner_data.OwnerCurr = owner_id
owner_data.OwnerNext = owner_id
-- We cannot lock by default as it would likely break lots of legacy code.
-- In the case of using LockUntilRelease while key is not down we still lock during the frame (no key_data.Down test)
owner_data.LockUntilRelease = (bit32.band(flags, 2097152 --[[ImGuiInputFlags.LockUntilRelease]]) ~= 0)
owner_data.LockThisFrame = (bit32.band(flags, 1048576 --[[ImGuiInputFlags.LockThisFrame]]) ~= 0) or owner_data.LockUntilRelease
end
-- Rarely used helper
--- @param key_chord ImGuiKeyChord
--- @param owner_id ImGuiID
--- @param flags? ImGuiInputFlags
function ImGui.SetKeyOwnersForKeyChord(key_chord, owner_id, flags)
if flags == nil then flags = 0 end
if bit32.band(key_chord, 4096 --[[ImGuiMod_Ctrl]]) ~= 0 then
ImGui.SetKeyOwner(4096 --[[ImGuiMod_Ctrl]], owner_id, flags)
end
if bit32.band(key_chord, 8192 --[[ImGuiMod_Shift]]) ~= 0 then
ImGui.SetKeyOwner(8192 --[[ImGuiMod_Shift]], owner_id, flags)
end
if bit32.band(key_chord, 16384 --[[ImGuiMod_Alt]]) ~= 0 then
ImGui.SetKeyOwner(16384 --[[ImGuiMod_Alt]], owner_id, flags)
end
if bit32.band(key_chord, 32768 --[[ImGuiMod_Super]]) ~= 0 then
ImGui.SetKeyOwner(32768 --[[ImGuiMod_Super]], owner_id, flags)
end
if bit32.band(key_chord, bit32.bnot(61440 --[[ImGuiMod_Mask_]])) ~= 0 then
ImGui.SetKeyOwner(bit32.band(key_chord, bit32.bnot(61440 --[[ImGuiMod_Mask_]])), owner_id, flags)
end
end
-- This is more or less equivalent to a fancier version of:
-- if (IsItemHovered() || IsItemActive())
-- SetKeyOwner(key, GetItemID());
-- Extensive uses of that (e.g. many calls for a single item) may want to manually perform the tests once and then call SetKeyOwner() multiple times.
-- More advanced usage scenarios may want to call SetKeyOwner() manually based on different condition.
-- Worth noting is that only one item can be hovered and only one item can be active, therefore this usage pattern doesn't need to bother with routing and priority.
--- @param key ImGuiKey
--- @param flags? ImGuiInputFlags
function ImGui.SetItemKeyOwner(key, flags)
if flags == nil then flags = 0 --[[ImGuiInputFlags.None]] end
local g = GImGui
local id = g.LastItemData.ID
if id == 0 or (g.HoveredId ~= id and g.ActiveId ~= id) then
return false
end
if bit32.band(flags, 12582912 --[[ImGuiInputFlags.CondMask_]]) == 0 then
flags = bit32.bor(flags, 12582912 --[[ImGuiInputFlags.CondDefault_]])
end
if (g.HoveredId == id and bit32.band(flags, 4194304 --[[ImGuiInputFlags.CondHovered]]) ~= 0) or (g.ActiveId == id and bit32.band(flags, 8388608 --[[ImGuiInputFlags.CondActive]]) ~= 0) then
assert(bit32.band(flags, bit32.bnot(15728640 --[[ImGuiInputFlags.SupportedBySetItemKeyOwner]])) == 0) -- Passing flags not supported by this function!
if not ImGui.TestKeyOwner(key, id) then
return false
end
ImGui.SetKeyOwner(key, id, bit32.band(flags, bit32.bnot(12582912 --[[ImGuiInputFlags.CondMask_]])))
return true
end
return false
end
-- This is equivalent to comparing KeyMods + doing a IsKeyPressed()
-- [Port] public overload IsKeyChordPressed(key_chord) == IsKeyChordPressed(key_chord, ImGuiInputFlags_None, ImGuiKeyOwner_Any)
--- @param key_chord ImGuiKeyChord
--- @param flags? ImGuiInputFlags
--- @param owner_id? ImGuiID
function ImGui.IsKeyChordPressed(key_chord, flags, owner_id)
if flags == nil then flags = 0 --[[ImGuiInputFlags.None]] end
if owner_id == nil then owner_id = 0 --[[ImGuiKeyOwner_Any]] end
local g = GImGui
key_chord = ImGui.FixupKeyChord(key_chord)
local mods = bit32.band(key_chord, 61440 --[[ImGuiMod_Mask_]])
if g.IO.KeyMods ~= mods then
return false
end
-- Special storage location for mods
local key = bit32.band(key_chord, bit32.bnot(61440 --[[ImGuiMod_Mask_]]))
if key == 0 --[[ImGuiKey.None]] then
key = ImGui.ConvertSingleModFlagToKey(mods)
end
if not ImGui.IsKeyPressedEx(key, bit32.band(flags, 255 --[[ImGuiInputFlags.RepeatMask_]]), owner_id) then
return false
end
return true
end
--- @param key_chord ImGuiKeyChord
--- @param flags? ImGuiInputFlags
function ImGui.SetNextItemShortcut(key_chord, flags)
if flags == nil then flags = 0 end
local g = GImGui
g.NextItemData.HasFlags = bit32.bor(g.NextItemData.HasFlags, 4 --[[ImGuiNextItemDataFlags.HasShortcut]])
g.NextItemData.Shortcut = key_chord
g.NextItemData.ShortcutFlags = flags
end
-- [Port] public overload Shortcut(key_chord, flags = 0) == Shortcut(key_chord, flags, ImGuiKeyOwner_Any)
--- @param key_chord ImGuiKeyChord
--- @param flags? ImGuiInputFlags
--- @param owner_id? ImGuiID
function ImGui.Shortcut(key_chord, flags, owner_id)
if flags == nil then flags = 0 end
if owner_id == nil then owner_id = 0 --[[ImGuiKeyOwner_Any]] end
local g = GImGui
-- IMGUI_DEBUG_LOG("Shortcut(%s, flags=%X, owner_id=0x%08X)\n", ImGui.GetKeyChordName(key_chord, g.TempBuffer.Data, g.TempBuffer.Size), flags, owner_id)
-- When using (owner_id == 0/Any): SetShortcutRouting() will use CurrentFocusScopeId and filter with this, so IsKeyPressed() is fine with he 0/Any.
if bit32.band(flags, 15360 --[[ImGuiInputFlags.RouteTypeMask_]]) == 0 then
flags = bit32.bor(flags, 2048 --[[ImGuiInputFlags.RouteFocused]])
end
-- Using 'owner_id == ImGuiKeyOwner_Any/0': auto-assign an owner based on current focus scope (each window has its focus scope by default)
-- Effectively makes Shortcut() always input-owner aware.
if owner_id == 0 --[[ImGuiKeyOwner_Any]] or owner_id == 4294967295 --[[ImGuiKeyOwner_NoOwner]] then
owner_id = GetRoutingIdFromOwnerId(owner_id)
end
if bit32.band(g.CurrentItemFlags, 64 --[[ImGuiItemFlags.Disabled]]) ~= 0 then
return false
end
-- Submit route
if not ImGui.SetShortcutRouting(key_chord, flags, owner_id) then
return false
end
-- Default repeat behavior for Shortcut()
-- So e.g. pressing Ctrl+W and releasing Ctrl while holding W will not trigger the W shortcut.
if bit32.band(flags, 1 --[[ImGuiInputFlags.Repeat]]) ~= 0 and bit32.band(flags, 240 --[[ImGuiInputFlags.RepeatUntilMask_]]) == 0 then
flags = bit32.bor(flags, 32 --[[ImGuiInputFlags.RepeatUntilKeyModsChange]])
end
if not ImGui.IsKeyChordPressed(key_chord, flags, owner_id) then
return false
end
-- Claim mods during the press
ImGui.SetKeyOwnersForKeyChord(bit32.band(key_chord, 61440 --[[ImGuiMod_Mask_]]), owner_id)
assert(bit32.band(flags, bit32.bnot(261375 --[[ImGuiInputFlags.SupportedByShortcut]])) == 0) -- Passing flags not supported by this function!
return true
end
--- @param window ImGuiWindow
--- @return bool
function ImGui.IsWindowActiveAndVisible(window)
return window.Active and not window.Hidden
end
--- @param window ImGuiWindow
--- @nodiscard
--- @param window ImGuiWindow
--- @return ImGuiWindow
local function GetWindowForTitleAndMenuHeight(window)
return (window.DockNodeAsHost and window.DockNodeAsHost.VisibleWindow) and window.DockNodeAsHost.VisibleWindow or window
end
local function CalcWindowMinSize(window)
-- We give windows non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups)
-- FIXME: Essentially we want to restrict manual resizing to WindowMinSize+Decoration, and allow api resizing to be smaller.
-- Perhaps should tend further a neater test for this.
local g = GImGui
local size_min = ImVec2()
if (bit32.band(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0) and (bit32.band(window.Flags, 67108864 --[[ImGuiWindowFlags.Popup]]) == 0) then
if bit32.band(window.ChildFlags, 4 --[[ImGuiChildFlags.ResizeX]]) ~= 0 then
size_min.x = g.Style.WindowMinSize.x
else
size_min.x = 4 --[[IMGUI_WINDOW_HARD_MIN_SIZE]]
end
if bit32.band(window.ChildFlags, 8 --[[ImGuiChildFlags.ResizeY]]) ~= 0 then
size_min.y = g.Style.WindowMinSize.y
else
size_min.y = 4 --[[IMGUI_WINDOW_HARD_MIN_SIZE]]
end
else
if bit32.band(window.Flags, 64 --[[ImGuiWindowFlags.AlwaysAutoResize]]) == 0 then
size_min.x = g.Style.WindowMinSize.x
else
size_min.x = 4 --[[IMGUI_WINDOW_HARD_MIN_SIZE]]
end
if bit32.band(window.Flags, 64 --[[ImGuiWindowFlags.AlwaysAutoResize]]) == 0 then
size_min.y = g.Style.WindowMinSize.y
else
size_min.y = 4 --[[IMGUI_WINDOW_HARD_MIN_SIZE]]
end
end
-- Reduce artifacts with very small windows
local window_for_height = GetWindowForTitleAndMenuHeight(window)
size_min.y = ImMax(size_min.y, window_for_height.TitleBarHeight + window_for_height.MenuBarHeight + ImMax(0, g.Style.WindowRounding - 1))
return size_min
end
--- @param window ImGuiWindow
--- @param size_desired ImVec2
--- @return ImVec2
--- @nodiscard
local function CalcWindowSizeAfterConstraint(window, size_desired)
local g = GImGui
local new_size = ImVec2()
ImVec2_Copy(new_size, size_desired)
if bit32.band(g.NextWindowData.HasFlags, 16 --[[ImGuiNextWindowDataFlags.HasSizeConstraint]]) ~= 0 then
-- See comments in SetNextWindowSizeConstraints() for details about setting size_min an size_max.
local cr = ImRect()
ImRect_Copy(cr, g.NextWindowData.SizeConstraintRect)
new_size.x = (cr.Min.x >= 0 and cr.Max.x >= 0) and ImClamp(new_size.x, cr.Min.x, cr.Max.x) or window.SizeFull.x
new_size.y = (cr.Min.y >= 0 and cr.Max.y >= 0) and ImClamp(new_size.y, cr.Min.y, cr.Max.y) or window.SizeFull.y
if g.NextWindowData.SizeCallback then
local data = {} -- TODO: ImGuiSizeCallbackData
data.UserData = g.NextWindowData.SizeCallbackUserData
data.Pos = ImVec2(window.Pos.x, window.Pos.y)
data.CurrentSize = ImVec2(window.SizeFull.x, window.SizeFull.y)
data.DesiredSize = ImVec2(new_size.x, new_size.y)
g.NextWindowData.SizeCallback(data)
new_size.x = data.DesiredSize.x
new_size.y = data.DesiredSize.y
end
new_size.x = IM_TRUNC(new_size.x)
new_size.y = IM_TRUNC(new_size.y)
end
-- Minimum size
local size_min = CalcWindowMinSize(window)
return ImMax(new_size, size_min)
end
--- @param window ImGuiWindow
--- @param content_size_current ImVec2
--- @param content_size_ideal ImVec2
local function CalcWindowContentSizes(window, content_size_current, content_size_ideal)
local preserve_old_content_sizes = false
if (window.Collapsed and window.AutoFitFramesX <= 0 and window.AutoFitFramesY <= 0) then
preserve_old_content_sizes = true
elseif (window.Hidden and window.HiddenFramesCannotSkipItems == 0 and window.HiddenFramesCanSkipItems > 0) then
preserve_old_content_sizes = true
end
if preserve_old_content_sizes then
content_size_current.x = window.ContentSize.x
content_size_current.y = window.ContentSize.y
content_size_ideal.x = window.ContentSizeIdeal.x
content_size_ideal.y = window.ContentSizeIdeal.y
return
end
if (window.ContentSizeExplicit.x ~= 0.0) then content_size_current.x = window.ContentSizeExplicit.x else content_size_current.x = ImTrunc64(window.DC.CursorMaxPos.x - window.DC.CursorStartPos.x) end
if (window.ContentSizeExplicit.y ~= 0.0) then content_size_current.y = window.ContentSizeExplicit.y else content_size_current.y = ImTrunc64(window.DC.CursorMaxPos.y - window.DC.CursorStartPos.y) end
if (window.ContentSizeExplicit.x ~= 0.0) then content_size_ideal.x = window.ContentSizeExplicit.x else content_size_ideal.x = ImTrunc64(ImMax(window.DC.CursorMaxPos.x, window.DC.IdealMaxPos.x) - window.DC.CursorStartPos.x) end
if (window.ContentSizeExplicit.y ~= 0.0) then content_size_ideal.y = window.ContentSizeExplicit.y else content_size_ideal.y = ImTrunc64(ImMax(window.DC.CursorMaxPos.y, window.DC.IdealMaxPos.y) - window.DC.CursorStartPos.y) end
end
--- @param window ImGuiWindow # The window to calculate auto-fit size for
--- @param size_contents ImVec2 # The content size
--- @param axis_mask int # The axis mask to determine which axes to auto-fit
--- @return ImVec2 # The auto-fit size
local function CalcWindowAutoFitSize(window, size_contents, axis_mask)
local g = GImGui
local style = g.Style
local decoration_w_without_scrollbars = window.DecoOuterSizeX1 + window.DecoOuterSizeX2 - window.ScrollbarSizes.x
local decoration_h_without_scrollbars = window.DecoOuterSizeY1 + window.DecoOuterSizeY2 - window.ScrollbarSizes.y
local size_pad = ImVec2(window.WindowPadding.x * 2, window.WindowPadding.y * 2)
local size_desired = ImVec2()
size_desired.x = (bit32.band(axis_mask, 1) ~= 0) and (size_contents.x + size_pad.x + decoration_w_without_scrollbars) or window.Size.x
size_desired.y = (bit32.band(axis_mask, 2) ~= 0) and (size_contents.y + size_pad.y + decoration_h_without_scrollbars) or window.Size.y
-- Determine maximum window size
-- Child windows are laid within their parent (unless they are also popups/menus) and thus have no restriction
local size_max = ImVec2(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]])
if bit32.band(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) == 0 or bit32.band(window.Flags, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0 then
if not window.ViewportOwned then
size_max = ImGui.GetMainViewport().WorkSize - style.DisplaySafeAreaPadding * 2.0
end
local monitor_idx = window.ViewportAllowPlatformMonitorExtend
if monitor_idx >= 1 and monitor_idx <= g.PlatformIO.Monitors.Size then
size_max = g.PlatformIO.Monitors.Data[monitor_idx].WorkSize - style.DisplaySafeAreaPadding * 2.0
end
end
if bit32.band(window.Flags, 33554432 --[[ImGuiWindowFlags.Tooltip]]) ~= 0 then
-- Tooltip always resize (up to maximum size)
return ImMin(size_desired, size_max)
else
local size_min = CalcWindowMinSize(window)
local size_auto_fit = ImClamp(size_desired, ImMin(size_min, size_max), size_max)
-- When the window cannot fit all contents (either because of constraints, either because screen is too small),
-- we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
local size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit)
local size_contents_for_scrollbar_x = (bit32.band(axis_mask, 1) ~= 0) and size_contents.x or window.ContentSize.x -- See #9352. In theory this should use same logic as `window.ScrollbarY = ...` codepath in Begin(). Needs some plumbling.
local size_contents_for_scrollbar_y = (bit32.band(axis_mask, 2) ~= 0) and size_contents.y or window.ContentSize.y
local will_have_scrollbar_x = (size_auto_fit_after_constraint.x < size_contents_for_scrollbar_x + size_pad.x + decoration_w_without_scrollbars and bit32.band(window.Flags, 8 --[[ImGuiWindowFlags.NoScrollbar]]) == 0 and bit32.band(window.Flags, 2048 --[[ImGuiWindowFlags.HorizontalScrollbar]]) ~= 0) or bit32.band(window.Flags, 32768 --[[ImGuiWindowFlags.AlwaysHorizontalScrollbar]]) ~= 0
local will_have_scrollbar_y = (size_auto_fit_after_constraint.y < size_contents_for_scrollbar_y + size_pad.y + decoration_h_without_scrollbars and bit32.band(window.Flags, 8 --[[ImGuiWindowFlags.NoScrollbar]]) == 0) or bit32.band(window.Flags, 16384 --[[ImGuiWindowFlags.AlwaysVerticalScrollbar]]) ~= 0
if will_have_scrollbar_x then
size_auto_fit.y = size_auto_fit.y + style.ScrollbarSize
end
if will_have_scrollbar_y then
size_auto_fit.x = size_auto_fit.x + style.ScrollbarSize
end
return size_auto_fit
end
end
--- @param window ImGuiWindow
--- @return ImVec2
function ImGui.CalcWindowNextAutoFitSize(window)
local size_contents_current = ImVec2()
local size_contents_ideal = ImVec2()
CalcWindowContentSizes(window, size_contents_current, size_contents_ideal)
local size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal, -1)
local size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit)
return size_final
end
--- @param window ImGuiWindow
--- @return ImGuiCol
local function GetWindowBgColorIdx(window)
if bit32.band(window.Flags, bit32.bor(33554432 --[[ImGuiWindowFlags.Tooltip]], 67108864 --[[ImGuiWindowFlags.Popup]])) ~= 0 then
return 4 --[[ImGuiCol.PopupBg]]
elseif bit32.band(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0 and not window.DockIsActive then
return 3 --[[ImGuiCol.ChildBg]]
else
return 2 --[[ImGuiCol.WindowBg]]
end
end
--- @param window ImGuiWindow
--- @param corner_target_arg ImVec2
--- @param corner_norm ImVec2
--- @return ImVec2, ImVec2
--- @nodiscard
local function CalcResizePosSizeFromAnyCorner(window, corner_target_arg, corner_norm)
local corner_target = ImVec2()
ImVec2_Copy(corner_target, corner_target_arg)
if bit32.band(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0 then -- Clamp resizing of childs within parent
local parent_window = window.ParentWindow
local parent_flags = parent_window.Flags
local limit_rect = ImRect()
ImRect_Copy(limit_rect, parent_window.InnerRect)
limit_rect:Expand(ImVec2(-ImMax(parent_window.WindowPadding.x, parent_window.WindowBorderSize), -ImMax(parent_window.WindowPadding.y, parent_window.WindowBorderSize)))
if (bit32.band(parent_flags, bit32.bor(2048 --[[ImGuiWindowFlags.HorizontalScrollbar]], 32768 --[[ImGuiWindowFlags.AlwaysHorizontalScrollbar]])) == 0) or (bit32.band(parent_flags, 8 --[[ImGuiWindowFlags.NoScrollbar]]) ~= 0) then
corner_target.x = ImClamp(corner_target.x, limit_rect.Min.x, limit_rect.Max.x)
end
if bit32.band(parent_flags, 8 --[[ImGuiWindowFlags.NoScrollbar]]) ~= 0 then
corner_target.y = ImClamp(corner_target.y, limit_rect.Min.y, limit_rect.Max.y)
end
end
local pos_min = ImLerp(corner_target, window.Pos, corner_norm) -- Expected window upper-left
local pos_max = ImLerp(window.Pos + window.Size, corner_target, corner_norm) -- Expected window lower-right
local size_expected = pos_max - pos_min
local size_constrained = CalcWindowSizeAfterConstraint(window, size_expected)
local out_pos = pos_min
if corner_norm.x == 0.0 then
out_pos.x = out_pos.x - (size_constrained.x - size_expected.x)
end
if corner_norm.y == 0.0 then
out_pos.y = out_pos.y - (size_constrained.y - size_expected.y)
end
return out_pos, size_constrained
end
-- Handle resize for: Resize Grips, Borders, Gamepad
-- Return true when using auto-fit (double-click on resize grip)
-- Returns (ret_auto_fit_mask, border_hovered, border_held)
--- @param window ImGuiWindow
--- @param resize_grip_count int
--- @param resize_grip_col table
--- @param visibility_rect ImRect
--- @return int, int, int
--- @param window ImGuiWindow
--- @param n int
--- @return ImGuiID
function ImGui.GetWindowResizeCornerID(window, n)
assert(n >= 0 and n < 4)
local id = window.DockIsActive and window.DockNode.HostWindow.ID or window.ID
id = ImHashStr("#RESIZE", nil, id)
id = ImHashData(n, -1, id) -- [Luau] ImHashData(&n, sizeof(int), id): same hashing as window:GetID(n) used by UpdateWindowManualResize()
return id
end
--- @param window ImGuiWindow
--- @param dir ImGuiDir
--- @return ImGuiID
function ImGui.GetWindowResizeBorderID(window, dir)
assert(dir >= 0 and dir < 4)
local n = dir + 4
local id = window.DockIsActive and window.DockNode.HostWindow.ID or window.ID
id = ImHashStr("#RESIZE", nil, id)
id = ImHashData(n, -1, id) -- [Luau] see GetWindowResizeCornerID()
return id
end
local function UpdateWindowManualResize(window, resize_grip_count, resize_grip_col, visibility_rect)
local g = GImGui
local flags = window.Flags
if (bit32.band(flags, 2 --[[ImGuiWindowFlags.NoResize]]) ~= 0 or window.AutoFitFramesX > 0 or window.AutoFitFramesY > 0) then
return 0, -1, -1
end
if (bit32.band(flags, 64 --[[ImGuiWindowFlags.AlwaysAutoResize]]) ~= 0 and bit32.band(window.ChildFlags, bit32.bor(4 --[[ImGuiChildFlags.ResizeX]], 8 --[[ImGuiChildFlags.ResizeY]])) == 0) then
return 0, -1, -1
end
if window.WasActive == false then -- Early out to avoid running this code for e.g. a hidden implicit/fallback Debug window.
return 0, -1, -1
end
local border_hovered = -1
local border_held = -1
local ret_auto_fit_mask = 0x00
local grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.35, g.Style.WindowRounding + 1.0 + g.FontSize * 0.2))
local grip_hover_inner_size = (resize_grip_count > 0) and IM_TRUNC(grip_draw_size * 0.75) or 0.0
local grip_hover_outer_size = g.WindowsBorderHoverPadding + 1
ImGui.PushID("#RESIZE")
local pos_target = ImVec2(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]])
local size_target = ImVec2(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]])
-- Clip mouse interaction rectangles within the viewport rectangle (in practice the narrowing is going to happen most of the time).
-- - Not narrowing would mostly benefit the situation where OS windows _without_ decoration have a threshold for hovering when outside their limits.
-- This is however not the case with current backends under Win32, but a custom borderless window implementation would benefit from it.
-- - When decoration are enabled we typically benefit from that distance, but then our resize elements would be conflicting with OS resize elements, so we also narrow.
-- - Note that we are unable to tell if the platform setup allows hovering with a distance threshold (on Win32, decorated window have such threshold).
-- We only clip interaction so we overwrite window.ClipRect, cannot call PushClipRect() yet as DrawList is not yet setup.
local clip_with_viewport_rect = bit32.band(g.IO.BackendFlags, 4096 --[[ImGuiBackendFlags.HasMouseHoveredViewport]]) == 0 or g.IO.MouseHoveredViewport ~= window.ViewportId or bit32.band(window.Viewport.Flags, 8 --[[ImGuiViewportFlags.NoDecoration]]) == 0
if (clip_with_viewport_rect) then
ImRect_Copy(window.ClipRect, window.Viewport:GetMainRect())
end
local clamp_rect = ImRect()
ImRect_Copy(clamp_rect, visibility_rect)
local window_move_from_title_bar = (bit32.band(window.BgClickFlags, 1 --[[ImGuiWindowBgClickFlags.Move]]) == 0) and (bit32.band(window.Flags, 1 --[[ImGuiWindowFlags.NoTitleBar]]) == 0)
if window_move_from_title_bar then
clamp_rect.Min.y = clamp_rect.Min.y - window.TitleBarHeight
end
for resize_grip_n = 0, resize_grip_count - 1 do
local def = ImGuiResizeGripDef[resize_grip_n + 1]
local corner_pos = def.CornerPosN
local inner_dir = def.InnerDir
local corner = ImVec2(window.Pos.x + corner_pos.x * window.Size.x, window.Pos.y + corner_pos.y * window.Size.y)
-- Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
local resize_rect = ImRect(corner - inner_dir * grip_hover_outer_size, corner + inner_dir * grip_hover_inner_size)
if resize_rect.Min.x > resize_rect.Max.x then resize_rect.Min.x, resize_rect.Max.x = resize_rect.Max.x, resize_rect.Min.x end
if resize_rect.Min.y > resize_rect.Max.y then resize_rect.Min.y, resize_rect.Max.y = resize_rect.Max.y, resize_rect.Min.y end
local resize_grip_id = window:GetID(resize_grip_n) -- == GetWindowResizeCornerID()
ImGui.ItemAdd(resize_rect, resize_grip_id, nil, 2 --[[ImGuiItemFlags.NoNav]])
local _, hovered, held = ImGui.ButtonBehavior(resize_rect, resize_grip_id, bit32.bor(2048 --[[ImGuiButtonFlags.FlattenChildren]], 262144 --[[ImGuiButtonFlags.NoNavFocus]]))
-- GetForegroundDrawList(window):AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255))
if hovered or held then
ImGui.SetMouseCursor((bit32.band(resize_grip_n, 1) ~= 0) and 5 --[[ImGuiMouseCursor.ResizeNESW]] or 6 --[[ImGuiMouseCursor.ResizeNWSE]])
end
if held and g.IO.MouseDoubleClicked[0] then
-- Auto-fit when double-clicking
local size_auto_fit = CalcWindowAutoFitSize(window, window.ContentSizeIdeal, -1)
size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit)
ret_auto_fit_mask = 0x03 -- Both axes
ImGui.ClearActiveID()
elseif held then
-- Resize from any of the four corners
-- We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
local clamp_min = ImVec2((corner_pos.x == 1.0) and clamp_rect.Min.x or -3.4028234663853e+38 --[[FLT_MAX]], (corner_pos.y == 1.0) and clamp_rect.Min.y or -3.4028234663853e+38 --[[FLT_MAX]])
local clamp_max = ImVec2((corner_pos.x == 0.0) and clamp_rect.Max.x or 3.4028234663853e+38 --[[FLT_MAX]], (corner_pos.y == 0.0) and clamp_rect.Max.y or 3.4028234663853e+38 --[[FLT_MAX]])
local corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(def.InnerDir * grip_hover_outer_size, def.InnerDir * -grip_hover_inner_size, def.CornerPosN) -- Corner of the window corresponding to our corner grip
corner_target = ImClamp(corner_target, clamp_min, clamp_max)
pos_target, size_target = CalcResizePosSizeFromAnyCorner(window, corner_target, corner_pos)
end
-- Only lower-left grip is visible before hovering/activating
local resize_grip_visible = held or hovered or (resize_grip_n == 0 and bit32.band(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) == 0)
if resize_grip_visible then
local color
if held then
color = ImGui.GetColorU32(33 --[[ImGuiCol.ResizeGripActive]])
else
if hovered then
color = ImGui.GetColorU32(32 --[[ImGuiCol.ResizeGripHovered]])
else
color = ImGui.GetColorU32(31 --[[ImGuiCol.ResizeGrip]])
end
end
resize_grip_col[resize_grip_n + 1] = color
end
end
local resize_border_mask = 0x00
if bit32.band(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0 then
local mask_x = (bit32.band(window.ChildFlags, 4 --[[ImGuiChildFlags.ResizeX]]) ~= 0) and 0x02 or 0
local mask_y = (bit32.band(window.ChildFlags, 8 --[[ImGuiChildFlags.ResizeY]]) ~= 0) and 0x08 or 0
resize_border_mask = bit32.bor(mask_x, mask_y)
else
resize_border_mask = g.IO.ConfigWindowsResizeFromEdges and 0x0F or 0x00
end
for border_n = 0, 3 do
repeat
if bit32.band(resize_border_mask, bit32.lshift(1, border_n)) == 0 then
break
end
local def = ImGuiResizeBorderDef[border_n + 1]
local axis
if border_n == 0 --[[ImGuiDir.Left]] or border_n == 1 --[[ImGuiDir.Right]] then
axis = 1 --[[ImGuiAxis.X]]
else
axis = 2 --[[ImGuiAxis.Y]]
end
local border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, g.WindowsBorderHoverPadding)
local border_id = window:GetID(border_n + 4) -- == GetWindowResizeBorderID()
ImGui.ItemAdd(border_rect, border_id, nil, 2 --[[ImGuiItemFlags.NoNav]])
local _, hovered, held = ImGui.ButtonBehavior(border_rect, border_id, bit32.bor(2048 --[[ImGuiButtonFlags.FlattenChildren]], 262144 --[[ImGuiButtonFlags.NoNavFocus]]))
-- GetForegroundDrawList(window):AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255))
if hovered and g.HoveredIdTimer <= WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER then
hovered = false
end
if hovered or held then
ImGui.SetMouseCursor((axis == 1 --[[ImGuiAxis.X]]) and 4 --[[ImGuiMouseCursor.ResizeEW]] or 3 --[[ImGuiMouseCursor.ResizeNS]])
end
if held and g.IO.MouseDoubleClicked[0] then
-- Double-clicking bottom or right border auto-fit on this axis
-- FIXME: Support top and right borders: rework CalcResizePosSizeFromAnyCorner() to be reusable in both cases.
if border_n == 1 or border_n == 3 then -- Right and bottom border
local size_auto_fit = CalcWindowAutoFitSize(window, window.ContentSizeIdeal, bit32.lshift(1, axis - 1))
size_target[axis] = CalcWindowSizeAfterConstraint(window, size_auto_fit)[axis]
ret_auto_fit_mask = bit32.bor(ret_auto_fit_mask, bit32.lshift(1, axis - 1))
hovered = false
held = false -- So border doesn't show highlighted at new position
end
ImGui.ClearActiveID()
elseif held then
-- Switch to relative resizing mode when border geometry moved (e.g. resizing a child altering parent scroll), in order to avoid resizing feedback loop.
-- Currently only using relative mode on resizable child windows, as the problem to solve is more likely noticeable for them, but could apply for all windows eventually.
-- FIXME: May want to generalize this idiom at lower-level, so more widgets can use it!
local just_scrolled_manually_while_resizing = (g.WheelingWindow ~= nil and g.WheelingWindowScrolledFrame == g.FrameCount and ImGui.IsWindowChildOf(window, g.WheelingWindow, false, true))
if g.ActiveIdIsJustActivated or just_scrolled_manually_while_resizing then
g.WindowResizeBorderExpectedRect = border_rect
g.WindowResizeRelativeMode = false
end
if (bit32.band(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0) and g.WindowResizeBorderExpectedRect ~= border_rect then
g.WindowResizeRelativeMode = true
end
local border_curr = window.Pos + ImMin(def.SegmentN1, def.SegmentN2) * window.Size
local border_target_rel_mode_for_axis
local border_target_abs_mode_for_axis
border_target_rel_mode_for_axis = border_curr[axis] + g.IO.MouseDelta[axis]
border_target_abs_mode_for_axis = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + g.WindowsBorderHoverPadding -- Match ButtonBehavior() padding above.
-- Use absolute mode position
local border_target = ImVec2()
ImVec2_Copy(border_target, window.Pos)
border_target[axis] = border_target_abs_mode_for_axis
-- Use relative mode target for child window, ignore resize when moving back toward the ideal absolute position.
local ignore_resize = false
if g.WindowResizeRelativeMode then
-- GetForegroundDrawList():AddText(GetMainViewport().WorkPos, IM_COL32_WHITE, "Relative Mode")
border_target[axis] = border_target_rel_mode_for_axis
if g.IO.MouseDelta[axis] == 0.0 or ((g.IO.MouseDelta[axis] > 0.0) == (border_target_rel_mode_for_axis > border_target_abs_mode_for_axis)) then
ignore_resize = true
end
end
-- Clamp, apply
local clamp_min = ImVec2((border_n == 1 --[[ImGuiDir.Right]]) and clamp_rect.Min.x or -3.4028234663853e+38 --[[FLT_MAX]], (border_n == 3 --[[ImGuiDir.Down]] or (border_n == 2 --[[ImGuiDir.Up]] and window_move_from_title_bar)) and clamp_rect.Min.y or -3.4028234663853e+38 --[[FLT_MAX]])
local clamp_max = ImVec2((border_n == 0 --[[ImGuiDir.Left]]) and clamp_rect.Max.x or 3.4028234663853e+38 --[[FLT_MAX]], (border_n == 2 --[[ImGuiDir.Up]]) and clamp_rect.Max.y or 3.4028234663853e+38 --[[FLT_MAX]])
border_target = ImClamp(border_target, clamp_min, clamp_max)
if not ignore_resize then
pos_target, size_target = CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2))
end
end
if hovered then
border_hovered = border_n
end
if held then
border_held = border_n
end
until true
end
ImGui.PopID()
-- Restore nav layer
window.DC.NavLayerCurrent = 0 --[[ImGuiNavLayer.Main]]
-- Apply back modified position/size to window
if size_target.x ~= 3.4028234663853e+38 --[[FLT_MAX]] and (window.Size.x ~= size_target.x or window.SizeFull.x ~= size_target.x) then
window.Size.x = size_target.x
window.SizeFull.x = size_target.x
end
if size_target.y ~= 3.4028234663853e+38 --[[FLT_MAX]] and (window.Size.y ~= size_target.y or window.SizeFull.y ~= size_target.y) then
window.Size.y = size_target.y
window.SizeFull.y = size_target.y
end
if pos_target.x ~= 3.4028234663853e+38 --[[FLT_MAX]] and window.Pos.x ~= ImFloor(pos_target.x) then
window.Pos.x = ImFloor(pos_target.x)
end
if pos_target.y ~= 3.4028234663853e+38 --[[FLT_MAX]] and window.Pos.y ~= ImFloor(pos_target.y) then
window.Pos.y = ImFloor(pos_target.y)
end
-- Recalculate next expected border expected coordinates
if border_held ~= -1 then
g.WindowResizeBorderExpectedRect = GetResizeBorderRect(window, border_held, grip_hover_inner_size, g.WindowsBorderHoverPadding)
end
return ret_auto_fit_mask, border_hovered, border_held
end
--- @param pos ImVec2
--- @param wrap_pos_x float
--- @return float
function ImGui.CalcWrapWidthForPos(pos, wrap_pos_x)
if wrap_pos_x < 0.0 then
return 0.0
end
local g = GImGui
local window = g.CurrentWindow
if wrap_pos_x == 0.0 then
-- We could decide to setup a default wrapping max point for auto-resizing windows,
-- or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function?
-- if (window.Hidden and (window.Flags & ImGuiWindowFlags.AlwaysAutoResize))
-- wrap_pos_x = ImMax(window.WorkRect.Min.x + g.FontSize * 10.0, window.WorkRect.Max.x)
-- else
wrap_pos_x = window.WorkRect.Max.x
elseif wrap_pos_x > 0.0 then
wrap_pos_x = wrap_pos_x + window.Pos.x - window.Scroll.x -- wrap_pos_x is provided is window local space
end
return math.max(wrap_pos_x - pos.x, 1.0)
end
-------------------------------------------------------------------------------
-- [SECTION] RENDER HELPERS
-- Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change,
-- we need a nicer separation between low-level functions and high-level functions relying on the ImGui context.
-- Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.
-------------------------------------------------------------------------------
--- @param text ImString
--- @param text_end int?
--- @return int # Exclusive upper bound
@native
function ImGui.FindRenderedTextEnd(text, text_end)
local text_display_end = 1
local text_len = #text
if not text_end then
text_end = text_len + 1
end
text_display_end = ImMemchr(text, "##", 1) or text_end
text_display_end = math.min(text_display_end, text_end) -- [Luau] scalar ImMin()
return text_display_end
end
-- Internal ImGui functions to render text
-- RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
--- @param pos ImVec2
--- @param text string
--- @param text_begin int?
--- @param text_end int?
--- @param hide_text_after_hash bool? # true by default
@native
function ImGui.RenderText(pos, text, text_begin, text_end, hide_text_after_hash)
if text_begin == nil then text_begin = 1 end
if hide_text_after_hash == nil then hide_text_after_hash = true end
local g = GImGui
local window = g.CurrentWindow
-- Hide anything after a '##' string
local text_display_end
if hide_text_after_hash then
text_display_end = ImGui.FindRenderedTextEnd(text, text_end)
else
if text_end == nil then
text_end = #text + 1 -- FIXME-OPT (not reached by our internal calls)
end
text_display_end = text_end
end
if text ~= "" and text_display_end > 1 then
window.DrawList:AddText(g.Font, g.FontSize, pos, ImGui.GetColorU32(0 --[[ImGuiCol.Text]]), text, text_begin, text_display_end, 0.0)
if g.LogEnabled then
ImGui.LogRenderedText(pos, text, text_display_end, text_begin)
end
end
end
--- @param pos ImVec2
--- @param text string
--- @param text_end int?
--- @param wrap_width float
function ImGui.RenderTextWrapped(pos, text, text_end, wrap_width)
local g = GImGui
local window = g.CurrentWindow
if text_end == nil then
text_end = #text + 1 -- FIXME-OPT (not reached by our internal calls)
end
if text ~= "" then
window.DrawList:AddText(g.Font, g.FontSize, pos, ImGui.GetColorU32(0 --[[ImGuiCol.Text]]), text, 1, text_end, wrap_width)
if g.LogEnabled then
ImGui.LogRenderedText(pos, text, text_end)
end
end
end
-- Default clip_rect uses (pos_min,pos_max)
-- Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
-- FIXME-OPT: Since we have or calculate text_size we could coarse clip whole block immediately, especially for text above draw_list->DrawList.
-- Effectively as this is called from widget doing their own coarse clipping it's not very valuable presently. Next time function will take
-- better advantage of the render function taking size into account for coarse clipping.
-- DOES NOT CALL LogRenderedText(), unlike RenderTextClipped!!!
--- @param draw_list ImDrawList
--- @param pos_min ImVec2
--- @param pos_max ImVec2
--- @param text string
--- @param text_begin int
--- @param text_display_end int
--- @param text_size_if_known? ImVec2
--- @param align? ImVec2
--- @param clip_rect? ImRect
local RenderTextClipped_align0 = ImVec2(0, 0) -- [Luau] shared read-only default
local RenderTextClippedEx_pos = ImVec2() -- [Luau] scratch: AddText() only reads pos
local RenderTextClippedEx_fine_clip = ImVec4() -- [Luau] scratch: AddText() only reads it
@native
function ImGui.RenderTextClippedEx(draw_list, pos_min, pos_max, text, text_begin, text_display_end, text_size_if_known, align, clip_rect)
if not align then align = RenderTextClipped_align0 end
-- Perform CPU side clipping for single clipped element to avoid using scissor state
local pos = RenderTextClippedEx_pos
pos.x = pos_min.x; pos.y = pos_min.y
local text_size = text_size_if_known or ImGui.CalcTextSize(text, text_display_end, false, 0.0)
local clip_min = clip_rect and clip_rect.Min or pos_min
local clip_max = clip_rect and clip_rect.Max or pos_max
local need_clipping = (pos.x + text_size.x >= clip_max.x) or (pos.y + text_size.y >= clip_max.y)
if (clip_rect) then -- If we had no explicit clipping rectangle then pos==clip_min
need_clipping = need_clipping or ((pos.x < clip_min.x) or (pos.y < clip_min.y))
end
-- Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
if (align.x > 0.0) then pos.x = math.max(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x) end
if (align.y > 0.0) then pos.y = math.max(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y) end
-- Render
if (need_clipping) then
local fine_clip_rect = RenderTextClippedEx_fine_clip
fine_clip_rect.x = clip_min.x; fine_clip_rect.y = clip_min.y; fine_clip_rect.z = clip_max.x; fine_clip_rect.w = clip_max.y
draw_list:AddText(nil, 0.0, pos, ImGui.GetColorU32(0 --[[ImGuiCol.Text]]), text, text_begin, text_display_end, 0.0, fine_clip_rect)
else
draw_list:AddText(nil, 0.0, pos, ImGui.GetColorU32(0 --[[ImGuiCol.Text]]), text, text_begin, text_display_end, 0.0, nil)
end
end
--- @param pos_min ImVec2
--- @param pos_max ImVec2
--- @param text string
--- @param text_end? int
--- @param text_size_if_known? ImVec2
--- @param align? ImVec2
--- @param clip_rect? ImRect
function ImGui.RenderTextClipped(pos_min, pos_max, text, text_end, text_size_if_known, align, clip_rect)
if not align then align = RenderTextClipped_align0 end
-- Hide anything after a '##' string
local text_display_end = ImGui.FindRenderedTextEnd(text, text_end)
local text_len = text_display_end - 1
if text_len == 0 then
return
end
local g = GImGui
local window = g.CurrentWindow
ImGui.RenderTextClippedEx(window.DrawList, pos_min, pos_max, text, 1, text_display_end, text_size_if_known, align, clip_rect)
if g.LogEnabled then
ImGui.LogRenderedText(pos_min, text, text_display_end)
end
end
--- @param bb ImRect
--- @param id ImGuiID
--- @param flags? ImGuiNavRenderCursorFlags
--- @param rounding? float
@native
function ImGui.RenderNavCursor(bb, id, flags, rounding)
if flags == nil then flags = 0 --[[ImGuiNavRenderCursorFlags.None]] end
if rounding == nil then rounding = -1.0 end
local g = GImGui
if id ~= g.NavId then
return
end
if not g.NavCursorVisible and bit32.band(flags, 4 --[[ImGuiNavRenderCursorFlags.AlwaysDraw]]) == 0 then
return
end
if id == g.LastItemData.ID and bit32.band(g.LastItemData.ItemFlags, 2 --[[ImGuiItemFlags.NoNav]]) ~= 0 then
return
end
-- We don't early out on 'window.Flags & ImGuiWindowFlags.NoNavInputs' because it would be inconsistent with
-- other code directly checking NavCursorVisible. Instead we aim for NavCursorVisible to always be false.
local window = g.CurrentWindow
if window.DC.NavHideHighlightOneFrame then
return
end
if rounding < 0.0 then
rounding = g.Style.FrameRounding
end
local display_rect = ImRect()
ImRect_Copy(display_rect, bb)
display_rect:ClipWith(window.ClipRect)
local scale_factor = ImGui.GetScale() -- FIXME-DPI
local thickness = ImTrunc(ImMax(2.0, 1.5 * scale_factor))
if bit32.band(flags, 2 --[[ImGuiNavRenderCursorFlags.Compact]]) ~= 0 then
window.DrawList:AddRect(display_rect.Min, display_rect.Max, ImGui.GetColorU32(59 --[[ImGuiCol.NavCursor]]), rounding, thickness)
else
local distance = ImTrunc(3.0 + thickness * 0.5)
display_rect:Expand(ImVec2(distance, distance))
local fully_visible = window.ClipRect:Contains(display_rect)
if not fully_visible then
window.DrawList:PushClipRect(display_rect.Min, display_rect.Max)
end
window.DrawList:AddRect(display_rect.Min, display_rect.Max, ImGui.GetColorU32(59 --[[ImGuiCol.NavCursor]]), rounding, thickness)
if not fully_visible then
window.DrawList:PopClipRect()
end
end
end
do --[[ImGui.RenderMouseCursor]]
local offset = ImVec2()
local size = ImVec2()
local uv_border = {ImVec2(), ImVec2()}
local uv_fill = {ImVec2(), ImVec2()}
--- @param base_pos ImVec2
--- @param base_scale float
--- @param mouse_cursor ImGuiMouseCursor
--- @param col_fill ImU32
--- @param col_border ImU32
--- @param col_shadow ImU32
function ImGui.RenderMouseCursor(base_pos, base_scale, mouse_cursor, col_fill, col_border, col_shadow)
local g = GImGui
if mouse_cursor <= (-1) --[[ImGuiMouseCursor.None]] or mouse_cursor >= 11 --[[ImGuiMouseCursor.COUNT]] then -- We intentionally accept out of bound values.
mouse_cursor = 0 --[[ImGuiMouseCursor.Arrow]]
end
local font_atlas = g.DrawListSharedData.FontAtlas
for _, viewport in g.Viewports:iter() do
repeat
-- We scale cursor with current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor.
if not ImFontAtlasGetMouseCursorTexData(font_atlas, mouse_cursor, offset, size, uv_border, uv_fill) then
break
end
local pos = base_pos - offset
local scale = base_scale * viewport.DpiScale
if not viewport:GetMainRect():Overlaps(ImRect(pos, pos + ImVec2(size.x + 2, size.y + 2) * scale)) then
break
end
local draw_list = ImGui.GetForegroundDrawList(viewport)
local tex_ref = font_atlas.TexRef
draw_list:PushTexture(tex_ref);
draw_list:AddImage(tex_ref, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv_fill[1], uv_fill[2], col_shadow)
draw_list:AddImage(tex_ref, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv_fill[1], uv_fill[2], col_shadow)
draw_list:AddImage(tex_ref, pos, pos + size * scale, uv_fill[1], uv_fill[2], col_border)
draw_list:AddImage(tex_ref, pos, pos + size * scale, uv_border[1], uv_border[2], col_fill)
if mouse_cursor == 8 --[[ImGuiMouseCursor.Wait]] or mouse_cursor == 9 --[[ImGuiMouseCursor.Progress]] then
local a_min = ImFmod(g.Time * 5.0, 2.0 * 3.141592653589793 --[[IM_PI]])
local a_max = a_min + 3.141592653589793 --[[IM_PI]] * 1.65
draw_list:PathArcTo(pos + ImVec2(14, -1) * scale, 6.0 * scale, a_min, a_max)
draw_list:PathStroke(col_fill, 3.0 * scale)
end
draw_list:PopTexture()
until true
end
end
end
-- Another overly complex function until we reorganize everything into a nice all-in-one helper.
-- This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) from 'ellipsis_max_x' which may be beyond it.
-- This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move.
-- (BREAKING) On 2025/04/16 we removed the 'float clip_max_x' parameters which was preceding 'float ellipsis_max' and was the same value for 99% of users.
--- @param draw_list ImDrawList
--- @param pos_min ImVec2
--- @param pos_max ImVec2
--- @param ellipsis_max_x float
--- @param text string
--- @param text_end_full? int
--- @param text_size_if_known? ImVec2
function ImGui.RenderTextEllipsis(draw_list, pos_min, pos_max, ellipsis_max_x, text, text_end_full, text_size_if_known)
local g = GImGui
if text_end_full == nil then
text_end_full = ImGui.FindRenderedTextEnd(text)
end
local text_size
if text_size_if_known then
text_size = ImVec2()
ImVec2_Copy(text_size, text_size_if_known)
else
text_size = ImGui.CalcTextSize(text, text_end_full, false, 0.0)
end
-- draw_list:AddLineV(pos_max.x, pos_min.y - 4, pos_max.y + 6, IM_COL32(0, 0, 255, 255))
-- draw_list:AddLineV(ellipsis_max_x, pos_min.y - 2, pos_max.y + 3, IM_COL32(0, 255, 0, 255))
-- FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
if text_size.x > pos_max.x - pos_min.x then
-- Hello wo...
-- | | |
-- min max ellipsis_max
-- <-> this is generally some padding value
local font = draw_list._Data.Font
local font_size = draw_list._Data.FontSize
local font_scale = draw_list._Data.FontScale
local text_end_ellipsis = nil
local baked = font:GetFontBaked(font_size)
local ellipsis_width = baked:GetCharAdvance(font.EllipsisChar) * font_scale
-- We can now claim the space between pos_max.x and ellipsis_max.x
local text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_width) - pos_min.x, 1.0)
local text_size_clipped
text_size_clipped, text_end_ellipsis = font:CalcTextSizeA(font_size, text_avail_width, 0.0, text, 1, text_end_full)
local text_size_clipped_x = text_size_clipped.x
-- Render text, render ellipsis
ImGui.RenderTextClippedEx(draw_list, pos_min, pos_max, text, 1, text_end_ellipsis, text_size, ImVec2(0.0, 0.0))
local cpu_fine_clip_rect = ImVec4(pos_min.x, pos_min.y, pos_max.x, pos_max.y)
local ellipsis_pos = ImTrunc(ImVec2(pos_min.x + text_size_clipped_x, pos_min.y))
font:RenderChar(draw_list, font_size, ellipsis_pos, ImGui.GetColorU32(0 --[[ImGuiCol.Text]]), font.EllipsisChar, cpu_fine_clip_rect)
else
ImGui.RenderTextClippedEx(draw_list, pos_min, pos_max, text, 1, text_end_full, text_size, ImVec2(0.0, 0.0))
end
if g.LogEnabled then
ImGui.LogRenderedText(pos_min, text, text_end_full)
end
end
-- Render a rectangle shaped with optional rounding and borders
--- @param p_min ImVec2
--- @param p_max ImVec2
--- @param fill_col ImU32
--- @param borders? bool
--- @param rounding? float
@native
function ImGui.RenderFrame(p_min, p_max, fill_col, borders, rounding)
if borders == nil then borders = true end
if rounding == nil then rounding = 0.0 end
local g = GImGui
local window = g.CurrentWindow
window.DrawList:AddRectFilled(p_min, p_max, fill_col, rounding)
local border_size = g.Style.FrameBorderSize
if borders and border_size > 0 then
window.DrawList:AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), ImGui.GetColorU32(6 --[[ImGuiCol.BorderShadow]]), rounding, border_size, 0)
window.DrawList:AddRect(p_min, p_max, ImGui.GetColorU32(5 --[[ImGuiCol.Border]]), rounding, border_size, 0)
end
end
--- @param p_min ImVec2
--- @param p_max ImVec2
--- @param rounding float
@native
function ImGui.RenderFrameBorder(p_min, p_max, rounding)
local g = GImGui
local window = g.CurrentWindow
local border_size = g.Style.FrameBorderSize
if border_size > 0.0 then
window.DrawList:AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), ImGui.GetColorU32(6 --[[ImGuiCol.BorderShadow]]), rounding, border_size, 0)
window.DrawList:AddRect(p_min, p_max, ImGui.GetColorU32(5 --[[ImGuiCol.Border]]), rounding, border_size, 0)
end
end
--- @param bb ImRect
--- @param col ImU32
--- @param rounding float
function ImGui.RenderColorComponentMarker(bb, col, rounding)
if bb.Min.x + 1 >= bb.Max.x then
return
end
local g = GImGui
local window = g.CurrentWindow
ImGui.RenderRectFilledInRangeH(window.DrawList, bb, col, bb.Min.x, ImMin(bb.Min.x + g.Style.ColorMarkerSize, bb.Max.x), rounding)
end
--- @param window ImGuiWindow
--- @param border_n int
--- @param border_col ImU32
--- @param border_size float
--- @param window ImGuiWindow
--- @param visibility_rect ImRect
local function ClampWindowPos(window, visibility_rect)
local size_for_clamping = ImVec2(window.Size.x, window.Size.y)
local move_from_title_bar_only = bit32.band(window.BgClickFlags, 1 --[[ImGuiWindowBgClickFlags.Move]]) == 0
if move_from_title_bar_only and window.DockNodeAsHost then
size_for_clamping.y = ImGui.GetFrameHeight() -- Not using window->TitleBarHeight() as DockNodeAsHost will report 0.0f here.
elseif move_from_title_bar_only and bit32.band(window.Flags, 1 --[[ImGuiWindowFlags.NoTitleBar]]) == 0 then
size_for_clamping.y = window.TitleBarHeight
end
-- [Luau] in place (window.Pos may be referenced elsewhere): window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max)
window.Pos.x = ImClamp(window.Pos.x, visibility_rect.Min.x - size_for_clamping.x, visibility_rect.Max.x)
window.Pos.y = ImClamp(window.Pos.y, visibility_rect.Min.y - size_for_clamping.y, visibility_rect.Max.y)
end
local function RenderWindowOuterSingleBorder(window, border_n, border_col, border_size)
local def = ImGuiResizeBorderDef[border_n + 1]
local rounding = window.WindowRounding
local border_r = GetResizeBorderRect(window, border_n, rounding, 0.0)
window.DrawList:PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN1) + ImVec2(0.5, 0.5) + def.InnerDir * rounding, rounding, def.OuterAngle - 3.141592653589793 --[[IM_PI]] * 0.25, def.OuterAngle)
window.DrawList:PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN2) + ImVec2(0.5, 0.5) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + 3.141592653589793 --[[IM_PI]] * 0.25)
window.DrawList:PathStroke(border_col, border_size)
end
local function RenderWindowOuterBorders(window)
local g = GImGui
local border_size = window.WindowBorderSize
local border_col = ImGui.GetColorU32(5 --[[ImGuiCol.Border]])
if border_size > 0.0 and (bit32.band(window.Flags, 128 --[[ImGuiWindowFlags.NoBackground]]) == 0) then
window.DrawList:AddRect(window.Pos, window.Pos + window.Size, border_col, window.WindowRounding, window.WindowBorderSize, 0)
elseif border_size > 0.0 then
if bit32.band(window.ChildFlags, 4 --[[ImGuiChildFlags.ResizeX]]) ~= 0 then -- Similar code as 'resize_border_mask' computation in UpdateWindowManualResize() but we specifically only always draw explicit child resize border.
RenderWindowOuterSingleBorder(window, 1, border_col, border_size)
end
if bit32.band(window.ChildFlags, 8 --[[ImGuiChildFlags.ResizeY]]) ~= 0 then
RenderWindowOuterSingleBorder(window, 3, border_col, border_size)
end
end
if window.ResizeBorderHovered ~= -1 or window.ResizeBorderHeld ~= -1 then
local border_n = (window.ResizeBorderHeld ~= -1) and window.ResizeBorderHeld or window.ResizeBorderHovered
local border_col_resizing = ImGui.GetColorU32((window.ResizeBorderHeld ~= -1) and 30 --[[ImGuiCol.SeparatorActive]] or 29 --[[ImGuiCol.SeparatorHovered]])
RenderWindowOuterSingleBorder(window, border_n, border_col_resizing, ImMax(2.0, window.WindowBorderSize)) -- Thicker than usual
end
if g.Style.FrameBorderSize > 0 and (bit32.band(window.Flags, 1 --[[ImGuiWindowFlags.NoTitleBar]]) == 0) and not window.DockIsActive then
local y = window.Pos.y + window.TitleBarHeight - 1
window.DrawList:AddLineH(window.Pos.x + border_size, window.Pos.x + window.Size.x - border_size, y, border_col, g.Style.FrameBorderSize, 393216 --[[ImDrawFlags.StrokeCenterBiased]])
end
end
-- Draw background and borders
-- Draw and handle scrollbars
--- @param window ImGuiWindow
--- @param title_bar_rect ImRect
--- @param title_bar_is_highlight bool
--- @param handle_borders_and_resize_grips bool
--- @param resize_grip_col ImU32[]
--- @param resize_grip_draw_size float
local function RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_col, resize_grip_draw_size)
local g = GImGui
local style = g.Style
local flags = window.Flags
-- Draw window + handle manual resize
-- As we highlight the title bar when want_focus is set, multiple reappearing windows will have their title bar highlighted on their reappearing frame.
local window_rounding = window.WindowRounding
local window_border_size = window.WindowBorderSize
if window.Collapsed then
-- Title bar only
local backup_border_size = style.FrameBorderSize
g.Style.FrameBorderSize = window.WindowBorderSize
local title_bar_col = ImGui.GetColorU32((title_bar_is_highlight and g.NavCursorVisible) and 11 --[[ImGuiCol.TitleBgActive]] or 12 --[[ImGuiCol.TitleBgCollapsed]])
ImGui.RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding)
g.Style.FrameBorderSize = backup_border_size
else
-- Window background
if bit32.band(flags, 128 --[[ImGuiWindowFlags.NoBackground]]) == 0 then
local bg_col = ImGui.GetColorU32(GetWindowBgColorIdx(window))
-- Adjust alpha. For docking
local override_alpha = false
local alpha = 1.0
if bit32.band(g.NextWindowData.HasFlags, 64 --[[ImGuiNextWindowDataFlags.HasBgAlpha]]) ~= 0 then
alpha = g.NextWindowData.BgAlphaVal
override_alpha = true
end
if override_alpha then
bg_col = bit32.bor(bit32.band(bg_col, bit32.bnot(4278190080 --[[IM_COL32_A_MASK]])), bit32.lshift(IM_F32_TO_INT8_SAT(alpha), 24 --[[IM_COL32_A_SHIFT]]))
end
-- Render, for docked windows and host windows we ensure BG goes before decorations
if window.DockIsActive then window.DockNode.LastBgColor = bg_col end
if bit32.band(flags, 8388608 --[[ImGuiWindowFlags.DockNodeHost]]) ~= 0 then bg_col = 0 end
if bit32.band(bg_col, 4278190080 --[[IM_COL32_A_MASK]]) ~= 0 then
local bg_rect = ImRect(window.Pos + ImVec2(0, window.TitleBarHeight), window.Pos + window.Size)
local bg_rounding_flags
if window.DockIsActive then
bg_rounding_flags = ImGui.CalcRoundingFlagsForRectInRect(bg_rect, window.DockNode.HostWindow:Rect(), 0.0)
else
bg_rounding_flags = (bit32.band(flags, 1 --[[ImGuiWindowFlags.NoTitleBar]]) ~= 0) and 240 --[[ImDrawFlags.RoundAll]] or 192 --[[ImDrawFlags.RoundBottom]]
end
local bg_draw_list = window.DockIsActive and window.DockNode.HostWindow.DrawList or window.DrawList
if window.DockIsActive then bg_draw_list:ChannelsSetCurrent(0) end
bg_draw_list:AddRectFilled(bg_rect.Min, bg_rect.Max, bg_col, window_rounding, bg_rounding_flags)
if window.DockIsActive then bg_draw_list:ChannelsSetCurrent(1) end
end
end
if window.DockIsActive then window.DockNode.IsBgDrawnThisFrame = true end
-- Title bar
-- (when docked, DockNode are drawing their own title bar. Individual windows however do NOT set the _NoTitleBar flag,
-- in order for their pos/size to be matching their undocking state.)
if bit32.band(flags, 1 --[[ImGuiWindowFlags.NoTitleBar]]) == 0 and not window.DockIsActive then
local title_bar_col = ImGui.GetColorU32(title_bar_is_highlight and 11 --[[ImGuiCol.TitleBgActive]] or 10 --[[ImGuiCol.TitleBg]])
window.DrawList:AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, 48 --[[ImDrawFlags.RoundTop]])
end
-- Menu bar
if bit32.band(flags, 1024 --[[ImGuiWindowFlags.MenuBar]]) ~= 0 then
local menu_bar_rect = window:MenuBarRect()
menu_bar_rect:ClipWith(window:Rect()) -- Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them.
window.DrawList:AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, ImGui.GetColorU32(13 --[[ImGuiCol.MenuBarBg]]), (bit32.band(flags, 1 --[[ImGuiWindowFlags.NoTitleBar]]) ~= 0) and window_rounding or 0.0, 48 --[[ImDrawFlags.RoundTop]])
if style.FrameBorderSize > 0.0 and menu_bar_rect.Max.y < window.Pos.y + window.Size.y then
window.DrawList:AddLineH(menu_bar_rect.Min.x + window_border_size, menu_bar_rect.Max.x - window_border_size, menu_bar_rect.Max.y, ImGui.GetColorU32(5 --[[ImGuiCol.Border]]), style.FrameBorderSize, 393216 --[[ImDrawFlags.StrokeCenterBiased]])
end
end
-- Docking: Unhide tab bar (small triangle in the corner), drag from small triangle to quickly undock
local node = window.DockNode
if window.DockIsActive and node:IsHiddenTabBar() and not node:IsNoTabBar() then
local unhide_sz_draw = ImTrunc(g.FontSize * 0.70)
local unhide_sz_hit = ImTrunc(g.FontSize * 0.55)
local p = node.Pos
local r = ImRect(p, p + ImVec2(unhide_sz_hit, unhide_sz_hit))
local unhide_id = window:GetID("#UNHIDE")
ImGui.KeepAliveID(unhide_id)
local pressed, hovered, held = ImGui.ButtonBehavior(r, unhide_id, 2048 --[[ImGuiButtonFlags.FlattenChildren]])
if pressed then
node.WantHiddenTabBarToggle = true
elseif held and ImGui.IsMouseDragging(0) then
ImGui.StartMouseMovingWindowOrNode(window, node, true) -- Undock from tab-bar triangle = same as window/collapse menu button
end
-- FIXME-DOCK: Ideally we'd use ImGuiCol.TitleBgActive/ImGuiCol.TitleBg here, but neither is guaranteed to be visible enough at this sort of size..
local col = ImGui.GetColorU32(((held and hovered) or (node.IsFocused and not hovered)) and 24 --[[ImGuiCol.ButtonActive]] or (hovered and 23 --[[ImGuiCol.ButtonHovered]] or 22 --[[ImGuiCol.Button]]))
window.DrawList:AddTriangleFilled(p, p + ImVec2(unhide_sz_draw, 0.0), p + ImVec2(0.0, unhide_sz_draw), col)
end
-- Scrollbars
if window.ScrollbarX then
ImGui.Scrollbar(1 --[[ImGuiAxis.X]])
end
if window.ScrollbarY then
ImGui.Scrollbar(2 --[[ImGuiAxis.Y]])
end
-- Render resize grips (after their input handling so we don't have a frame of latency)
if handle_borders_and_resize_grips and (bit32.band(flags, 2 --[[ImGuiWindowFlags.NoResize]]) == 0) then
for resize_grip_n = 1, #ImGuiResizeGripDef do
repeat
local col = resize_grip_col[resize_grip_n]
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
break
end
local grip = ImGuiResizeGripDef[resize_grip_n]
local corner = ImLerp(window.Pos, window.Pos + window.Size, grip.CornerPosN)
local border_inner = IM_ROUND(window_border_size * 0.5)
window.DrawList:PathLineTo(corner + grip.InnerDir * ((resize_grip_n % 2 == 0) and ImVec2(border_inner, resize_grip_draw_size) or ImVec2(resize_grip_draw_size, border_inner)))
window.DrawList:PathLineTo(corner + grip.InnerDir * ((resize_grip_n % 2 == 0) and ImVec2(resize_grip_draw_size, border_inner) or ImVec2(border_inner, resize_grip_draw_size)))
window.DrawList:PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + border_inner), corner.y + grip.InnerDir.y * (window_rounding + border_inner)), window_rounding, grip.AngleMin12, grip.AngleMax12)
window.DrawList:PathFillConvex(col)
until true
end
end
-- Borders (for dock node host they will be rendered over after the tab bar)
if handle_borders_and_resize_grips and not window.DockNodeAsHost then
RenderWindowOuterBorders(window)
end
end
window.DC.NavLayerCurrent = 0 --[[ImGuiNavLayer.Main]]
end
ImGui.RenderWindowOuterBorders = RenderWindowOuterBorders
-- When inside a dock node, this is handled in DockNodeCalcTabBarLayout() instead.
-- Render title text, collapse button, close button
-- Returns the updated 'open' value (nil when there is no close button)
--- @param window ImGuiWindow
--- @param title_bar_rect ImRect
--- @param name string
--- @param open bool?
--- @return bool?
local function RenderWindowTitleBarContents(window, title_bar_rect, name, open)
local g = GImGui
local style = g.Style
local flags = window.Flags
local has_close_button = (open ~= nil)
local has_collapse_button = bit32.band(flags, 32 --[[ImGuiWindowFlags.NoCollapse]]) == 0 and (style.WindowMenuButtonPosition ~= (-1) --[[ImGuiDir.None]])
-- Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
-- FIXME-NAV: Might want (or not?) to set the equivalent of ImGuiButtonFlags.NoNavFocus so that mouse clicks on standard title bar items don't necessarily set nav/keyboard ref?
local item_flags_backup = g.CurrentItemFlags
g.CurrentItemFlags = bit32.bor(g.CurrentItemFlags, 4 --[[ImGuiItemFlags.NoNavDefaultFocus]])
window.DC.NavLayerCurrent = 1 --[[ImGuiNavLayer.Menu]]
-- Layout buttons
-- FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
local pad_l = g.Style.FramePadding.x
local pad_r = g.Style.FramePadding.x
local button_sz = g.FontSize
local close_button_pos
local collapse_button_pos
if has_close_button then
close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y)
pad_r = pad_r + button_sz + style.ItemInnerSpacing.x
end
if has_collapse_button and style.WindowMenuButtonPosition == 1 --[[ImGuiDir.Right]] then
collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y)
pad_r = pad_r + button_sz + style.ItemInnerSpacing.x
end
if has_collapse_button and style.WindowMenuButtonPosition == 0 --[[ImGuiDir.Left]] then
collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y + style.FramePadding.y)
pad_l = pad_l + button_sz + style.ItemInnerSpacing.x
end
-- Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
if has_collapse_button then
if ImGui.CollapseButton(window:GetID("#COLLAPSE"), collapse_button_pos) then
window.WantCollapseToggle = true -- Defer actual collapsing to next frame as we are too far in the Begin() function
end
end
-- Close button
if has_close_button then
local backup_item_flags = g.CurrentItemFlags
g.CurrentItemFlags = bit32.bor(g.CurrentItemFlags, 131072 --[[ImGuiItemFlags.NoFocus]])
if ImGui.CloseButton(window:GetID("#CLOSE"), close_button_pos) then
open = false
end
g.CurrentItemFlags = backup_item_flags
end
window.DC.NavLayerCurrent = 0 --[[ImGuiNavLayer.Main]]
g.CurrentItemFlags = item_flags_backup
-- Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
-- FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
local marker_size_x = (bit32.band(flags, 262144 --[[ImGuiWindowFlags.UnsavedDocument]]) ~= 0) and (button_sz * 0.80) or 0.0
local text_size = ImGui.CalcTextSize(name, nil, true) + ImVec2(marker_size_x, 0.0)
-- As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
-- while uncentered title text will still reach edges correctly.
if (pad_l > style.FramePadding.x) then
pad_l = pad_l + g.Style.ItemInnerSpacing.x
end
if (pad_r > style.FramePadding.x) then
pad_r = pad_r + g.Style.ItemInnerSpacing.x
end
if (style.WindowTitleAlign.x > 0.0 and style.WindowTitleAlign.x < 1.0) then
local centerness = ImSaturate(1.0 - ImFabs(style.WindowTitleAlign.x - 0.5) * 2.0) -- 0.0 on either edges, 1.0 on center
local pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect:GetWidth() - pad_l - pad_r - text_size.x)
pad_l = ImMax(pad_l, pad_extend * centerness)
pad_r = ImMax(pad_r, pad_extend * centerness)
end
local layout_r = ImRect(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y, title_bar_rect.Max.x - pad_r, title_bar_rect.Max.y)
local clip_r = ImRect(layout_r.Min.x, layout_r.Min.y, ImMin(layout_r.Max.x + g.Style.ItemInnerSpacing.x, title_bar_rect.Max.x), layout_r.Max.y)
if bit32.band(flags, 262144 --[[ImGuiWindowFlags.UnsavedDocument]]) ~= 0 then
local marker_pos = ImVec2()
marker_pos.x = ImClamp(layout_r.Min.x + (layout_r:GetWidth() - text_size.x) * style.WindowTitleAlign.x + text_size.x, layout_r.Min.x, layout_r.Max.x)
marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5
if marker_pos.x > layout_r.Min.x then
ImGui.RenderBullet(window.DrawList, marker_pos, ImGui.GetColorU32(58 --[[ImGuiCol.UnsavedMarker]]))
clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - math.floor(marker_size_x * 0.5))
end
end
-- if (g.IO.KeyShift) window.DrawList:AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)) -- [DEBUG]
-- if (g.IO.KeyCtrl) window.DrawList:AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)) -- [DEBUG]
ImGui.RenderTextClipped(layout_r.Min, layout_r.Max, name, nil, text_size, style.WindowTitleAlign, clip_r)
return open
end
--- @param window ImGuiWindow
--- @param flags ImGuiWindowFlags
--- @param parent_window? ImGuiWindow
function ImGui.UpdateWindowParentAndRootLinks(window, flags, parent_window)
window.ParentWindow = parent_window
window.RootWindow = window
window.RootWindowPopupTree = window
window.RootWindowDockTree = window
window.RootWindowForTitleBarHighlight = window
window.RootWindowForNav = window
if parent_window and (bit32.band(flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0) and (bit32.band(flags, 33554432 --[[ImGuiWindowFlags.Tooltip]]) == 0) then
window.RootWindowDockTree = parent_window.RootWindowDockTree
if not window.DockIsActive and (bit32.band(parent_window.Flags, 8388608 --[[ImGuiWindowFlags.DockNodeHost]]) == 0) then
window.RootWindow = parent_window.RootWindow
end
end
if parent_window and (bit32.band(flags, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0) then
window.RootWindowPopupTree = parent_window.RootWindowPopupTree
end
if parent_window and (bit32.band(flags, 134217728 --[[ImGuiWindowFlags.Modal]]) == 0) and (bit32.band(flags, bit32.bor(16777216 --[[ImGuiWindowFlags.ChildWindow]], 67108864 --[[ImGuiWindowFlags.Popup]], 33554432 --[[ImGuiWindowFlags.Tooltip]])) ~= 0) then -- FIXME: simply use _NoTitleBar ?
window.RootWindowForTitleBarHighlight = parent_window.RootWindowForTitleBarHighlight
end
while bit32.band(window.RootWindowForNav.ChildFlags, 256 --[[ImGuiChildFlags.NavFlattened]]) ~= 0 do
assert(window.RootWindowForNav.ParentWindow ~= nil)
window.RootWindowForNav = window.RootWindowForNav.ParentWindow
end
end
-- [EXPERIMENTAL] Called by Begin(). NextWindowData is valid at this point.
-- This is designed as a toy/test-bed for
--- @param window ImGuiWindow
function ImGui.UpdateWindowSkipRefresh(window)
local g = GImGui
window.SkipRefresh = false
if bit32.band(g.NextWindowData.HasFlags, 1024 --[[ImGuiNextWindowDataFlags.HasRefreshPolicy]]) == 0 then
return
end
if bit32.band(g.NextWindowData.RefreshFlagsVal, 1 --[[ImGuiWindowRefreshFlags.TryToAvoidRefresh]]) ~= 0 then
-- [Port] SetNextWindowCached(): refresh on any change since the last refresh (and one frame after being busy)
if g.NextWindowData.CacheKeySet then
local function related(w) return w ~= nil and (w.RootWindow == window.RootWindow or ImGui.IsWindowWithinBeginStackOf(w.RootWindow, window)) end
local busy = window.Appearing or window.Hidden or window.DockIsActive or related(g.HoveredWindow) or related(g.NavWindow) or related(g.ActiveIdWindow) or (g.MovingWindow ~= nil and related(g.MovingWindow))
local c, key, ds, tex = window.CacheSnap, g.NextWindowData.CacheKeyVal, g.IO.DisplaySize, g.IO.Fonts.TexData
if busy or c == nil or c.Busy or c.Key ~= key or c.Tex ~= tex or c.Collapsed ~= window.Collapsed
or c.PosX ~= window.Pos.x or c.PosY ~= window.Pos.y or c.SizeX ~= window.SizeFull.x or c.SizeY ~= window.SizeFull.y
or c.ScrollX ~= window.Scroll.x or c.ScrollY ~= window.Scroll.y or window.ScrollTarget.x < 3.4028234663853e+38 --[[FLT_MAX]] or window.ScrollTarget.y < 3.4028234663853e+38 --[[FLT_MAX]]
or c.DisplayX ~= ds.x or c.DisplayY ~= ds.y then
c = c or {}
window.CacheSnap = c
c.Busy, c.Key, c.Tex, c.Collapsed = busy, key, tex, window.Collapsed
c.PosX, c.PosY, c.SizeX, c.SizeY = window.Pos.x, window.Pos.y, window.SizeFull.x, window.SizeFull.y
c.ScrollX, c.ScrollY, c.DisplayX, c.DisplayY = window.Scroll.x, window.Scroll.y, ds.x, ds.y
return
end
-- skip: widgets early-out on SkipItems and Begin() returns visible=false; End() restores it
window.CacheSkipItemsBackup = window.SkipItems
window.SkipItems = true
end
-- FIXME-IDLE: Tests for e.g. mouse clicks or keyboard while focused.
if window.Appearing then -- If currently appearing
return
end
if window.Hidden then -- If was hidden (previous frame)
return
end
if bit32.band(g.NextWindowData.RefreshFlagsVal, 2 --[[ImGuiWindowRefreshFlags.RefreshOnHover]]) ~= 0 and g.HoveredWindow then
if window.RootWindow == g.HoveredWindow.RootWindow or ImGui.IsWindowWithinBeginStackOf(g.HoveredWindow.RootWindow, window) then
return
end
end
if bit32.band(g.NextWindowData.RefreshFlagsVal, 4 --[[ImGuiWindowRefreshFlags.RefreshOnFocus]]) ~= 0 and g.NavWindow then
if window.RootWindow == g.NavWindow.RootWindow or ImGui.IsWindowWithinBeginStackOf(g.NavWindow.RootWindow, window) then
return
end
end
window.DrawList = nil
window.SkipRefresh = true
end
end
--- @param window ImGuiWindow
local function SetWindowActiveForSkipRefresh(window)
window.Active = true
for _, child in window.DC.ChildWindows:iter() do
if not child.Hidden then
child.Active = true
child.SkipRefresh = true
SetWindowActiveForSkipRefresh(child)
end
end
end
--- @param window ImGuiWindow?
local function SetCurrentWindow(window)
local g = GImGui
--- @diagnostic disable-next-line
g.CurrentWindow = window
g.CurrentTable = (window and window.DC.CurrentTableIdx ~= -1) and g.Tables:GetByIndex(window.DC.CurrentTableIdx) or nil
if window then
if bit32.band(g.IO.BackendFlags, 16 --[[ImGuiBackendFlags.RendererHasTextures]]) ~= 0 then
local viewport = window.Viewport
g.CurrentPixelDensity = (viewport.FramebufferScale.x ~= 0.0) and viewport.FramebufferScale.x or g.IO.DisplayFramebufferScale.x
end
local backup_skip_items = window.SkipItems
window.SkipItems = false
ImGui.UpdateCurrentFontSize(0.0)
window.SkipItems = backup_skip_items
ImGui.NavUpdateCurrentWindowIsScrollPushableX()
end
end
-- TODO:
function ImGui.GcCompactTransientMiscBuffers()
local g = GImGui
g.ItemFlagsStack:clear()
g.GroupStack:clear()
g.InputTextLineIndex:clear()
g.MultiSelectTempDataStacked = 0
g.MultiSelectTempData:clear()
ImGui.TableGcCompactSettings()
for _, atlas in g.FontAtlases:iter() do
atlas:CompactCache()
end
end
-- Free up/compact internal window buffers, we can use this when a window becomes unused.
-- Not freed:
-- - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data)
-- This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
-- FIXME: Consider exposing of elaborating GC policy, e.g. being able to trim excessive ImDrawList gaps. (#9303)
--- @param window ImGuiWindow
function ImGui.GcCompactTransientWindowBuffers(window)
window.MemoryCompacted = true
window.MemoryDrawListIdxCapacity = ImMin(math.floor(window.DrawList.IdxBuffer.Size * 1.05), window.DrawList.IdxBuffer.Capacity)
window.MemoryDrawListVtxCapacity = ImMin(math.floor(window.DrawList.VtxBuffer.Size * 1.05), window.DrawList.VtxBuffer.Capacity)
window.IDStack:clear()
window.DrawList:_ClearFreeMemory()
window.DC.ChildWindows:clear()
window.DC.ItemWidthStack:clear()
window.DC.TextWrapPosStack:clear()
end
--- @param window ImGuiWindow
function ImGui.GcAwakeTransientWindowBuffers(window)
-- We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
-- The other buffers tends to amortize much faster.
window.MemoryCompacted = false
window.DrawList.IdxBuffer:reserve(window.MemoryDrawListIdxCapacity)
window.DrawList.VtxBuffer:reserve(window.MemoryDrawListVtxCapacity)
window.MemoryDrawListIdxCapacity = 0
window.MemoryDrawListVtxCapacity = 0
end
function ImGui.GetWindowWidth()
local window = GImGui.CurrentWindow
return window.Size.x
end
--- @param window ImGuiWindow
--- @param pos ImVec2
--- @param cond? ImGuiCond
function ImGui.SetWindowPos(window, pos, cond)
-- Overloads: SetWindowPos(pos, cond) / SetWindowPos(name, pos, cond) / SetWindowPos(window, pos, cond)
if type(window) == "string" then
window = ImGui.FindWindowByName(window)
if window == nil then return end
elseif window.SetWindowPosAllowFlags == nil then
window, pos, cond = ImGui.GetCurrentWindowRead(), window, pos
end
if cond == nil then cond = 0 end
-- Test condition (NB: bit 0 is always true) and clear flags for next time
if (cond ~= 0) and (bit32.band(window.SetWindowPosAllowFlags, cond) == 0) then
return
end
assert(cond == 0 or ImIsPowerOfTwo(cond)) -- Make sure the user doesn't attempt to combine multiple condition flags.
window.SetWindowPosAllowFlags = bit32.band(window.SetWindowPosAllowFlags, bit32.bnot(bit32.bor(2 --[[ImGuiCond.Once]], 4 --[[ImGuiCond.FirstUseEver]], 8 --[[ImGuiCond.Appearing]])))
ImVec2_Copy(window.SetWindowPosVal, ImVec2(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]]))
-- Set
local old_pos = ImVec2()
ImVec2_Copy(old_pos, window.Pos)
window.Pos.x = ImTrunc(pos.x)
window.Pos.y = ImTrunc(pos.y)
local offset = window.Pos - old_pos
if offset.x == 0 and offset.y == 0 then
return
end
ImGui.MarkIniSettingsDirty(window)
-- FIXME: share code with TranslateWindow(), need to confirm whether the 3 rect modified by TranslateWindow() are desirable here.
ImVec2_Copy(window.DC.CursorPos, window.DC.CursorPos + offset) -- As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor
ImVec2_Copy(window.DC.CursorMaxPos, window.DC.CursorMaxPos + offset) -- And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
ImVec2_Copy(window.DC.IdealMaxPos, window.DC.IdealMaxPos + offset)
ImVec2_Copy(window.DC.CursorStartPos, window.DC.CursorStartPos + offset)
end
--- @param window ImGuiWindow
--- @param size ImVec2
--- @param cond? ImGuiCond
function ImGui.SetWindowSize(window, size, cond)
-- Overloads: SetWindowSize(size, cond) / SetWindowSize(name, size, cond) / SetWindowSize(window, size, cond)
if type(window) == "string" then
window = ImGui.FindWindowByName(window)
if window == nil then return end
elseif window.SetWindowSizeAllowFlags == nil then
window, size, cond = GImGui.CurrentWindow, window, size
end
if cond == nil then cond = 0 end
-- Test condition (NB: bit 0 is always true) and clear flags for next time
if ((cond ~= 0) and bit32.band(window.SetWindowSizeAllowFlags, cond) == 0) then
return
end
assert(cond == 0 or ImIsPowerOfTwo(cond)) -- Make sure the user doesn't attempt to combine multiple condition flags.
window.SetWindowSizeAllowFlags = bit32.band(window.SetWindowSizeAllowFlags, bit32.bnot(bit32.bor(2 --[[ImGuiCond.Once]], 4 --[[ImGuiCond.FirstUseEver]], 8 --[[ImGuiCond.Appearing]])))
-- Enable auto-fit (not done in BeginChild() path unless appearing or combined with ImGuiChildFlags.AlwaysAutoResize)
if bit32.band(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) == 0 or window.Appearing or bit32.band(window.ChildFlags, 64 --[[ImGuiChildFlags.AlwaysAutoResize]]) ~= 0 then
window.AutoFitFramesX = (size.x <= 0.0) and 2 or 0
end
if bit32.band(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) == 0 or window.Appearing or bit32.band(window.ChildFlags, 64 --[[ImGuiChildFlags.AlwaysAutoResize]]) ~= 0 then
window.AutoFitFramesY = (size.y <= 0.0) and 2 or 0
end
-- Set
local old_size_x, old_size_y = window.SizeFull.x, window.SizeFull.y
if size.x <= 0.0 then
window.AutoFitOnlyGrows = false
else
window.SizeFull.x = IM_TRUNC(size.x)
end
if size.y <= 0.0 then
window.AutoFitOnlyGrows = false
else
window.SizeFull.y = IM_TRUNC(size.y)
end
if old_size_x ~= window.SizeFull.x or old_size_y ~= window.SizeFull.y then
ImGui.MarkIniSettingsDirty(window)
end
end
--- Overloads: SetWindowCollapsed(collapsed, cond) / SetWindowCollapsed(name, collapsed, cond) / SetWindowCollapsed(window, collapsed, cond)
function ImGui.SetWindowCollapsed(window, collapsed, cond)
if type(window) == "string" then
window = ImGui.FindWindowByName(window)
if window == nil then return end
elseif type(window) == "boolean" then
window, collapsed, cond = GImGui.CurrentWindow, window, collapsed
end
if cond == nil then cond = 0 end
-- Test condition (NB: bit 0 is always true) and clear flags for next time
if cond ~= 0 and bit32.band(window.SetWindowCollapsedAllowFlags, cond) == 0 then
return
end
window.SetWindowCollapsedAllowFlags = bit32.band(window.SetWindowCollapsedAllowFlags, bit32.bnot(bit32.bor(2 --[[ImGuiCond.Once]], 4 --[[ImGuiCond.FirstUseEver]], 8 --[[ImGuiCond.Appearing]])))
-- Queue applying in Begin()
if window.WantCollapseToggle then
window.Collapsed = not window.Collapsed
end
window.WantCollapseToggle = (window.Collapsed ~= collapsed)
end
--- @param window ImGuiWindow
--- @param pos ImVec2
--- @param size ImVec2
function ImGui.SetWindowHitTestHole(window, pos, size)
assert(window.HitTestHoleSize.x == 0) -- We don't support multiple holes/hit test filters
window.HitTestHoleSize = ImVec2(math.floor(size.x), math.floor(size.y))
window.HitTestHoleOffset = ImVec2(math.floor(pos.x - window.Pos.x), math.floor(pos.y - window.Pos.y))
end
function ImGui.IsWindowCollapsed()
return ImGui.GetCurrentWindowRead().Collapsed
end
function ImGui.IsWindowAppearing()
return ImGui.GetCurrentWindowRead().Appearing
end
function ImGui.GetWindowHeight()
return GImGui.CurrentWindow.Size.y
end
--- @return ImVec2 # Ref, do not modify
function ImGui.GetWindowPos()
return GImGui.CurrentWindow.Pos
end
--- @return ImVec2 # Ref, do not modify
function ImGui.GetWindowSize()
return ImGui.GetCurrentWindowRead().Size
end
-- Overloads: SetWindowFocus() / SetWindowFocus(name). Passing nil name removes focus.
--- @param name? string
function ImGui.SetWindowFocus(...)
if select("#", ...) == 0 then
ImGui.FocusWindow(GImGui.CurrentWindow)
return
end
local name = ...
if name then
local window = ImGui.FindWindowByName(name)
if window then
ImGui.FocusWindow(window)
end
else
ImGui.FocusWindow(nil)
end
end
--- @param window ImGuiWindow
function ImGui.SetWindowHiddenAndSkipItemsForCurrentFrame(window)
window.Hidden = true
window.SkipItems = true
window.HiddenFramesCanSkipItems = 1
end
--- @param pos ImVec2
--- @param cond? ImGuiCond
--- @param pivot? ImVec2
function ImGui.SetNextWindowPos(pos, cond, pivot)
if cond == nil then cond = 0 end
if pivot == nil then pivot = ImVec2(0, 0) end
local g = GImGui
assert(cond == 0 or ImIsPowerOfTwo(cond)) -- Make sure the user doesn't attempt to combine multiple condition flags.
g.NextWindowData.HasFlags = bit32.bor(g.NextWindowData.HasFlags, 1 --[[ImGuiNextWindowDataFlags.HasPos]])
ImVec2_Copy(g.NextWindowData.PosVal, pos)
ImVec2_Copy(g.NextWindowData.PosPivotVal, pivot)
g.NextWindowData.PosCond = (cond ~= 0) and cond or 1 --[[ImGuiCond.Always]]
g.NextWindowData.PosUndock = true
end
--- @param size ImVec2
--- @param cond? ImGuiCond
function ImGui.SetNextWindowSize(size, cond)
if cond == nil then cond = 0 end
local g = GImGui
assert(cond == 0 or ImIsPowerOfTwo(cond)) -- Make sure the user doesn't attempt to combine multiple condition flags.
g.NextWindowData.HasFlags = bit32.bor(g.NextWindowData.HasFlags, 2 --[[ImGuiNextWindowDataFlags.HasSize]])
ImVec2_Copy(g.NextWindowData.SizeVal, size)
g.NextWindowData.SizeCond = (cond ~= 0) and cond or 1 --[[ImGuiCond.Always]]
end
-- For each axis:
-- - Use 0.0f as min or FLT_MAX as max if you don't want limits, e.g. size_min = (500.0f, 0.0f), size_max = (FLT_MAX, FLT_MAX) sets a minimum width.
-- - Use -1 for both min and max of same axis to preserve current size which itself is a constraint.
-- - See "Demo->Examples->Constrained-resizing window" for examples.
--- @param size_min ImVec2
--- @param size_max ImVec2
--- @param custom_callback? function
--- @param custom_callback_user_data? any
function ImGui.SetNextWindowSizeConstraints(size_min, size_max, custom_callback, custom_callback_user_data)
local g = GImGui
g.NextWindowData.HasFlags = bit32.bor(g.NextWindowData.HasFlags, 16 --[[ImGuiNextWindowDataFlags.HasSizeConstraint]])
g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max)
g.NextWindowData.SizeCallback = custom_callback
g.NextWindowData.SizeCallbackUserData = custom_callback_user_data
end
--- @param alpha float
function ImGui.SetNextWindowBgAlpha(alpha)
local g = GImGui
g.NextWindowData.HasFlags = bit32.bor(g.NextWindowData.HasFlags, 64 --[[ImGuiNextWindowDataFlags.HasBgAlpha]])
g.NextWindowData.BgAlphaVal = alpha
end
function ImGui.GetWindowDrawList()
local window = ImGui.GetCurrentWindow()
return window.DrawList
end
--- @return ImFontBaked
function ImGui.GetFontBaked()
return GImGui.FontBaked
end
function ImGui.GetFont()
return GImGui.Font
end
-- Get current font size (= height in pixels) of current font, with global scale factors applied.
-- - Use style.FontSizeBase to get value before global scale factors.
-- - recap: ImGui.GetFontSize() == style.FontSizeBase * (style.FontScaleMain * style.FontScaleDpi * other_scaling_factors)
function ImGui.GetFontSize()
return GImGui.FontSize
end
--- @return ImVec2
function ImGui.GetFontTexUvWhitePixel()
return GImGui.DrawListSharedData.TexUvWhitePixel
end
-- This is a shortcut for not taking ownership of 100+ keys, frequently used by drag operations.
-- FIXME: It might be undesirable that this will likely disable KeyOwner-aware shortcuts systems. Consider a more fine-tuned version if needed?
function ImGui.SetActiveIdUsingAllKeyboardKeys()
local g = GImGui
assert(g.ActiveId ~= 0)
g.ActiveIdUsingNavDirMask = (bit32.lshift(1, 4 --[[ImGuiDir.COUNT]]) - 1)
g.ActiveIdUsingAllKeyboardKeys = true
ImGui.NavMoveRequestCancel()
end
--- @return ImVec2 # Ref
function ImGui.GetItemRectMin()
local g = GImGui
return g.LastItemData.Rect.Min
end
--- @return ImVec2 # Ref
function ImGui.GetItemRectMax()
local g = GImGui
return g.LastItemData.Rect.Max
end
--- @param name string
--- @param id ImGuiID
--- @param size_arg ImVec2
--- @param child_flags ImGuiChildFlags
--- @param window_flags ImGuiWindowFlags
function ImGui.BeginChildEx(name, id, size_arg, child_flags, window_flags)
local g = GImGui
local parent_window = g.CurrentWindow
assert(id ~= 0)
if child_flags == nil then child_flags = 0 end
if window_flags == nil then window_flags = 0 end
if size_arg == nil then size_arg = ImVec2(0, 0) end
if bit32.band(g.NextWindowData.HasFlags, 512 --[[ImGuiNextWindowDataFlags.HasChildFlags]]) ~= 0 then
child_flags = bit32.band(bit32.bor(child_flags, g.NextWindowData.ChildFlagsSet), bit32.bnot(g.NextWindowData.ChildFlagsClear))
end
-- Sanity check as it is likely that some user will accidentally pass ImGuiWindowFlags into the ImGuiChildFlags argument.
local ImGuiChildFlags_SupportedMask_ = bit32.bor(1 --[[ImGuiChildFlags.Borders]], 2 --[[ImGuiChildFlags.AlwaysUseWindowPadding]], 4 --[[ImGuiChildFlags.ResizeX]], 8 --[[ImGuiChildFlags.ResizeY]], 16 --[[ImGuiChildFlags.AutoResizeX]], 32 --[[ImGuiChildFlags.AutoResizeY]], 64 --[[ImGuiChildFlags.AlwaysAutoResize]], 128 --[[ImGuiChildFlags.FrameStyle]], 256 --[[ImGuiChildFlags.NavFlattened]])
-- IM_UNUSED(ImGuiChildFlags_SupportedMask_)
assert(bit32.band(child_flags, bit32.bnot(ImGuiChildFlags_SupportedMask_)) == 0, "Illegal ImGuiChildFlags value. Did you pass ImGuiWindowFlags values instead of ImGuiChildFlags?")
assert(bit32.band(window_flags, 64 --[[ImGuiWindowFlags.AlwaysAutoResize]]) == 0, "Cannot specify ImGuiWindowFlags.AlwaysAutoResize for BeginChild(). Use ImGuiChildFlags.AlwaysAutoResize!")
if bit32.band(child_flags, 64 --[[ImGuiChildFlags.AlwaysAutoResize]]) ~= 0 then
assert(bit32.band(child_flags, bit32.bor(4 --[[ImGuiChildFlags.ResizeX]], 8 --[[ImGuiChildFlags.ResizeY]])) == 0, "Cannot use ImGuiChildFlags.ResizeX or ImGuiChildFlags.ResizeY with ImGuiChildFlags.AlwaysAutoResize!")
assert(bit32.band(child_flags, bit32.bor(16 --[[ImGuiChildFlags.AutoResizeX]], 32 --[[ImGuiChildFlags.AutoResizeY]])) ~= 0, "Must use ImGuiChildFlags.AutoResizeX or ImGuiChildFlags.AutoResizeY with ImGuiChildFlags.AlwaysAutoResize!")
end
if bit32.band(child_flags, 16 --[[ImGuiChildFlags.AutoResizeX]]) ~= 0 then
child_flags = bit32.band(child_flags, bit32.bnot(4 --[[ImGuiChildFlags.ResizeX]]))
end
if bit32.band(child_flags, 32 --[[ImGuiChildFlags.AutoResizeY]]) ~= 0 then
child_flags = bit32.band(child_flags, bit32.bnot(8 --[[ImGuiChildFlags.ResizeY]]))
end
-- Set window flags
window_flags = bit32.bor(window_flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]], 1 --[[ImGuiWindowFlags.NoTitleBar]], 524288 --[[ImGuiWindowFlags.NoDocking]])
window_flags = bit32.bor(window_flags, bit32.band(parent_window.Flags, 4 --[[ImGuiWindowFlags.NoMove]])) -- Inherit the NoMove flag
if bit32.band(child_flags, bit32.bor(16 --[[ImGuiChildFlags.AutoResizeX]], 32 --[[ImGuiChildFlags.AutoResizeY]], 64 --[[ImGuiChildFlags.AlwaysAutoResize]])) ~= 0 then
window_flags = bit32.bor(window_flags, 64 --[[ImGuiWindowFlags.AlwaysAutoResize]]) -- FIXME: Would be sane to not make single-axis flag set this. (#9355)
end
if bit32.band(child_flags, bit32.bor(4 --[[ImGuiChildFlags.ResizeX]], 8 --[[ImGuiChildFlags.ResizeY]])) == 0 then
window_flags = bit32.bor(window_flags, 2 --[[ImGuiWindowFlags.NoResize]], 256 --[[ImGuiWindowFlags.NoSavedSettings]])
end
-- Special framed style
if bit32.band(child_flags, 128 --[[ImGuiChildFlags.FrameStyle]]) ~= 0 then
ImGui.PushStyleColor(3 --[[ImGuiCol.ChildBg]], g.Style.Colors[7 --[[ImGuiCol.FrameBg]]])
ImGui.PushStyleVar(7 --[[ImGuiStyleVar.ChildRounding]], g.Style.FrameRounding)
ImGui.PushStyleVar(8 --[[ImGuiStyleVar.ChildBorderSize]], g.Style.FrameBorderSize)
ImGui.PushStyleVar(2 --[[ImGuiStyleVar.WindowPadding]], g.Style.FramePadding)
child_flags = bit32.bor(child_flags, 1 --[[ImGuiChildFlags.Borders]], 2 --[[ImGuiChildFlags.AlwaysUseWindowPadding]])
window_flags = bit32.bor(window_flags, 4 --[[ImGuiWindowFlags.NoMove]])
end
-- Forward size
-- Important: Begin() has special processing to switch condition to ImGuiCond.FirstUseEver for a given axis when ImGuiChildFlags.ResizeXXX is set.
-- (the alternative would to store conditional flags per axis, which is possible but more code)
local size_avail = ImGui.GetContentRegionAvail()
local size_default = ImVec2((bit32.band(child_flags, 16 --[[ImGuiChildFlags.AutoResizeX]]) ~= 0) and 0.0 or size_avail.x, (bit32.band(child_flags, 32 --[[ImGuiChildFlags.AutoResizeY]]) ~= 0) and 0.0 or size_avail.y)
local size = ImGui.CalcItemSize(size_arg, size_default.x, size_default.y)
-- A SetNextWindowSize() call always has priority (#8020)
-- (since the code in Begin() never supported SizeVal==0.0 aka auto-resize via SetNextWindowSize() call, we don't support it here for now)
-- FIXME: We only support ImGuiCond.Always in this path. Supporting other paths would requires to obtain window pointer.
if bit32.band(g.NextWindowData.HasFlags, 2 --[[ImGuiNextWindowDataFlags.HasSize]]) ~= 0 and bit32.band(g.NextWindowData.SizeCond, 1 --[[ImGuiCond.Always]]) ~= 0 then
if g.NextWindowData.SizeVal.x > 0.0 then
size.x = g.NextWindowData.SizeVal.x
child_flags = bit32.band(child_flags, bit32.bnot(4 --[[ImGuiChildFlags.ResizeX]]))
end
if g.NextWindowData.SizeVal.y > 0.0 then
size.y = g.NextWindowData.SizeVal.y
child_flags = bit32.band(child_flags, bit32.bnot(8 --[[ImGuiChildFlags.ResizeY]]))
end
end
ImGui.SetNextWindowSize(size)
ImGui.SetNextWindowChildFlags(child_flags, true)
-- Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value.
-- FIXME: 2023/11/14: commented out shorted version. We had an issue with multiple ### in child window path names, which the trailing hash helped workaround.
-- e.g. "ParentName###ParentIdentifier/ChildName###ChildIdentifier" would get hashed incorrectly by ImHashStr(), trailing _%08X somehow fixes it.
local temp_window_name
-- if (name and parent_window.IDStack:back() == parent_window.ID)
-- temp_window_name = ImFormatString("%s/%s", parent_window.Name, name) -- May omit ID if in root of ID stack
-- else
if name then
temp_window_name = ImFormatString("%s/%s_%08X", parent_window.Name, name, id)
else
temp_window_name = ImFormatString("%s/%08X", parent_window.Name, id)
end
-- Set style
local backup_border_size = g.Style.ChildBorderSize
if bit32.band(child_flags, 1 --[[ImGuiChildFlags.Borders]]) == 0 then
g.Style.ChildBorderSize = 0.0
end
-- Begin into window
local _, ret = ImGui.Begin(temp_window_name, nil, window_flags)
-- Restore style
g.Style.ChildBorderSize = backup_border_size
if bit32.band(child_flags, 128 --[[ImGuiChildFlags.FrameStyle]]) ~= 0 then
ImGui.PopStyleVar(3)
ImGui.PopStyleColor()
end
local child_window = g.CurrentWindow
child_window.ChildId = id
-- Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
-- While this is not really documented/defined, it seems that the expected thing to do.
if child_window.BeginCount == 1 then
ImVec2_Copy(parent_window.DC.CursorPos, child_window.Pos)
end
-- Process navigation-in immediately so NavInit can run on first frame
-- Can enter a child if (A) it has navigable items or (B) it can be scrolled.
local temp_id_for_activation = ImHashStr("##Child", nil, id)
if g.ActiveId == temp_id_for_activation then
ImGui.ClearActiveID()
end
if g.NavActivateId == id and bit32.band(child_flags, 256 --[[ImGuiChildFlags.NavFlattened]]) == 0 and (child_window.DC.NavLayersActiveMask ~= 0 or child_window.DC.NavWindowHasScrollY) then
ImGui.FocusWindow(child_window)
ImGui.NavInitWindow(child_window, false)
ImGui.SetActiveID(temp_id_for_activation, child_window) -- Steal ActiveId with another arbitrary id so that key-press won't activate child item
g.ActiveIdSource = g.NavInputSource
end
return ret
end
-- Prior to v1.90 2023/10/16, the BeginChild() function took a 'bool border = false' parameter instead of 'ImGuiChildFlags child_flags = 0'.
-- ImGuiChildFlags.Borders is defined as always == 1 in order to allow old code passing 'true'. Read comments in imgui.h for details!
-- `true` is accepted as ImGuiChildFlags.Borders for legacy code.
--- @param str_id_or_id string|ImGuiID
--- @param size_arg? ImVec2
--- @param child_flags? ImGuiChildFlags|bool
--- @param window_flags? ImGuiWindowFlags
--- @return bool # visible (always call EndChild()!)
function ImGui.BeginChild(str_id_or_id, size_arg, child_flags, window_flags)
if child_flags == true then child_flags = 1 --[[ImGuiChildFlags.Borders]] elseif not child_flags then child_flags = 0 end
if type(str_id_or_id) == "number" then
return ImGui.BeginChildEx(nil, str_id_or_id, size_arg, child_flags, window_flags)
end
local id = ImGui.GetCurrentWindow():GetID(str_id_or_id)
return ImGui.BeginChildEx(str_id_or_id, id, size_arg, child_flags, window_flags)
end
--- @param window ImGuiWindow
--- @param rect ImRect
function ImGui.SetLastItemDataForChildWindowItem(window, rect)
local g = GImGui
ImGui.SetLastItemData(window.ChildId, g.CurrentItemFlags, window.DC.ChildItemStatusFlags or 0, rect)
end
function ImGui.EndChild()
local g = GImGui
local child_window = g.CurrentWindow
local backup_within_end_child_id = g.WithinEndChildID
assert(bit32.band(child_window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0) -- Mismatched BeginChild()/EndChild() calls
g.WithinEndChildID = child_window.ID
local child_size = ImVec2(child_window.Size.x, child_window.Size.y)
ImGui.End()
if child_window.BeginCount == 1 then
local parent_window = g.CurrentWindow
local bb = ImRect(parent_window.DC.CursorPos, parent_window.DC.CursorPos + child_size)
ImGui.ItemSize(child_size)
local nav_flattened = bit32.band(child_window.ChildFlags, 256 --[[ImGuiChildFlags.NavFlattened]]) ~= 0
if (child_window.DC.NavLayersActiveMask ~= 0 or child_window.DC.NavWindowHasScrollY) and not nav_flattened then
ImGui.ItemAdd(bb, child_window.ChildId)
ImGui.RenderNavCursor(bb, child_window.ChildId)
-- When browsing a window that has no activable items (scroll only) we keep a highlight on the child (pass g.NavId to trick into always displaying)
if child_window.DC.NavLayersActiveMask == 0 and child_window == g.NavWindow then
ImGui.RenderNavCursor(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, 2 --[[ImGuiNavRenderCursorFlags.Compact]])
end
else
-- Not navigable into
-- - This is a bit of a fringe use case, mostly useful for undecorated, non-scrolling contents childs, or empty childs.
-- - We could later decide to not apply this path if ImGuiChildFlags.FrameStyle or ImGuiChildFlags.Borders is set.
ImGui.ItemAdd(bb, child_window.ChildId, nil, 2 --[[ImGuiItemFlags.NoNav]])
-- But when flattened we directly reach items, adjust active layer mask accordingly
if nav_flattened then
parent_window.DC.NavLayersActiveMaskNext = bit32.bor(parent_window.DC.NavLayersActiveMaskNext, child_window.DC.NavLayersActiveMaskNext)
end
end
if g.HoveredWindow == child_window then
g.LastItemData.StatusFlags = bit32.bor(g.LastItemData.StatusFlags, 128 --[[ImGuiItemStatusFlags.HoveredWindow]])
end
child_window.DC.ChildItemStatusFlags = g.LastItemData.StatusFlags
-- SetLastItemDataForChildWindowItem(child_window, child_window:Rect()) -- Not needed, effectively done by ItemAdd()
else
ImGui.SetLastItemDataForChildWindowItem(child_window, child_window:Rect())
end
g.WithinEndChildID = backup_within_end_child_id
g.LogLinePosY = -3.4028234663853e+38 --[[FLT_MAX]] -- To enforce a carriage return
end
--- @param flags ImGuiChildFlags
--- @param enabled bool
function ImGui.SetNextWindowChildFlags(flags, enabled)
local g = GImGui
local nwd = g.NextWindowData
if bit32.band(nwd.HasFlags, 512 --[[ImGuiNextWindowDataFlags.HasChildFlags]]) == 0 then
nwd.ChildFlagsSet = 0; nwd.ChildFlagsClear = 0
end
if enabled then
nwd.ChildFlagsSet = bit32.bor(nwd.ChildFlagsSet, flags)
nwd.ChildFlagsClear = bit32.band(nwd.ChildFlagsClear, bit32.bnot(flags))
else
nwd.ChildFlagsSet = bit32.band(nwd.ChildFlagsSet, bit32.bnot(flags))
nwd.ChildFlagsClear = bit32.bor(nwd.ChildFlagsClear, flags)
end
nwd.HasFlags = bit32.bor(nwd.HasFlags, 512 --[[ImGuiNextWindowDataFlags.HasChildFlags]])
end
--- @param flags ImGuiWindowFlags
--- @param enabled bool
function ImGui.SetNextWindowFlags(flags, enabled)
local g = GImGui
local nwd = g.NextWindowData
if bit32.band(nwd.HasFlags, 256 --[[ImGuiNextWindowDataFlags.HasWindowFlags]]) == 0 then
nwd.WindowFlagsSet = 0; nwd.WindowFlagsClear = 0
end
if enabled then
nwd.WindowFlagsSet = bit32.bor(nwd.WindowFlagsSet, flags)
nwd.WindowFlagsClear = bit32.band(nwd.WindowFlagsClear, bit32.bnot(flags))
else
nwd.WindowFlagsSet = bit32.band(nwd.WindowFlagsSet, bit32.bnot(flags))
nwd.WindowFlagsClear = bit32.bor(nwd.WindowFlagsClear, flags)
end
nwd.HasFlags = bit32.bor(nwd.HasFlags, 256 --[[ImGuiNextWindowDataFlags.HasWindowFlags]])
end
-- Content size = inner scrollable rectangle, padded with WindowPadding.
-- SetNextWindowContentSize(ImVec2(100,100)) + ImGuiWindowFlags.AlwaysAutoResize will always allow submitting a 100x100 item.
--- @param size ImVec2
function ImGui.SetNextWindowContentSize(size)
local g = GImGui
g.NextWindowData.HasFlags = bit32.bor(g.NextWindowData.HasFlags, 4 --[[ImGuiNextWindowDataFlags.HasContentSize]])
ImVec2_CopyV(g.NextWindowData.ContentSizeVal, ImTrunc(size.x), ImTrunc(size.y))
end
--- @param scroll ImVec2
function ImGui.SetNextWindowScroll(scroll)
local g = GImGui
g.NextWindowData.HasFlags = bit32.bor(g.NextWindowData.HasFlags, 128 --[[ImGuiNextWindowDataFlags.HasScroll]])
ImVec2_Copy(g.NextWindowData.ScrollVal, scroll)
end
--- @param collapsed bool
--- @param cond? ImGuiCond
function ImGui.SetNextWindowCollapsed(collapsed, cond)
if cond == nil then cond = 0 end
local g = GImGui
assert(cond == 0 or ImIsPowerOfTwo(cond)) -- Make sure the user doesn't attempt to combine multiple condition flags.
g.NextWindowData.HasFlags = bit32.bor(g.NextWindowData.HasFlags, 8 --[[ImGuiNextWindowDataFlags.HasCollapsed]])
g.NextWindowData.CollapsedVal = collapsed
g.NextWindowData.CollapsedCond = (cond ~= 0) and cond or 1 --[[ImGuiCond.Always]]
end
function ImGui.SetNextWindowFocus()
local g = GImGui
g.NextWindowData.HasFlags = bit32.bor(g.NextWindowData.HasFlags, 32 --[[ImGuiNextWindowDataFlags.HasFocus]])
end
--- @param id ImGuiID
function ImGui.SetNextWindowViewport(id)
local g = GImGui
g.NextWindowData.HasFlags = bit32.bor(g.NextWindowData.HasFlags, 2048 --[[ImGuiNextWindowDataFlags.HasViewport]])
g.NextWindowData.ViewportId = id
end
--- @param window_class ImGuiWindowClass
function ImGui.SetNextWindowClass(window_class)
local g = GImGui
g.NextWindowData.HasFlags = bit32.bor(g.NextWindowData.HasFlags, 8192 --[[ImGuiNextWindowDataFlags.HasWindowClass]])
local wc = ImGuiWindowClass()
for k, v in pairs(window_class) do wc[k] = v end -- C++ copies the struct
g.NextWindowData.WindowClass = wc
end
-- This is experimental and meant to be a toy for exploring a future/wider range of features.
--- @param flags ImGuiWindowRefreshFlags
function ImGui.SetNextWindowRefreshPolicy(flags)
local g = GImGui
g.NextWindowData.HasFlags = bit32.bor(g.NextWindowData.HasFlags, 1024 --[[ImGuiNextWindowDataFlags.HasRefreshPolicy]])
g.NextWindowData.RefreshFlagsVal = flags
g.NextWindowData.CacheKeySet = false
end
-- [Port] Opt-in cached window (built on the refresh policy above). While nothing that could change the window
-- happened, Begin() keeps last frame's draw list and returns visible=false, so `if visible then ... end` skips your code.
-- It refreshes when `key` changes (pass e.g. a version number of the data shown), while the window (or a popup/child
-- of it) is hovered, focused or active and for one frame after, and when it appears, moves, resizes, scrolls,
-- collapses, is docked, or the display size or font atlas texture changes.
-- Anything else that changes the contents (style, fonts, time) must be part of `key`.
function ImGui.SetNextWindowCached(key)
local g = GImGui
ImGui.SetNextWindowRefreshPolicy(bit32.bor(1 --[[ImGuiWindowRefreshFlags.TryToAvoidRefresh]], 2 --[[ImGuiWindowRefreshFlags.RefreshOnHover]], 4 --[[ImGuiWindowRefreshFlags.RefreshOnFocus]]))
g.NextWindowData.CacheKeySet = true
g.NextWindowData.CacheKeyVal = key
end
function ImGui.GetWindowDpiScale()
return 1.0 -- Roblox: single viewport, no DPI scaling
end
function ImGui.GetWindowViewport()
local g = GImGui
return g.CurrentViewport or ImGui.GetMainViewport()
end
-- Prefer using PushFont(nil, style.FontSizeBase * factor), or use style.FontScaleMain to scale all windows.
--- @param scale float
function ImGui.SetWindowFontScale(scale)
assert(scale > 0.0)
local window = ImGui.GetCurrentWindow()
window.FontWindowScale = scale
ImGui.UpdateCurrentFontSize(0.0)
end
--- @param window ImGuiWindow
function ImGui.StartMouseMovingWindow(window)
-- Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
-- We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
-- This is because we want ActiveId to be set even when the window is not permitted to move.
local g = GImGui
ImGui.FocusWindow(window)
ImGui.SetActiveID(window.MoveId, window)
if (g.IO.ConfigNavCursorVisibleAuto) then
g.NavCursorVisible = false
end
g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window.RootWindowDockTree.Pos
g.ActiveIdNoClearOnFocusLoss = true
ImGui.SetActiveIdUsingAllKeyboardKeys()
local can_move_window = true
if bit32.band(window.Flags, 4 --[[ImGuiWindowFlags.NoMove]]) ~= 0 or bit32.band(window.RootWindowDockTree.Flags, 4 --[[ImGuiWindowFlags.NoMove]]) ~= 0 then
can_move_window = false
end
local node = window.DockNodeAsHost
if node and node.VisibleWindow and bit32.band(node.VisibleWindow.Flags, 4 --[[ImGuiWindowFlags.NoMove]]) ~= 0 then
can_move_window = false
end
if can_move_window then
g.MovingWindow = window
end
end
-- Handle mouse moving window
-- Note: moving window with the navigation keys (Square + d-pad / Ctrl+Tab + Arrows) are processed in NavUpdateWindowing()
-- FIXME: We don't have strong guarantee that g.MovingWindow stay synced with g.ActiveId == g.MovingWindow.MoveId.
-- This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs,
-- but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other.
function ImGui.UpdateMouseMovingWindowNewFrame()
local g = GImGui
if g.MovingWindow then
-- We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
-- We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow.MoveId for consistency.
ImGui.KeepAliveID(g.ActiveId)
assert(g.MovingWindow and g.MovingWindow.RootWindowDockTree)
local moving_window = g.MovingWindow.RootWindowDockTree
-- When a window stop being submitted while being dragged, it may will its viewport until next Begin()
local window_disappeared = (not moving_window.WasActive and not moving_window.Active)
if g.IO.MouseDown[0] and ImGui.IsMousePosValid(g.IO.MousePos) and not window_disappeared then
local pos = g.IO.MousePos - g.ActiveIdClickOffset
if moving_window.Pos.x ~= pos.x or moving_window.Pos.y ~= pos.y then
ImGui.SetWindowPos(moving_window, pos, 1 --[[ImGuiCond.Always]])
if moving_window.Viewport and moving_window.ViewportOwned then -- Synchronize viewport immediately because some overlays may relies on clipping rectangle before we Begin() into the window.
ImVec2_Copy(moving_window.Viewport.Pos, pos)
moving_window.Viewport:UpdateWorkRect()
end
end
ImGui.FocusWindow(g.MovingWindow)
else
ImGui.StopMouseMovingWindow()
ImGui.ClearActiveID()
end
else
-- When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
if (g.ActiveIdWindow and g.ActiveIdWindow.MoveId == g.ActiveId) then
ImGui.KeepAliveID(g.ActiveId)
if not g.IO.MouseDown[0] then
ImGui.ClearActiveID()
end
end
end
end
function ImGui.GetDrawListSharedData()
return GImGui.DrawListSharedData
end
-- Supported flags: ImGuiPopupFlags.AnyPopupId, ImGuiPopupFlags.AnyPopupLevel
--- @param id ImGuiID
--- @param popup_flags ImGuiPopupFlags
function ImGui.IsPopupOpen(id, popup_flags)
local g = GImGui
if popup_flags == nil then popup_flags = 0 end
if type(id) == "string" then
-- str_id overload: with AnyPopupId the id must be 0, otherwise it is hashed with the current window's ID stack
id = (bit32.band(popup_flags, 1024 --[[ImGuiPopupFlags.AnyPopupId]]) ~= 0) and 0 or g.CurrentWindow:GetID(id)
end
if bit32.band(popup_flags, 1024 --[[ImGuiPopupFlags.AnyPopupId]]) ~= 0 then
-- Return true if any popup is open at the current BeginPopup() level of the popup stack
-- This may be used to e.g. test for another popups already opened to handle popups priorities at the same level.
assert(id == 0)
if bit32.band(popup_flags, 2048 --[[ImGuiPopupFlags.AnyPopupLevel]]) ~= 0 then
return g.OpenPopupStack.Size > 0
else
return g.OpenPopupStack.Size > g.BeginPopupStack.Size
end
else
if bit32.band(popup_flags, 2048 --[[ImGuiPopupFlags.AnyPopupLevel]]) ~= 0 then
-- Return true if the popup is open anywhere in the popup stack
for n = 1, g.OpenPopupStack.Size do
if g.OpenPopupStack.Data[n].PopupId == id then
return true
end
end
return false
else
-- Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query)
return g.OpenPopupStack.Size > g.BeginPopupStack.Size and g.OpenPopupStack.Data[g.BeginPopupStack.Size + 1].PopupId == id
end
end
end
-- Also see FindBlockingModal(nil)
--- @return ImGuiWindow?
function ImGui.GetTopMostPopupModal()
local g = GImGui
for n = g.OpenPopupStack.Size, 1, -1 do
local popup = g.OpenPopupStack.Data[n].Window
if popup then
if bit32.band(popup.Flags, 134217728 --[[ImGuiWindowFlags.Modal]]) ~= 0 then
return popup
end
end
end
return nil
end
-- Initiate focusing and moving window when clicking on empty space or title bar.
-- Initiate focusing window when clicking on a disabled item.
-- Handle left-click and right-click focus.
function ImGui.UpdateMouseMovingWindowEndFrame()
local g = GImGui
if g.ActiveId ~= 0 or (g.HoveredId ~= 0 and not g.HoveredIdIsDisabled) then
return
end
-- Unless we just made a window/popup appear
if g.NavWindow and g.NavWindow.Appearing then
return
end
local hovered_window = g.HoveredWindow
-- Click on empty space to focus window and start moving
-- (after we're done with all our widgets, so e.g. clicking on docking tab-bar which have set HoveredId already and not get us here!)
if ImGui.IsMouseClickedEx(0, 0 --[[ImGuiInputFlags.None]], 4294967295 --[[ImGuiKeyOwner_NoOwner]]) then
-- Handle the edge case of a popup being closed while clicking in its empty space.
-- If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.
local hovered_root
if hovered_window then
hovered_root = hovered_window.RootWindow
else
hovered_root = nil
end
local is_closed_popup = hovered_root and (bit32.band(hovered_root.Flags, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0) and not ImGui.IsPopupOpen(hovered_root.PopupId, 2048 --[[ImGuiPopupFlags.AnyPopupLevel]])
local is_queued_focus_request = g.NavMoveSubmitted and (bit32.band(g.NavMoveFlags, 512 --[[ImGuiNavMoveFlags.FocusApi]]) ~= 0)
if hovered_window ~= nil and not is_closed_popup and not is_queued_focus_request then
ImGui.StartMouseMovingWindow(hovered_window)
-- FIXME: In principle we might be able to call StopMouseMovingWindow() below.
-- Please note how StartMouseMovingWindow() and StopMouseMovingWindow() and not entirely symmetrical, at the later doesn't clear ActiveId.
-- Cancel moving if clicked outside of title bar
if bit32.band(hovered_window.BgClickFlags, 1 --[[ImGuiWindowBgClickFlags.Move]]) == 0 then -- set by io.ConfigWindowsMoveFromTitleBarOnly
if bit32.band(hovered_root.Flags, 1 --[[ImGuiWindowFlags.NoTitleBar]]) == 0 then
if not hovered_root:TitleBarRect():Contains(g.IO.MouseClickedPos[0]) then
g.MovingWindow = nil
end
end
end
-- Cancel moving if clicked over an item which was disabled or inhibited by popups
-- (when g.HoveredIdIsDisabled == true && g.HoveredId == 0 we are inhibited by popups, when g.HoveredIdIsDisabled == true && g.HoveredId != 0 we are over a disabled item)
if g.HoveredIdIsDisabled then
g.MovingWindow = nil
g.ActiveIdDisabledId = g.HoveredId
end
elseif hovered_window == nil and g.NavWindow ~= nil then
-- Clicking on void disable focus
ImGui.FocusWindow(nil, 2 --[[ImGuiFocusRequestFlags.UnlessBelowModal]])
end
end
-- With right mouse button we close popups without changing focus based on where the mouse is aimed
-- Instead, focus will be restored to the window under the bottom-most closed popup.
-- (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
if g.HoveredId == 0 and ImGui.IsMouseClickedEx(1, 0 --[[ImGuiInputFlags.None]], 4294967295 --[[ImGuiKeyOwner_NoOwner]]) then
-- Find the top-most window between HoveredWindow and the top-most Modal Window.
-- This is where we can trim the popup stack.
local modal = ImGui.GetTopMostPopupModal()
local hovered_window_above_modal = hovered_window and (modal == nil or ImGui.IsWindowAbove(hovered_window, modal))
ImGui.ClosePopupsOverWindow(hovered_window_above_modal and hovered_window or modal, true)
end
end
-- This is called during NewFrame()->UpdateViewportsNewFrame() only.
-- Need to keep in sync with SetWindowPos()
--- @param window ImGuiWindow
--- @param delta ImVec2
function ImGui.TranslateWindow(window, delta)
ImVec2_Copy(window.Pos, window.Pos + delta)
window.ClipRect:Translate(delta)
window.OuterRectClipped:Translate(delta)
window.InnerRect:Translate(delta)
ImVec2_Copy(window.DC.CursorPos, window.DC.CursorPos + delta)
ImVec2_Copy(window.DC.CursorStartPos, window.DC.CursorStartPos + delta)
ImVec2_Copy(window.DC.CursorMaxPos, window.DC.CursorMaxPos + delta)
ImVec2_Copy(window.DC.IdealMaxPos, window.DC.IdealMaxPos + delta)
end
--- ImGui::FindWindowByID
function ImGui.FindWindowByID(id)
local g = GImGui
if not g then return end
return g.WindowsById[id]
end
--- @param name string
--- @return ImGuiWindow?
function ImGui.FindWindowByName(name)
local id = ImHashStr(name)
return ImGui.FindWindowByID(id)
end
-- Push a new Dear ImGui window to add widgets to.
-- - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair.
-- - Begin/End can be called multiple times during the frame with the same window name to append content.
-- - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
-- You can use the "##" or "###" markers to use the same label with different id, or same id with different label. See documentation at the top of this file.
-- - Return false when window is collapsed, so you can early out in your code. You always need to call ImGui.End() even if false is returned.
-- - Passing a non-nil 'open' displays a Close button on the upper-right corner of the window, the returned 'is_open' value will be false when the button is pressed.
--- @param name string
--- @param open? bool
--- @param flags? ImGuiWindowFlags
--- @return bool is_open # The updated `open` passed in
--- @return bool no_skip_items # You always need to call `ImGui.End()` even if false is returned
function ImGui.Begin(name, open, flags)
if flags == nil then flags = 0 end
local g = GImGui
local style = g.Style
assert(name ~= nil and name ~= "") -- Window name required
assert(g.WithinFrameScope) -- Forgot to call ImGui.NewFrame()
assert(g.FrameCountEnded ~= g.FrameCount) -- Called ImGui.Render() or ImGui.EndFrame() and haven't called ImGui.NewFrame() again yet
-- Find or create
local window = ImGui.FindWindowByName(name)
local window_just_created = (window == nil)
if window_just_created then
window = CreateNewWindow(name, flags) --- @cast window ImGuiWindow
end
-- [DEBUG] Debug break requested by user
if g.DebugBreakInWindow == window.ID then
IM_DEBUG_BREAK()
end
if bit32.band(g.NextWindowData.HasFlags, 256 --[[ImGuiNextWindowDataFlags.HasWindowFlags]]) ~= 0 then
flags = bit32.band(bit32.bor(flags, g.NextWindowData.WindowFlagsSet), bit32.bnot(g.NextWindowData.WindowFlagsClear))
end
-- Automatically disable manual moving/resizing when NoInputs is set
if bit32.band(flags, 197120 --[[ImGuiWindowFlags.NoInputs]]) == 197120 --[[ImGuiWindowFlags.NoInputs]] then
flags = bit32.bor(flags, 4 --[[ImGuiWindowFlags.NoMove]], 2 --[[ImGuiWindowFlags.NoResize]])
end
local current_frame = g.FrameCount
local first_begin_of_the_frame = (window.LastFrameActive ~= current_frame)
window.IsFallbackWindow = (g.CurrentWindowStack.Size == 0 and g.WithinFrameScopeWithImplicitWindow)
-- Update the Appearing flag (note: the BeginDocked() path may also set this to true later)
local window_just_activated_by_user = (window.LastFrameActive < (current_frame - 1)) -- Not using !WasActive because the implicit "Debug" window would always toggle off->on
if bit32.band(flags, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0 then
local popup_ref = g.OpenPopupStack.Data[g.BeginPopupStack.Size + 1]
window_just_activated_by_user = window_just_activated_by_user or (window.PopupId ~= popup_ref.PopupId) -- We recycle popups so treat window as activated if popup id changed
window_just_activated_by_user = window_just_activated_by_user or (window ~= popup_ref.Window)
end
-- Update Flags, LastFrameActive, BeginOrderXXX fields
local window_was_appearing = window.Appearing
if first_begin_of_the_frame then
ImGui.UpdateWindowInFocusOrderList(window, window_just_created, flags)
window.Appearing = window_just_activated_by_user
if (window.Appearing) then
SetWindowConditionAllowFlags(window, 8 --[[ImGuiCond.Appearing]], true)
end
window.FlagsPreviousFrame = window.Flags
window.Flags = flags
window.ChildFlags = (bit32.band(g.NextWindowData.HasFlags, 512 --[[ImGuiNextWindowDataFlags.HasChildFlags]]) ~= 0) and g.NextWindowData.ChildFlagsSet or 0
window.LastFrameActive = current_frame
window.LastTimeActive = g.Time
window.BeginOrderWithinParent = 0
window.BeginOrderWithinContext = g.WindowsActiveCount
g.WindowsActiveCount = g.WindowsActiveCount + 1
else
flags = window.Flags
end
-- Docking
-- (NB: during the frame dock nodes are created, it is possible that (window->DockIsActive == false) even though (window->DockNode->Windows.Size > 1)
assert(window.DockNode == nil or window.DockNodeAsHost == nil) -- Cannot be both
if bit32.band(g.NextWindowData.HasFlags, 4096 --[[ImGuiNextWindowDataFlags.HasDock]]) ~= 0 then
ImGui.SetWindowDock(window, g.NextWindowData.DockId, g.NextWindowData.DockCond)
end
if first_begin_of_the_frame then
local has_dock_node = (window.DockId ~= 0 or window.DockNode ~= nil)
local new_auto_dock_node = not has_dock_node and ImGui.GetWindowAlwaysWantOwnTabBar(window)
local dock_node_was_visible = window.DockNodeIsVisible
local dock_tab_was_visible = window.DockTabIsVisible
window.DockIsActive = false
window.DockNodeIsVisible = false
window.DockTabIsVisible = false
if has_dock_node or new_auto_dock_node then
open = ImGui.BeginDocked(window, open)
flags = window.Flags
if window.DockIsActive then
assert(window.DockNode ~= nil)
g.NextWindowData.HasFlags = bit32.band(g.NextWindowData.HasFlags, bit32.bnot(16 --[[ImGuiNextWindowDataFlags.HasSizeConstraint]])) -- Docking currently override constraints
end
-- Amend the Appearing flag
if window.DockTabIsVisible and not dock_tab_was_visible and dock_node_was_visible and not window.Appearing and not window_was_appearing then
window.Appearing = true
SetWindowConditionAllowFlags(window, 8 --[[ImGuiCond.Appearing]], true)
end
end
end
-- Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack
local parent_window_in_stack
if window.DockIsActive and window.DockNode.HostWindow then
parent_window_in_stack = window.DockNode.HostWindow
elseif g.CurrentWindowStack:empty() then
parent_window_in_stack = nil
else
parent_window_in_stack = g.CurrentWindowStack:back().Window
end
local parent_window
if first_begin_of_the_frame then
if (bit32.band(flags, bit32.bor(16777216 --[[ImGuiWindowFlags.ChildWindow]], 67108864 --[[ImGuiWindowFlags.Popup]], 33554432 --[[ImGuiWindowFlags.Tooltip]])) ~= 0) then
parent_window = parent_window_in_stack
else
parent_window = nil
end
else
parent_window = window.ParentWindow
end
-- We allow window memory to be compacted so recreate the base stack when needed.
if window.IDStack.Size == 0 then
window.IDStack:push_back(window.ID)
end
-- Add to stack
g.CurrentWindow = window
g.CurrentWindowStack:resize(g.CurrentWindowStack.Size + 1)
g.CurrentWindowStack.Data[g.CurrentWindowStack.Size] = ImGuiWindowStackData()
local window_stack_data = g.CurrentWindowStack.Data[g.CurrentWindowStack.Size]
window_stack_data.Window = window
ImGuiLastItemData_Copy(window_stack_data.ParentLastItemDataBackup, g.LastItemData)
window_stack_data.DisabledOverrideReenable = (bit32.band(flags, 33554432 --[[ImGuiWindowFlags.Tooltip]]) ~= 0) and (bit32.band(g.CurrentItemFlags, 64 --[[ImGuiItemFlags.Disabled]]) ~= 0)
window_stack_data.DisabledOverrideReenableAlphaBackup = 0.0
window_stack_data.StackSizesInBegin = ImGuiErrorRecoveryState()
ImGui.ErrorRecoveryStoreState(window_stack_data.StackSizesInBegin)
g.StackSizesInBeginForCurrentWindow = window_stack_data.StackSizesInBegin
if bit32.band(flags, 268435456 --[[ImGuiWindowFlags.ChildMenu]]) ~= 0 then
g.BeginMenuDepth = g.BeginMenuDepth + 1
end
-- Update .RootWindow and others pointers (before any possible call to FocusWindow)
if first_begin_of_the_frame then
ImGui.UpdateWindowParentAndRootLinks(window, flags, parent_window)
window.ParentWindowInBeginStack = parent_window_in_stack
-- Focus route
-- There's little point to expose a flag to set this: because the interesting cases won't be using parent_window_in_stack,
-- Use for e.g. linking a tool window in a standalone viewport to a document window, regardless of their Begin() stack parenting. (#6798)
if window.RootWindow ~= window then
window.ParentWindowForFocusRoute = parent_window_in_stack
else
window.ParentWindowForFocusRoute = nil
end
if window.ParentWindowForFocusRoute == nil and window.DockNode ~= nil then
if bit32.band(window.DockNode.MergedFlags, 262144 --[[ImGuiDockNodeFlags.DockedWindowsInFocusRoute]]) ~= 0 then
window.ParentWindowForFocusRoute = window.DockNode.HostWindow
end
end
-- Override with SetNextWindowClass() field or direct call to SetWindowParentWindowForFocusRoute()
if window.WindowClass and window.WindowClass.FocusRouteParentWindowId ~= nil and window.WindowClass.FocusRouteParentWindowId ~= 0 then
window.ParentWindowForFocusRoute = ImGui.FindWindowByID(window.WindowClass.FocusRouteParentWindowId)
end
-- Inherit SetWindowFontScale() from parent until we fix this system...
if parent_window then
window.FontWindowScaleParents = parent_window.FontWindowScaleParents * parent_window.FontWindowScale
else
window.FontWindowScaleParents = 1.0
end
end
-- Add to focus scope stack
ImGui.PushFocusScope((bit32.band(window.ChildFlags, 256 --[[ImGuiChildFlags.NavFlattened]]) ~= 0) and g.CurrentFocusScopeId or window.ID)
window.NavRootFocusScopeId = g.CurrentFocusScopeId
-- Add to popup stacks: update OpenPopupStack[] data, push to BeginPopupStack[]
if bit32.band(flags, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0 then
local popup_ref = g.OpenPopupStack.Data[g.BeginPopupStack.Size + 1]
popup_ref.Window = window
popup_ref.ParentNavLayer = parent_window_in_stack.DC.NavLayerCurrent
g.BeginPopupStack:push_back(popup_ref)
window.PopupId = popup_ref.PopupId
end
-- Process SetNextWindow***() calls
-- (FIXME: Consider splitting the HasXXX flags into X/Y components)
local window_pos_set_by_api = false
local window_size_x_set_by_api = false
local window_size_y_set_by_api = false
if bit32.band(g.NextWindowData.HasFlags, 1 --[[ImGuiNextWindowDataFlags.HasPos]]) ~= 0 then
window_pos_set_by_api = (bit32.band(window.SetWindowPosAllowFlags, g.NextWindowData.PosCond) ~= 0)
if window_pos_set_by_api and ImLengthSqr(g.NextWindowData.PosPivotVal) > 1e-5 then
-- May be processed on the next frame if this is our first frame and we are measuring size
-- FIXME: Look into removing the branch so everything can go through this same code path for consistency.
ImVec2_Copy(window.SetWindowPosVal, g.NextWindowData.PosVal)
ImVec2_Copy(window.SetWindowPosPivot, g.NextWindowData.PosPivotVal)
window.SetWindowPosAllowFlags = bit32.band(window.SetWindowPosAllowFlags, bit32.bnot(bit32.bor(2 --[[ImGuiCond.Once]], 4 --[[ImGuiCond.FirstUseEver]], 8 --[[ImGuiCond.Appearing]])))
else
ImGui.SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond)
end
end
if bit32.band(g.NextWindowData.HasFlags, 2 --[[ImGuiNextWindowDataFlags.HasSize]]) ~= 0 then
window_size_x_set_by_api = (bit32.band(window.SetWindowSizeAllowFlags, g.NextWindowData.SizeCond) ~= 0) and (g.NextWindowData.SizeVal.x > 0.0)
window_size_y_set_by_api = (bit32.band(window.SetWindowSizeAllowFlags, g.NextWindowData.SizeCond) ~= 0) and (g.NextWindowData.SizeVal.y > 0.0)
if (bit32.band(window.ChildFlags, 4 --[[ImGuiChildFlags.ResizeX]]) ~= 0 and bit32.band(window.SetWindowSizeAllowFlags, 4 --[[ImGuiCond.FirstUseEver]]) == 0) then -- Axis-specific conditions for BeginChild()
g.NextWindowData.SizeVal.x = window.SizeFull.x
end
if (bit32.band(window.ChildFlags, 8 --[[ImGuiChildFlags.ResizeY]]) ~= 0 and bit32.band(window.SetWindowSizeAllowFlags, 4 --[[ImGuiCond.FirstUseEver]]) == 0) then
g.NextWindowData.SizeVal.y = window.SizeFull.y
end
ImGui.SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
end
if bit32.band(g.NextWindowData.HasFlags, 128 --[[ImGuiNextWindowDataFlags.HasScroll]]) ~= 0 then
if g.NextWindowData.ScrollVal.x >= 0.0 then
window.ScrollTarget.x = g.NextWindowData.ScrollVal.x
window.ScrollTargetCenterRatio.x = 0.0
end
if g.NextWindowData.ScrollVal.y >= 0.0 then
window.ScrollTarget.y = g.NextWindowData.ScrollVal.y
window.ScrollTargetCenterRatio.y = 0.0
end
end
if bit32.band(g.NextWindowData.HasFlags, 4 --[[ImGuiNextWindowDataFlags.HasContentSize]]) ~= 0 then
ImVec2_Copy(window.ContentSizeExplicit, g.NextWindowData.ContentSizeVal)
elseif first_begin_of_the_frame then
window.ContentSizeExplicit = ImVec2(0.0, 0.0)
end
if bit32.band(g.NextWindowData.HasFlags, 8192 --[[ImGuiNextWindowDataFlags.HasWindowClass]]) ~= 0 then
window.WindowClass = g.NextWindowData.WindowClass
end
if bit32.band(g.NextWindowData.HasFlags, 8 --[[ImGuiNextWindowDataFlags.HasCollapsed]]) ~= 0 then
ImGui.SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond)
end
if bit32.band(g.NextWindowData.HasFlags, 32 --[[ImGuiNextWindowDataFlags.HasFocus]]) ~= 0 then
ImGui.FocusWindow(window)
end
if window.Appearing then
SetWindowConditionAllowFlags(window, 8 --[[ImGuiCond.Appearing]], false)
end
-- [EXPERIMENTAL] Skip Refresh mode
ImGui.UpdateWindowSkipRefresh(window)
-- Nested root windows (typically tooltips) override disabled state
if window_stack_data.DisabledOverrideReenable and window.RootWindow == window then
ImGui.BeginDisabledOverrideReenable()
end
-- We intentionally set g.CurrentWindow to nil to prevent usage until when the viewport is set, then will call SetCurrentWindow()
g.CurrentWindow = nil
-- When reusing window again multiple times a frame, just append content (don't need to setup again)
if first_begin_of_the_frame and not window.SkipRefresh then
-- Initialize
local window_is_child_tooltip = (bit32.band(flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0 and bit32.band(flags, 33554432 --[[ImGuiWindowFlags.Tooltip]]) ~= 0) -- FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
local window_just_appearing_after_hidden_for_resize = (window.HiddenFramesCannotSkipItems > 0)
window.Active = true
window.HasCloseButton = (open ~= nil)
ImRect_CopyFromV4(window.ClipRect, ImVec4(-3.4028234663853e+38 --[[FLT_MAX]], -3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]]))
window.IDStack:resize(1)
window.DrawList:_ResetForNewFrame()
window.DC.CurrentTableIdx = -1
if bit32.band(flags, 8388608 --[[ImGuiWindowFlags.DockNodeHost]]) ~= 0 then
window.DrawList:ChannelsSplit(2)
window.DrawList:ChannelsSetCurrent(1) -- DOCKING_HOST_DRAW_CHANNEL_FG: Render decorations on channel 1 as we will render the backgrounds manually later
end
-- Restore buffer capacity when woken from a compacted state, to avoid
if window.MemoryCompacted then
ImGui.GcAwakeTransientWindowBuffers(window)
end
-- UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
-- Update contents size from last frame for auto-fitting (or use explicit size)
CalcWindowContentSizes(window, window.ContentSize, window.ContentSizeIdeal)
-- FIXME: These flags are decremented before they are used. This means that in order to have these fields produce their intended behaviors
-- for one frame we must set them to at least 2, which is counter-intuitive. HiddenFramesCannotSkipItems is a more complicated case because
-- it has a single usage before this code block and may be set below before it is finally checked.
if window.HiddenFramesCanSkipItems > 0 then
window.HiddenFramesCanSkipItems = window.HiddenFramesCanSkipItems - 1
end
if window.HiddenFramesCannotSkipItems > 0 then
window.HiddenFramesCannotSkipItems = window.HiddenFramesCannotSkipItems - 1
end
if window.HiddenFramesForRenderOnly > 0 then
window.HiddenFramesForRenderOnly = window.HiddenFramesForRenderOnly - 1
end
-- Hide new windows for one frame until they calculate their size
if window_just_created and (not window_size_x_set_by_api or not window_size_y_set_by_api) then
window.HiddenFramesCannotSkipItems = 1
end
-- Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
-- We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
if window_just_activated_by_user and bit32.band(flags, bit32.bor(67108864 --[[ImGuiWindowFlags.Popup]], 33554432 --[[ImGuiWindowFlags.Tooltip]])) ~= 0 then
window.HiddenFramesCannotSkipItems = 1
if bit32.band(flags, 64 --[[ImGuiWindowFlags.AlwaysAutoResize]]) ~= 0 then
if not window_size_x_set_by_api then
window.SizeFull.x = 0.0
window.Size.x = 0.0
end
if not window_size_y_set_by_api then
window.SizeFull.y = 0.0
window.Size.y = 0.0
end
ImVec2_Copy(window.ContentSize, ImVec2(0.0, 0.0))
ImVec2_Copy(window.ContentSizeIdeal, ImVec2(0.0, 0.0))
end
end
-- SELECT VIEWPORT
-- We need to do this before using any style/font sizes, as viewport with a different DPI may affect font sizes.
ImGui.WindowSelectViewport(window)
ImGui.SetCurrentViewport(window, window.Viewport)
SetCurrentWindow(window)
flags = window.Flags
-- LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
-- We read Style data after the call to UpdateSelectWindowViewport() which might be swapping the style.
if not window.DockIsActive and bit32.band(flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0 then
window.WindowBorderSize = style.ChildBorderSize
else
window.WindowBorderSize = (bit32.band(flags, bit32.bor(67108864 --[[ImGuiWindowFlags.Popup]], 33554432 --[[ImGuiWindowFlags.Tooltip]])) ~= 0 and bit32.band(flags, 134217728 --[[ImGuiWindowFlags.Modal]]) == 0) and style.PopupBorderSize or style.WindowBorderSize
end
window.WindowPadding = style.WindowPadding
if not window.DockIsActive and (bit32.band(flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0) and (bit32.band(flags, 67108864 --[[ImGuiWindowFlags.Popup]]) == 0) and (bit32.band(window.ChildFlags, 2 --[[ImGuiChildFlags.AlwaysUseWindowPadding]]) == 0) and window.WindowBorderSize == 0.0 then
if bit32.band(flags, 1024 --[[ImGuiWindowFlags.MenuBar]]) ~= 0 then
window.WindowPadding = ImVec2(0.0, style.WindowPadding.y)
else
window.WindowPadding = ImVec2(0.0, 0.0)
end
end
-- Lock menu offset so size calculation can use it as menu-bar windows need a minimum size.
window.DC.MenuBarOffset.x = ImMax(ImMax(window.WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x)
window.DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y
if bit32.band(flags, 1 --[[ImGuiWindowFlags.NoTitleBar]]) ~= 0 then
window.TitleBarHeight = 0.0
else
window.TitleBarHeight = g.FontSize + g.Style.FramePadding.y * 2.0
end
if bit32.band(flags, 1024 --[[ImGuiWindowFlags.MenuBar]]) ~= 0 then
window.MenuBarHeight = window.DC.MenuBarOffset.y + g.FontSize + g.Style.FramePadding.y * 2.0
else
window.MenuBarHeight = 0.0
end
window.TitleBarHeight = (bit32.band(flags, 1 --[[ImGuiWindowFlags.NoTitleBar]]) ~= 0) and 0 or g.FontSize + g.Style.FramePadding.y * 2
window.FontRefSize = g.FontSize -- Lock this to discourage calling window:CalcFontSize() outside of current window.
-- Depending on condition we use previous or current window size to compare against contents size to decide if a scrollbar should be visible.
-- Those flags will be altered further down in the function depending on more conditions.
local use_current_size_for_scrollbar_x = window_just_created
local use_current_size_for_scrollbar_y = window_just_created
if window_size_x_set_by_api and window.ContentSizeExplicit.x ~= 0.0 then
use_current_size_for_scrollbar_x = true
end
if window_size_y_set_by_api and window.ContentSizeExplicit.y ~= 0.0 then -- #7252
use_current_size_for_scrollbar_y = true
end
-- Collapse window by double-clicking on title bar
-- At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing
if bit32.band(flags, 1 --[[ImGuiWindowFlags.NoTitleBar]]) == 0 and bit32.band(flags, 32 --[[ImGuiWindowFlags.NoCollapse]]) == 0 and not window.DockIsActive then
-- We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed),
-- so verify that we don't have items over the title bar.
local title_bar_rect = window:TitleBarRect()
if g.HoveredWindow == window and g.HoveredId == 0 and g.HoveredIdPreviousFrame == 0 and g.ActiveId == 0 and ImGui.IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) then
if g.IO.MouseClickedCount[0] == 2 and ImGui.GetKeyOwner(656 --[[ImGuiKey.MouseLeft]]) == 4294967295 --[[ImGuiKeyOwner_NoOwner]] then
window.WantCollapseToggle = true
ImGui.SetKeyOwner(656 --[[ImGuiKey.MouseLeft]], window.MoveId) -- Claim input the same way ButtonBehavior() does. Prevent a same-frame move from triggering other items. (#9439)
end
end
if window.WantCollapseToggle then
window.Collapsed = not window.Collapsed
if not window.Collapsed then
use_current_size_for_scrollbar_y = true
end
ImGui.MarkIniSettingsDirty(window)
end
else
window.Collapsed = false
end
window.WantCollapseToggle = false
-- SIZE
-- Outer Decoration Sizes
-- (we need to clear ScrollbarSize immediately as CalcWindowAutoFitSize() needs it and can be called from other locations).
local scrollbar_sizes_from_last_frame = ImVec2()
ImVec2_Copy(scrollbar_sizes_from_last_frame, window.ScrollbarSizes)
window.DecoOuterSizeX1 = 0.0
window.DecoOuterSizeX2 = 0.0
window.DecoOuterSizeY1 = window.TitleBarHeight + window.MenuBarHeight
window.DecoOuterSizeY2 = 0.0
window.ScrollbarSizes = ImVec2(0.0, 0.0)
-- Calculate auto-fit size, handle automatic resize
-- - Using SetNextWindowSize() overrides ImGuiWindowFlags.AlwaysAutoResize, so it can be used on tooltips/popups, etc.
-- - We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags.AlwaysAutoResize when collapsed.
-- - Auto-fit may only grow window during the first few frames.
do
local size_auto_fit_x_always = not window_size_x_set_by_api and (bit32.band(flags, 64 --[[ImGuiWindowFlags.AlwaysAutoResize]]) ~= 0) and not window.Collapsed
local size_auto_fit_y_always = not window_size_y_set_by_api and (bit32.band(flags, 64 --[[ImGuiWindowFlags.AlwaysAutoResize]]) ~= 0) and not window.Collapsed
local size_auto_fit_x_current = not window_size_x_set_by_api and (window.AutoFitFramesX > 0)
local size_auto_fit_y_current = not window_size_y_set_by_api and (window.AutoFitFramesY > 0)
local size_auto_fit_mask = 0
if size_auto_fit_x_always or size_auto_fit_x_current then
size_auto_fit_mask = bit32.bor(size_auto_fit_mask, bit32.lshift(1, 1 --[[ImGuiAxis.X]] - 1))
end
if size_auto_fit_y_always or size_auto_fit_y_current then
size_auto_fit_mask = bit32.bor(size_auto_fit_mask, bit32.lshift(1, 2 --[[ImGuiAxis.Y]] - 1))
end
local size_auto_fit = CalcWindowAutoFitSize(window, window.ContentSizeIdeal, size_auto_fit_mask)
local old_size = ImVec2(window.SizeFull.x, window.SizeFull.y)
if size_auto_fit_x_always or size_auto_fit_x_current then
if size_auto_fit_x_always then
window.SizeFull.x = size_auto_fit.x
else
if window.AutoFitOnlyGrows then
window.SizeFull.x = ImMax(window.SizeFull.x, size_auto_fit.x)
else
window.SizeFull.x = size_auto_fit.x
end
end
use_current_size_for_scrollbar_x = true
end
if size_auto_fit_y_always or size_auto_fit_y_current then
if size_auto_fit_y_always then
window.SizeFull.y = size_auto_fit.y
else
if window.AutoFitOnlyGrows then
window.SizeFull.y = ImMax(window.SizeFull.y, size_auto_fit.y)
else
window.SizeFull.y = size_auto_fit.y
end
end
use_current_size_for_scrollbar_y = true
end
if old_size.x ~= window.SizeFull.x or old_size.y ~= window.SizeFull.y then
ImGui.MarkIniSettingsDirty(window)
end
end
-- Apply minimum/maximum window size constraints and final size
ImVec2_Copy(window.SizeFull, CalcWindowSizeAfterConstraint(window, window.SizeFull))
ImVec2_Copy(window.Size, (window.Collapsed and bit32.band(flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) == 0) and window:TitleBarRect():GetSize() or window.SizeFull)
-- POSITION
-- Popup latch its initial position, will position itself when it appears next frame
if window_just_activated_by_user then
window.AutoPosLastDirection = (-1) --[[ImGuiDir.None]]
if bit32.band(flags, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0 and bit32.band(flags, 134217728 --[[ImGuiWindowFlags.Modal]]) == 0 and not window_pos_set_by_api then -- FIXME: BeginPopup() could use SetNextWindowPos()
ImVec2_Copy(window.Pos, g.BeginPopupStack:back().OpenPopupPos)
end
end
-- Position child window
if bit32.band(flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0 then
assert(parent_window and parent_window.Active)
window.BeginOrderWithinParent = parent_window.DC.ChildWindows.Size
parent_window.DC.ChildWindows:push_back(window)
if (bit32.band(flags, 67108864 --[[ImGuiWindowFlags.Popup]]) == 0) and not window_pos_set_by_api and not window_is_child_tooltip then
ImVec2_Copy(window.Pos, parent_window.DC.CursorPos)
end
end
local window_pos_with_pivot = (window.SetWindowPosVal.x ~= 3.4028234663853e+38 --[[FLT_MAX]] and window.HiddenFramesCannotSkipItems == 0)
if window_pos_with_pivot then
ImGui.SetWindowPos(window, window.SetWindowPosVal - window.Size * window.SetWindowPosPivot, 0) -- Position given a pivot (e.g. for centering)
elseif (bit32.band(flags, 268435456 --[[ImGuiWindowFlags.ChildMenu]]) ~= 0) then
ImVec2_Copy(window.Pos, ImGui.FindBestWindowPosForPopup(window))
elseif (bit32.band(flags, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0) and not window_pos_set_by_api and window_just_appearing_after_hidden_for_resize then
ImVec2_Copy(window.Pos, ImGui.FindBestWindowPosForPopup(window))
elseif (bit32.band(flags, 33554432 --[[ImGuiWindowFlags.Tooltip]]) ~= 0) and not window_pos_set_by_api and not window_is_child_tooltip then
ImVec2_Copy(window.Pos, ImGui.FindBestWindowPosForPopup(window))
end
-- Late create viewport if we don't fit within our current host viewport.
if window.ViewportAllowPlatformMonitorExtend >= 0 and not window.ViewportOwned and bit32.band(window.Viewport.Flags, 4096 --[[ImGuiViewportFlags.IsMinimized]]) == 0 then
if not window.Viewport:GetMainRect():Contains(window:Rect()) then
-- This is based on the assumption that the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport)
-- local old_viewport = window.Viewport
window.Viewport = ImGui.AddUpdateViewport(window, window.ID, window.Pos, window.Size, 32 --[[ImGuiViewportFlags.NoFocusOnAppearing]])
-- FIXME-DPI
-- IM_ASSERT(old_viewport.DpiScale == window.Viewport.DpiScale) -- FIXME-DPI: Something went wrong
ImGui.SetCurrentViewport(window, window.Viewport)
SetCurrentWindow(window)
end
end
if window.ViewportOwned then
ImGui.WindowSyncOwnedViewport(window, parent_window_in_stack)
end
-- Calculate the range of allowed position for that window (to be movable and visible past safe area padding)
-- When clamping to stay visible, we will enforce that window.Pos stays inside of visibility_rect.
local viewport_rect = window.Viewport:GetMainRect()
local viewport_work_rect = window.Viewport:GetWorkRect()
local visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding)
local visibility_rect = ImRect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding)
-- Clamp position/size so window stays visible within its viewport or monitor
-- Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
-- FIXME: Similar to code in GetWindowAllowedExtentRect()
if not window_pos_set_by_api and bit32.band(flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) == 0 then
if not window.ViewportOwned and viewport_rect:GetWidth() > 0 and viewport_rect:GetHeight() > 0.0 then
ClampWindowPos(window, visibility_rect)
end
end
window.Pos.x = ImTrunc(window.Pos.x) window.Pos.y = ImTrunc(window.Pos.y)
-- Apply window focus (new and reactivated windows are moved to front)
local want_focus = false
if (window_just_activated_by_user and bit32.band(flags, 4096 --[[ImGuiWindowFlags.NoFocusOnAppearing]]) == 0) then
if bit32.band(flags, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0 then
want_focus = true
elseif (window.DockIsActive or bit32.band(flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) == 0) and bit32.band(flags, 33554432 --[[ImGuiWindowFlags.Tooltip]]) == 0 then
want_focus = true
end
end
-- Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
-- Large values tend to lead to variety of artifacts and are not recommended.
if bit32.band(flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0 and not window.DockIsActive then
window.WindowRounding = style.ChildRounding
elseif window.RootWindowDockTree.ViewportOwned then
window.WindowRounding = 0.0
else
if (bit32.band(flags, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0 and bit32.band(flags, 134217728 --[[ImGuiWindowFlags.Modal]]) == 0) then
window.WindowRounding = style.PopupRounding
else
window.WindowRounding = style.WindowRounding
end
end
-- Decide if we are going to handle borders and resize grips
-- 'window.SkipItems' is not updated yet so for child windows we rely on ParentWindow to avoid submitting decorations. (#8815)
-- Whenever we add support for full decorated child windows we will likely make this logic more general.
local handle_borders_and_resize_grips = (window.DockNodeAsHost ~= nil or not window.DockIsActive)
if bit32.band(flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0 and window.ParentWindow.SkipItems then
handle_borders_and_resize_grips = false
end
-- Handle manual resize: Resize Grips, Borders, Gamepad
-- Child windows can only be resized when they have the flags set. The resize grip allows resizing in both directions, so it should appear only if both flags are set.
local border_hovered, border_held = -1, -1
local resize_grip_col = {0, 0, 0, 0}
local resize_grip_count
if (bit32.band(flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0) and (bit32.band(flags, 67108864 --[[ImGuiWindowFlags.Popup]]) == 0) then
if (bit32.band(window.ChildFlags, 4 --[[ImGuiChildFlags.ResizeX]]) ~= 0) and (bit32.band(window.ChildFlags, 8 --[[ImGuiChildFlags.ResizeY]]) ~= 0) then
resize_grip_count = 1
else
resize_grip_count = 0
end
else
resize_grip_count = g.IO.ConfigWindowsResizeFromEdges and 2 or 1 -- Allow resize from lower-left if we have the mouse cursor feedback for it.
end
local resize_grip_draw_size = ImTrunc(ImMax(g.FontSize * 1.10, g.Style.WindowRounding + 1.0 + g.FontSize * 0.2))
if handle_borders_and_resize_grips and not window.Collapsed then
local auto_fit_mask
auto_fit_mask, border_hovered, border_held = UpdateWindowManualResize(window, resize_grip_count, resize_grip_col, visibility_rect)
if auto_fit_mask ~= 0 then
if bit32.band(auto_fit_mask, bit32.lshift(1, 1 --[[ImGuiAxis.X]] - 1)) ~= 0 then
use_current_size_for_scrollbar_x = true
end
if bit32.band(auto_fit_mask, bit32.lshift(1, 2 --[[ImGuiAxis.Y]] - 1)) ~= 0 then
use_current_size_for_scrollbar_y = true
end
end
end
window.ResizeBorderHovered = border_hovered
window.ResizeBorderHeld = border_held
-- Synchronize window --> viewport again and one last time (clamping and manual resize may have affected either)
if window.ViewportOwned then
if not window.Viewport.PlatformRequestMove then
ImVec2_Copy(window.Viewport.Pos, window.Pos)
end
if not window.Viewport.PlatformRequestResize then
ImVec2_Copy(window.Viewport.Size, window.Size)
end
window.Viewport:UpdateWorkRect()
viewport_rect = window.Viewport:GetMainRect()
end
-- Save last known viewport position within the window itself (so it can be saved in .ini file and restored)
ImVec2_Copy(window.ViewportPos, window.Viewport.Pos)
-- SCROLLBAR VISIBILITY
-- Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
if not window.Collapsed then
-- When reading the current size we need to read it after size constraints have been applied.
-- Intentionally use previous frame values for InnerRect and ScrollbarSizes.
-- And when we use window.DecorationUp here it doesn't have ScrollbarSizes.y applied yet.
local avail_size_from_current_frame = ImVec2(window.SizeFull.x, window.SizeFull.y - (window.DecoOuterSizeY1 + window.DecoOuterSizeY2))
local avail_size_from_last_frame = window.InnerRect:GetSize() + scrollbar_sizes_from_last_frame
local needed_size_from_last_frame = window_just_created and ImVec2(0, 0) or window.ContentSize + window.WindowPadding * 2.0
local size_for_scrollbars_x = use_current_size_for_scrollbar_x and avail_size_from_current_frame.x or avail_size_from_last_frame.x
local size_for_scrollbars_y = use_current_size_for_scrollbar_y and avail_size_from_current_frame.y or avail_size_from_last_frame.y
local scrollbar_x_prev = window.ScrollbarX
-- local scrollbar_y_from_last_frame = window.ScrollbarY -- FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
window.ScrollbarY = (bit32.band(flags, 16384 --[[ImGuiWindowFlags.AlwaysVerticalScrollbar]]) ~= 0) or ((needed_size_from_last_frame.y > size_for_scrollbars_y) and not (bit32.band(flags, 8 --[[ImGuiWindowFlags.NoScrollbar]]) ~= 0))
window.ScrollbarX = (bit32.band(flags, 32768 --[[ImGuiWindowFlags.AlwaysHorizontalScrollbar]]) ~= 0) or ((needed_size_from_last_frame.x > size_for_scrollbars_x - (window.ScrollbarY and style.ScrollbarSize or 0.0)) and not (bit32.band(flags, 8 --[[ImGuiWindowFlags.NoScrollbar]]) ~= 0) and (bit32.band(flags, 2048 --[[ImGuiWindowFlags.HorizontalScrollbar]]) ~= 0))
-- Track when ScrollbarX visibility keeps toggling, which is a sign of a feedback loop, and stabilize by enforcing visibility (#3285, #8488)
-- (Feedback loops of this sort can manifest in various situations, but combining horizontal + vertical scrollbar + using a clipper with varying width items is one frequent cause.
-- The better solution is to, either (1) enforce visibility by using ImGuiWindowFlags.AlwaysHorizontalScrollbar or (2) declare stable contents width with SetNextWindowContentSize(), if you can compute it)
window.ScrollbarXStabilizeToggledHistory = bit32.lshift(window.ScrollbarXStabilizeToggledHistory, 1)
if scrollbar_x_prev ~= window.ScrollbarX then
window.ScrollbarXStabilizeToggledHistory = bit32.bor(window.ScrollbarXStabilizeToggledHistory, 0x01)
end
local scrollbar_x_stabilize = (window.ScrollbarXStabilizeToggledHistory ~= 0) and ImCountSetBits(window.ScrollbarXStabilizeToggledHistory) >= 4 -- 4 == half of bits in our U8 history.
if scrollbar_x_stabilize then
window.ScrollbarX = true
end
-- if scrollbar_x_stabilize and not window.ScrollbarXStabilizeEnabled then
-- IMGUI_DEBUG_LOG("[scroll] Stabilize ScrollbarX for Window '%s'\n", window.Name)
-- end
window.ScrollbarXStabilizeEnabled = scrollbar_x_stabilize
if window.ScrollbarX and not window.ScrollbarY then
window.ScrollbarY = (needed_size_from_last_frame.y > size_for_scrollbars_y - style.ScrollbarSize) and not (bit32.band(flags, 8 --[[ImGuiWindowFlags.NoScrollbar]]) ~= 0)
end
window.ScrollbarSizes = ImVec2(window.ScrollbarY and style.ScrollbarSize or 0.0, window.ScrollbarX and style.ScrollbarSize or 0.0)
-- Amend the partially filled window.DecorationXXX values.
window.DecoOuterSizeX2 = window.DecoOuterSizeX2 + window.ScrollbarSizes.x
window.DecoOuterSizeY2 = window.DecoOuterSizeY2 + window.ScrollbarSizes.y
end
-- UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
-- Update various regions. Variables they depend on should be set above in this function.
-- We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
-- Outer rectangle
-- Not affected by window border size. Used by:
-- - FindHoveredWindow() (w/ extra padding when border resize is enabled)
-- - Begin() initial clipping rect for drawing window background and borders.
-- - Begin() clipping whole child
local host_rect
if (bit32.band(flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0 and bit32.band(flags, 67108864 --[[ImGuiWindowFlags.Popup]]) == 0 and not window_is_child_tooltip) then
host_rect = parent_window.ClipRect
else
host_rect = viewport_rect
end
local outer_rect = window:Rect()
local title_bar_rect = window:TitleBarRect()
ImRect_Copy(window.OuterRectClipped, outer_rect)
if window.DockIsActive then
window.OuterRectClipped.Min.y = window.OuterRectClipped.Min.y + window.TitleBarHeight
end
window.OuterRectClipped:ClipWith(host_rect)
-- Inner rectangle
-- Not affected by window border size. Used by:
-- - InnerClipRect
-- - ScrollToRectEx()
-- - NavUpdatePageUpPageDown()
-- - Scrollbar()
window.InnerRect.Min.x = window.Pos.x + window.DecoOuterSizeX1
window.InnerRect.Min.y = window.Pos.y + window.DecoOuterSizeY1
window.InnerRect.Max.x = window.Pos.x + window.Size.x - window.DecoOuterSizeX2
window.InnerRect.Max.y = window.Pos.y + window.Size.y - window.DecoOuterSizeY2
-- Inner clipping rectangle.
-- - Extend a outside of normal work region up to borders.
-- - This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
-- - It also makes clipped items be more noticeable.
-- - And is consistent on both axis (prior to 2024/05/03 ClipRect used WindowPadding.x * 0.5 on left and right edge), see #3312
-- - Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
-- Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
-- Affected by window/frame border size. Used by:
-- - Begin() initial clip rect
local top_border_size = ((bit32.band(flags, 1024 --[[ImGuiWindowFlags.MenuBar]]) ~= 0 or bit32.band(flags, 1 --[[ImGuiWindowFlags.NoTitleBar]]) == 0) and style.FrameBorderSize or window.WindowBorderSize)
-- Try to match the fact that our border is drawn centered over the window rectangle, rather than inner.
-- This is why we do a *0.5 here. We don't currently even technically support large values for WindowBorderSize,
-- see e.g #7887 #7888, but may do after we move the window border to become an inner border (and then we can remove the 0.5 here).
window.InnerClipRect.Min.x = ImFloor(0.5 + window.InnerRect.Min.x + window.WindowBorderSize * 0.5)
window.InnerClipRect.Min.y = ImFloor(0.5 + window.InnerRect.Min.y + top_border_size * 0.5)
window.InnerClipRect.Max.x = ImFloor(window.InnerRect.Max.x - window.WindowBorderSize * 0.5)
window.InnerClipRect.Max.y = ImFloor(window.InnerRect.Max.y - window.WindowBorderSize * 0.5)
window.InnerClipRect:ClipWithFull(host_rect)
-- SCROLLING
-- Lock down maximum scrolling
-- The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
-- for right/bottom aligned items without creating a scrollbar.
window.ScrollMax.x = ImMax(0.0, window.ContentSize.x + window.WindowPadding.x * 2.0 - window.InnerRect:GetWidth())
window.ScrollMax.y = ImMax(0.0, window.ContentSize.y + window.WindowPadding.y * 2.0 - window.InnerRect:GetHeight())
-- Apply scrolling
ImVec2_Copy(window.Scroll, CalcNextScrollFromScrollTargetAndClamp(window))
ImVec2_CopyV(window.ScrollTarget, 3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]])
window.DecoInnerSizeX1 = 0.0; window.DecoInnerSizeY1 = 0.0
-- DRAWING
-- Setup draw list and outer clipping rectangle
assert(window.DrawList.CmdBuffer.Size == 1 and window.DrawList.CmdBuffer.Data[1].ElemCount == 0)
window.DrawList:_SetPixelDensity(window.Viewport.FramebufferScale.x)
window.DrawList:PushTexture(g.Font.OwnerAtlas.TexRef)
ImGui.PushClipRect(host_rect.Min, host_rect.Max, false)
-- Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71)
-- When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
-- FIXME: User code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected (github #4493)
local is_undocked_or_docked_visible = not window.DockIsActive or window.DockTabIsVisible
if is_undocked_or_docked_visible then
local render_decorations_in_parent = false
if (bit32.band(flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0) and (bit32.band(flags, 67108864 --[[ImGuiWindowFlags.Popup]]) == 0) and not window_is_child_tooltip then
-- - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here)
-- - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs
local previous_child
if parent_window.DC.ChildWindows.Size >= 2 then
previous_child = parent_window.DC.ChildWindows.Data[parent_window.DC.ChildWindows.Size - 1]
else
previous_child = nil
end
local previous_child_overlapping
if previous_child ~= nil then
previous_child_overlapping = previous_child:Rect():Overlaps(window:Rect())
else
previous_child_overlapping = false
end
local parent_is_empty = (parent_window.DrawList.VtxBuffer.Size == 0)
if (window.DrawList.CmdBuffer:back().ElemCount == 0 and not parent_is_empty and not previous_child_overlapping) then
render_decorations_in_parent = true
end
end
if render_decorations_in_parent then
window.DrawList = parent_window.DrawList
end
-- Handle title bar, scrollbar, resize grips and resize borders
local window_to_highlight = g.NavWindowingTarget or g.NavWindow
local title_bar_is_highlight = want_focus or (window_to_highlight ~= nil and (window.RootWindowForTitleBarHighlight == window_to_highlight.RootWindowForTitleBarHighlight or (window.DockNode ~= nil and window.DockNode == window_to_highlight.DockNode)))
RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_col, resize_grip_draw_size)
if render_decorations_in_parent then
window.DrawList = window.DrawListInst
end
end
-- UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
-- Work rectangle.
-- Affected by window padding and border size. Used by:
-- - Columns() for right-most edge
-- - TreeNode(), CollapsingHeader() for right-most edge
-- - BeginTabBar() for right-most edge
local allow_scrollbar_x = (bit32.band(flags, 8 --[[ImGuiWindowFlags.NoScrollbar]]) == 0) and (bit32.band(flags, 2048 --[[ImGuiWindowFlags.HorizontalScrollbar]]) ~= 0)
local allow_scrollbar_y = (bit32.band(flags, 8 --[[ImGuiWindowFlags.NoScrollbar]]) == 0)
local work_rect_size_x
if window.ContentSizeExplicit.x ~= 0.0 then
work_rect_size_x = window.ContentSizeExplicit.x
else
local content_size_x = allow_scrollbar_x and (window.ContentSize and window.ContentSize.x or 0.0) or 0.0
local window_size_x = window.Size.x - window.WindowPadding.x * 2.0 - (window.DecoOuterSizeX1 + window.DecoOuterSizeX2)
work_rect_size_x = ImMax(content_size_x, window_size_x)
end
local work_rect_size_y
if window.ContentSizeExplicit.y ~= 0.0 then
work_rect_size_y = window.ContentSizeExplicit.y
else
local content_size_y = allow_scrollbar_y and (window.ContentSize and window.ContentSize.y or 0.0) or 0.0
local window_size_y = window.Size.y - window.WindowPadding.y * 2.0 - (window.DecoOuterSizeY1 + window.DecoOuterSizeY2)
work_rect_size_y = ImMax(content_size_y, window_size_y)
end
window.WorkRect.Min.x = ImTrunc(window.InnerRect.Min.x - window.Scroll.x + ImMax(window.WindowPadding.x, window.WindowBorderSize))
window.WorkRect.Min.y = ImTrunc(window.InnerRect.Min.y - window.Scroll.y + ImMax(window.WindowPadding.y, window.WindowBorderSize))
window.WorkRect.Max.x = window.WorkRect.Min.x + work_rect_size_x
window.WorkRect.Max.y = window.WorkRect.Min.y + work_rect_size_y
window.ParentWorkRect = window.WorkRect
-- [LEGACY] Content Region
-- FIXME-OBSOLETE: window.ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
-- Unless explicit content size is specified by user, this currently represent the region leading to no scrolling.
-- Used by:
-- - Mouse wheel scrolling + many other things
window.ContentRegionRect.Min.x = window.Pos.x - window.Scroll.x + window.WindowPadding.x + window.DecoOuterSizeX1
window.ContentRegionRect.Min.y = window.Pos.y - window.Scroll.y + window.WindowPadding.y + window.DecoOuterSizeY1
window.ContentRegionRect.Max.x = window.ContentRegionRect.Min.x + (window.ContentSizeExplicit.x ~= 0.0 and window.ContentSizeExplicit.x or (window.Size.x - window.WindowPadding.x * 2.0 - (window.DecoOuterSizeX1 + window.DecoOuterSizeX2)))
window.ContentRegionRect.Max.y = window.ContentRegionRect.Min.y + (window.ContentSizeExplicit.y ~= 0.0 and window.ContentSizeExplicit.y or (window.Size.y - window.WindowPadding.y * 2.0 - (window.DecoOuterSizeY1 + window.DecoOuterSizeY2)))
-- Setup drawing context
-- (NB: That term "drawing context / DC" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window. and window.DC. is dubious.)
window.DC.Indent.x = window.DecoOuterSizeX1 + window.WindowPadding.x - window.Scroll.x
window.DC.GroupOffset.x = 0.0
window.DC.ColumnsOffset.x = 0.0
-- Record the loss of precision of CursorStartPos which can happen due to really large scrolling amount.
-- This is used by clipper to compensate and fix the most common use case of large scroll area. Easy and cheap, next best thing compared to switching everything to double or ImU64.
local start_pos_highp_x = window.Pos.x + window.WindowPadding.x - window.Scroll.x + window.DecoOuterSizeX1 + window.DC.ColumnsOffset.x
local start_pos_highp_y = window.Pos.y + window.WindowPadding.y - window.Scroll.y + window.DecoOuterSizeY1
ImVec2_CopyV(window.DC.CursorStartPos, start_pos_highp_x, start_pos_highp_y)
ImVec2_CopyV(window.DC.CursorStartPosLossyness, start_pos_highp_x - window.DC.CursorStartPos.x, start_pos_highp_y - window.DC.CursorStartPos.y)
ImVec2_Copy(window.DC.CursorPos, window.DC.CursorStartPos)
ImVec2_Copy(window.DC.CursorPosPrevLine, window.DC.CursorPos)
ImVec2_Copy(window.DC.CursorMaxPos, window.DC.CursorStartPos)
ImVec2_Copy(window.DC.IdealMaxPos, window.DC.CursorStartPos)
ImVec2_CopyV(window.DC.CurrLineSize, 0.0, 0.0)
ImVec2_CopyV(window.DC.PrevLineSize, 0.0, 0.0)
window.DC.CurrLineTextBaseOffset = 0.0
window.DC.PrevLineTextBaseOffset = 0.0
window.DC.IsSameLine = false
window.DC.IsSetPos = false
window.DC.NavLayerCurrent = 0 --[[ImGuiNavLayer.Main]]
window.DC.NavLayersActiveMask = window.DC.NavLayersActiveMaskNext
window.DC.NavLayersActiveMaskNext = 0x00
window.DC.NavIsScrollPushableX = true
window.DC.NavHideHighlightOneFrame = false
window.DC.NavWindowHasScrollY = window.ScrollMax.y > 0.0
window.DC.MenuBarAppending = false
window.DC.MenuColumns:Update(style.ItemSpacing.x, window_just_activated_by_user)
window.DC.TreeDepth = 0
window.DC.TreeHasStackDataDepthMask = 0x00
window.DC.TreeRecordsClippedNodesY2Mask = 0x00
window.DC.ChildWindows:resize(0)
window.DC.StateStorage = window.StateStorage
window.DC.CurrentColumns = nil
window.DC.LayoutType = 1 --[[ImGuiLayoutType.Vertical]]
window.DC.ParentLayoutType = (parent_window ~= nil) and parent_window.DC.LayoutType or 1 --[[ImGuiLayoutType.Vertical]]
-- Default item width. Make it proportional to window size if window can be manually resized.
-- (we cannot use AutoFitFramesX/AutoFitFramesY which is a temporary state)
local is_resizable_width
if bit32.band(flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0 and not window.DockIsActive then
is_resizable_width = (window.Size.x > 0.0) and (bit32.band(window.ChildFlags, bit32.bor(16 --[[ImGuiChildFlags.AutoResizeX]], 64 --[[ImGuiChildFlags.AlwaysAutoResize]])) == 0)
else
is_resizable_width = (window.Size.x > 0.0) and (bit32.band(flags, 64 --[[ImGuiWindowFlags.AlwaysAutoResize]]) == 0)
end
if is_resizable_width then
window.DC.ItemWidthDefault = ImTrunc(window.Size.x * 0.65)
else
window.DC.ItemWidthDefault = ImTrunc(g.FontSize * 16.0)
end
window.DC.ItemWidth = window.DC.ItemWidthDefault
window.DC.ItemWidthStack:resize(0)
window.DC.TextWrapPos = -1.0 -- Disabled
window.DC.TextWrapPosStack:resize(0)
if bit32.band(flags, 134217728 --[[ImGuiWindowFlags.Modal]]) ~= 0 then
window.DC.ModalDimBgColor = ImGui.ColorConvertFloat4ToU32(ImGui.GetStyleColorVec4(62 --[[ImGuiCol.ModalWindowDimBg]]))
end
if window.AutoFitFramesX > 0 then
window.AutoFitFramesX = window.AutoFitFramesX - 1
end
if window.AutoFitFramesY > 0 then
window.AutoFitFramesY = window.AutoFitFramesY - 1
end
-- Clear SetNextWindowXXX data (can aim to move this higher in the function)
g.NextWindowData:ClearFlags()
-- Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
-- We ImGuiFocusRequestFlags.UnlessBelowModal to:
-- - Avoid focusing a window that is created outside of a modal. This will prevent active modal from being closed.
-- - Position window behind the modal that is not a begin-parent of this window.
if want_focus then
ImGui.FocusWindow(window, 2 --[[ImGuiFocusRequestFlags.UnlessBelowModal]])
end
if want_focus and window == g.NavWindow then
ImGui.NavInitWindow(window, false) -- <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls
end
-- Pressing Ctrl+C copy window content into the clipboard
-- [EXPERIMENTAL] Breaks on nested Begin/End pairs. We need to work that out and add better logging scope.
-- [EXPERIMENTAL] Text outputs has many issues.
if g.IO.ConfigWindowsCopyContentsWithCtrlC then
if g.NavWindow and g.NavWindow.RootWindow == window and g.ActiveId == 0 and ImGui.Shortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 548 --[[ImGuiKey.C]])) then
ImGui.LogToClipboard(0)
end
end
-- Title bar
if bit32.band(flags, 1 --[[ImGuiWindowFlags.NoTitleBar]]) == 0 and not window.DockIsActive then
open = RenderWindowTitleBarContents(window, ImRect(title_bar_rect.Min.x + window.WindowBorderSize, title_bar_rect.Min.y, title_bar_rect.Max.x - window.WindowBorderSize, title_bar_rect.Max.y), name, open)
elseif bit32.band(flags, 1 --[[ImGuiWindowFlags.NoTitleBar]]) == 0 and window.DockIsActive then
ImGui.LogText("%s\n", string.sub(window.Name, 1, ImGui.FindRenderedTextEnd(window.Name) - 1))
end
-- Clear hit test shape every frame
window.HitTestHoleSize.x = 0; window.HitTestHoleSize.y = 0
if bit32.band(flags, 33554432 --[[ImGuiWindowFlags.Tooltip]]) ~= 0 then
g.TooltipPreviousWindow = window
end
if bit32.band(g.IO.ConfigFlags, 128 --[[ImGuiConfigFlags.DockingEnable]]) ~= 0 then
-- Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source.
-- We need to do this _before_ we overwrite window.DC.LastItemId below because BeginDockableDragDropSource() also overwrites it.
if g.MovingWindow == window and bit32.band(window.RootWindowDockTree.Flags, 524288 --[[ImGuiWindowFlags.NoDocking]]) == 0 then
ImGui.BeginDockableDragDropSource(window)
end
-- Docking: Any dockable window can act as a target. For dock node hosts we call BeginDockableDragDropTarget() in DockNodeUpdate() instead.
if g.DragDropActive and bit32.band(flags, 524288 --[[ImGuiWindowFlags.NoDocking]]) == 0 then
if g.MovingWindow == nil or g.MovingWindow.RootWindowDockTree ~= window then
if window == window.RootWindowDockTree and bit32.band(window.Flags, 8388608 --[[ImGuiWindowFlags.DockNodeHost]]) == 0 then
ImGui.BeginDockableDragDropTarget(window)
end
end
end
end
-- Set default BgClickFlags
-- This is set at the end of this function, so UpdateWindowManualResize()/ClampWindowPos() may use last-frame value if overridden by user code.
-- FIXME: The general intent is that we will later expose config options to default to enable scrolling + select scrolling mouse button.
if bit32.band(flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0 then
window.BgClickFlags = parent_window.BgClickFlags
else
window.BgClickFlags = g.IO.ConfigWindowsMoveFromTitleBarOnly and 0 --[[ImGuiWindowBgClickFlags.None]] or 1 --[[ImGuiWindowBgClickFlags.Move]]
end
-- We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
-- This is useful to allow creating context menus on title bar only, etc.
window.DC.WindowItemStatusFlags = 0 --[[ImGuiItemStatusFlags.None]]
if ImGui.IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) then
window.DC.WindowItemStatusFlags = bit32.bor(window.DC.WindowItemStatusFlags, 1 --[[ImGuiItemStatusFlags.HoveredRect]])
end
ImGui.SetLastItemDataForWindow(window, title_bar_rect)
-- [DEBUG]
if not IMGUI_DISABLE_DEBUG_TOOLS then
if g.DebugLocateId ~= 0 and (window.ID == g.DebugLocateId or window.MoveId == g.DebugLocateId) then
ImGui.DebugLocateItemResolveWithLastItem()
end
end
else
-- Skip refresh always mark active
if (window.SkipRefresh) then
SetWindowActiveForSkipRefresh(window)
end
-- Append
ImGui.SetCurrentViewport(window, window.Viewport)
SetCurrentWindow(window)
g.NextWindowData:ClearFlags()
ImGui.SetLastItemDataForWindow(window, window:TitleBarRect())
end
if bit32.band(flags, 8388608 --[[ImGuiWindowFlags.DockNodeHost]]) == 0 and not window.SkipRefresh then
ImGui.PushClipRect(window.InnerClipRect.Min, window.InnerClipRect.Max, true)
-- [Roblox backend] content clip + scroll origin: lets the backend move scrolled content instead of redrawing it
local dl, cr = window.DrawList, window.DrawList._CmdHeader.ClipRect
dl._ContentClipX0, dl._ContentClipY0, dl._ContentClipX1, dl._ContentClipY1 = cr.x, cr.y, cr.z, cr.w
dl._ScrollOriginX, dl._ScrollOriginY = window.Pos.x - window.Scroll.x, window.Pos.y - window.Scroll.y
end
-- Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default "Debug" window is unused)
window.WriteAccessed = false
window.BeginCount = window.BeginCount + 1
g.NextWindowData:ClearFlags()
-- Update visibility
if first_begin_of_the_frame and not window.SkipRefresh then
-- When we are about to select this tab (which will only be visible on the _next frame_), flag it with a non-zero HiddenFramesCannotSkipItems.
-- This will have the important effect of actually returning true in Begin() and not setting SkipItems, allowing an earlier submission of the window contents.
-- This is analogous to regular windows being hidden from one frame.
-- It is especially important as e.g. nested TabBars would otherwise generate flicker in the form of one empty frame, or focus requests won't be processed.
if window.DockIsActive and not window.DockTabIsVisible then
if window.LastFrameJustFocused == g.FrameCount then
window.HiddenFramesCannotSkipItems = 1
else
window.HiddenFramesCanSkipItems = 1
end
end
if (bit32.band(flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0) and (bit32.band(flags, 268435456 --[[ImGuiWindowFlags.ChildMenu]]) == 0) then
-- Child window can be out of sight and have "negative" clip windows.
-- Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
assert((bit32.band(flags, 1 --[[ImGuiWindowFlags.NoTitleBar]]) ~= 0) or window.DockIsActive)
local nav_request = (bit32.band(window.ChildFlags, 256 --[[ImGuiChildFlags.NavFlattened]]) ~= 0) and (g.NavAnyRequest and g.NavWindow and g.NavWindow.RootWindowForNav == window.RootWindowForNav)
if not g.LogEnabled and not nav_request then
if window.OuterRectClipped.Min.x >= window.OuterRectClipped.Max.x or window.OuterRectClipped.Min.y >= window.OuterRectClipped.Max.y then
if window.AutoFitFramesX > 0 or window.AutoFitFramesY > 0 then
window.HiddenFramesCannotSkipItems = 1
else
window.HiddenFramesCanSkipItems = 1
end
end
end
-- Hide along with parent or if parent is collapsed
if parent_window and (parent_window.Collapsed or parent_window.HiddenFramesCanSkipItems > 0) then
window.HiddenFramesCanSkipItems = 1
end
if parent_window and parent_window.HiddenFramesCannotSkipItems > 0 then
window.HiddenFramesCannotSkipItems = 1
end
end
-- Don't render if style alpha is 0.0 at the time of Begin(). This is arbitrary and inconsistent but has been there for a long while (may remove at some point)
if style.Alpha <= 0.0 then
window.HiddenFramesCanSkipItems = 1
end
-- Update the Hidden flag
local hidden_regular = (window.HiddenFramesCanSkipItems > 0) or (window.HiddenFramesCannotSkipItems > 0)
window.Hidden = hidden_regular or (window.HiddenFramesForRenderOnly > 0)
-- Disable inputs for requested number of frames
if window.DisableInputsFrames > 0 then
window.DisableInputsFrames = window.DisableInputsFrames - 1
window.Flags = bit32.bor(window.Flags, 197120 --[[ImGuiWindowFlags.NoInputs]])
end
-- Update the SkipItems flag, used to early out of all items functions (no layout required)
local skip_items = false
if window.Collapsed or not window.Active or hidden_regular then
if window.AutoFitFramesX <= 0 and window.AutoFitFramesY <= 0 and window.HiddenFramesCannotSkipItems <= 0 then
skip_items = true
end
end
window.SkipItems = skip_items
-- Restore NavLayersActiveMaskNext to previous value when not visible, so a CTRL+Tab back can use a safe value.
if window.SkipItems then
window.DC.NavLayersActiveMaskNext = window.DC.NavLayersActiveMask
end
elseif first_begin_of_the_frame then
-- Skip refresh mode
window.SkipItems = true
end
if open == nil then open = true end
-- [DEBUG] io.ConfigDebugBeginReturnValue override return value to test Begin/End and BeginChild/EndChild behaviors.
-- (The implicit fallback window is NOT automatically ended allowing it to always be able to receive commands without crashing)
if not window.IsFallbackWindow then
if (g.IO.ConfigDebugBeginReturnValueOnce and window_just_created) or (g.IO.ConfigDebugBeginReturnValueLoop and g.DebugBeginReturnValueCullDepth == g.CurrentWindowStack.Size) then
if window.AutoFitFramesX > 0 then window.AutoFitFramesX = window.AutoFitFramesX + 1 end
if window.AutoFitFramesY > 0 then window.AutoFitFramesY = window.AutoFitFramesY + 1 end
return open, false
end
end
return open, not window.SkipItems
end
function ImGui.End()
local g = GImGui
local window = g.CurrentWindow
-- Error checking: verify that user hasn't called End() too many times!
if (g.CurrentWindowStack.Size <= 1 and g.WithinFrameScopeWithImplicitWindow) then
IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!")
return
end
local window_stack_data = g.CurrentWindowStack:back()
-- Error checking: verify that user doesn't directly call End() on a child window.
if bit32.band(window.Flags, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0 then
IM_ASSERT_USER_ERROR(g.WithinEndPopupID == window.ID, "Must call EndPopup() and not End()!")
end
if bit32.band(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0 and bit32.band(window.Flags, 8388608 --[[ImGuiWindowFlags.DockNodeHost]]) == 0 and not window.DockIsActive then
IM_ASSERT_USER_ERROR(g.WithinEndChildID == window.ID, "Must call EndChild() and not End()!")
end
-- Close anything that is open
if window.DC.CurrentColumns then
ImGui.EndColumns()
end
if bit32.band(window.Flags, 8388608 --[[ImGuiWindowFlags.DockNodeHost]]) == 0 and not window.SkipRefresh then -- Pop inner window clip rectangle
ImGui.PopClipRect()
end
ImGui.PopFocusScope()
if window_stack_data.DisabledOverrideReenable and window.RootWindow == window then
ImGui.EndDisabledOverrideReenable()
end
if (window.SkipRefresh) then
assert(window.DrawList == nil)
window.DrawList = window.DrawListInst
end
if window.CacheSkipItemsBackup ~= nil then -- [Port] SetNextWindowCached()
window.SkipItems = window.CacheSkipItemsBackup
window.CacheSkipItemsBackup = nil
end
-- Stop logging
if g.LogWindow == window then -- FIXME: add more options for scope of logging
ImGui.LogFinish()
end
if window.DC.IsSetPos then
ImGui.ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
end
-- Docking: report contents sizes to parent to allow for auto-resize
if window.DockNode and window.DockTabIsVisible then
local host_window = window.DockNode.HostWindow -- FIXME-DOCK
if host_window then
ImVec2_Copy(host_window.DC.CursorMaxPos, window.DC.CursorMaxPos + window.WindowPadding - host_window.WindowPadding)
end
end
-- Pop from window stack
ImGuiLastItemData_Copy(g.LastItemData, window_stack_data.ParentLastItemDataBackup)
if bit32.band(window.Flags, 268435456 --[[ImGuiWindowFlags.ChildMenu]]) ~= 0 then
g.BeginMenuDepth = g.BeginMenuDepth - 1
end
if bit32.band(window.Flags, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0 then
g.BeginPopupStack:pop_back()
end
-- Error handling, state recovery
if g.IO.ConfigErrorRecovery and window_stack_data.StackSizesInBegin then
ImGui.ErrorRecoveryTryToRecoverWindowState(window_stack_data.StackSizesInBegin)
end
g.CurrentWindowStack:pop_back()
-- LUA: No "Ternary Operator", since lua (... and (1) or (2)) will eval (1) and (2) no matter what
-- so something like `SetCurrentWindow((g.CurrentWindowStack.Size == 0) and nil or g.CurrentWindowStack:back().Window)` will error
if (g.CurrentWindowStack.Size == 0) then
SetCurrentWindow(nil)
else
SetCurrentWindow(g.CurrentWindowStack:back().Window)
end
if g.CurrentWindow then
ImGui.SetCurrentViewport(g.CurrentWindow, g.CurrentWindow.Viewport)
end
end
--- @param option ImGuiItemFlags
--- @param enabled bool
function ImGui.PushItemFlag(option, enabled)
local g = GImGui
local item_flags = g.CurrentItemFlags
assert(item_flags == g.ItemFlagsStack:back())
if enabled then
item_flags = bit32.bor(item_flags, option)
else
item_flags = bit32.band(item_flags, bit32.bnot(option))
end
g.CurrentItemFlags = item_flags
g.ItemFlagsStack:push_back(item_flags)
end
function ImGui.PopItemFlag()
local g = GImGui
IM_ASSERT_USER_ERROR_RET(g.ItemFlagsStack.Size > 1, "Calling PopItemFlag() too many times!")
g.ItemFlagsStack:pop_back()
g.CurrentItemFlags = g.ItemFlagsStack:back()
end
-- BeginDisabled()/EndDisabled()
-- - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled)
-- - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently.
-- - Feedback welcome at https://github.com/ocornut/imgui/issues/211
-- - BeginDisabled(false)/EndDisabled() essentially does nothing but is provided to facilitate use of boolean expressions.
-- (as a micro-optimization: if you have tens of thousands of BeginDisabled(false)/EndDisabled() pairs, you might want to reformulate your code to avoid making those calls)
-- - Note: mixing up BeginDisabled() and PushItemFlag(ImGuiItemFlags.Disabled) is currently NOT SUPPORTED.
--- @param disabled? bool
function ImGui.BeginDisabled(disabled)
if disabled == nil then disabled = true end
local g = GImGui
local was_disabled = bit32.band(g.CurrentItemFlags, 64 --[[ImGuiItemFlags.Disabled]]) ~= 0
if not was_disabled and disabled then
g.DisabledAlphaBackup = g.Style.Alpha
g.Style.Alpha = g.Style.Alpha * g.Style.DisabledAlpha -- PushStyleVar(ImGuiStyleVar.Alpha, g.Style.Alpha * g.Style.DisabledAlpha)
end
if was_disabled or disabled then
g.CurrentItemFlags = bit32.bor(g.CurrentItemFlags, 64 --[[ImGuiItemFlags.Disabled]])
end
g.ItemFlagsStack:push_back(g.CurrentItemFlags) -- FIXME-OPT: can we simply skip this and use DisabledStackSize?
g.DisabledStackSize = g.DisabledStackSize + 1
end
function ImGui.EndDisabled()
local g = GImGui
IM_ASSERT_USER_ERROR_RET(g.DisabledStackSize > 0, "Calling EndDisabled() too many times!")
g.DisabledStackSize = g.DisabledStackSize - 1
local was_disabled = bit32.band(g.CurrentItemFlags, 64 --[[ImGuiItemFlags.Disabled]]) ~= 0
-- PopItemFlag()
g.ItemFlagsStack:pop_back()
g.CurrentItemFlags = g.ItemFlagsStack:back()
if was_disabled and bit32.band(g.CurrentItemFlags, 64 --[[ImGuiItemFlags.Disabled]]) == 0 then
g.Style.Alpha = g.DisabledAlphaBackup -- PopStyleVar()
end
end
-- Could have been called BeginDisabledDisable() but it didn't want to be award nominated for most awkward function name.
-- Ideally we would use a shared e.g. BeginDisabled()->BeginDisabledEx() but earlier needs to be optimal.
-- The whole code for this is awkward, will reevaluate if we find a way to implement SetNextItemDisabled().
function ImGui.BeginDisabledOverrideReenable()
local g = GImGui
assert(bit32.band(g.CurrentItemFlags, 64 --[[ImGuiItemFlags.Disabled]]) ~= 0)
g.CurrentWindowStack.Data[g.CurrentWindowStack.Size].DisabledOverrideReenableAlphaBackup = g.Style.Alpha
g.Style.Alpha = g.DisabledAlphaBackup
g.CurrentItemFlags = bit32.band(g.CurrentItemFlags, bit32.bnot(64 --[[ImGuiItemFlags.Disabled]]))
g.ItemFlagsStack:push_back(g.CurrentItemFlags)
g.DisabledStackSize = g.DisabledStackSize + 1
end
function ImGui.EndDisabledOverrideReenable()
local g = GImGui
assert(g.DisabledStackSize > 0)
g.DisabledStackSize = g.DisabledStackSize - 1
g.ItemFlagsStack:pop_back()
g.CurrentItemFlags = g.ItemFlagsStack:back()
g.Style.Alpha = g.CurrentWindowStack:back().DisabledOverrideReenableAlphaBackup
end
-- Find window given position, search front-to-back
-- - Typically write output back to g.HoveredWindow and g.HoveredWindowUnderMovingWindow.
-- - FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically
-- with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
-- called, aka before the next Begin(). Moving window isn't affected.
-- - The 'find_first_and_in_any_viewport = true' mode is only used by TestEngine. It is simpler to maintain here.
--- @param pos ImVec2
--- @param find_first_and_in_any_viewport bool
--- @return ImGuiWindow, ImGuiWindow?
local function FindHoveredWindowEx(pos, find_first_and_in_any_viewport)
local g = GImGui
local hovered_window = nil
local hovered_window_under_moving_window = nil
-- Special handling for the window being moved: Ignore the mouse viewport check (because it may reset/lose its viewport during the undocking frame)
hovered_window = g.MovingWindow
local backup_moving_window_viewport = nil
if (find_first_and_in_any_viewport == false and g.MovingWindow) then
backup_moving_window_viewport = g.MovingWindow.Viewport
g.MovingWindow.Viewport = g.MouseViewport
if (bit32.band(g.MovingWindow.Flags, 512 --[[ImGuiWindowFlags.NoMouseInputs]]) == 0) then
hovered_window = g.MovingWindow
end
end
local padding_regular = g.Style.TouchExtraPadding
local padding_for_resize = ImMax(g.Style.TouchExtraPadding, ImVec2(g.Style.WindowBorderHoverPadding, g.Style.WindowBorderHoverPadding))
local window
local stop_6075 = false
for i = g.Windows.Size, 1, -1 do
repeat
window = g.Windows.Data[i]
if not window.WasActive or window.Hidden then
break
end
if bit32.band(window.Flags, 512 --[[ImGuiWindowFlags.NoMouseInputs]]) ~= 0 then
break
end
assert(window.Viewport)
if (window.Viewport ~= g.MouseViewport) then
break
end
-- Using the clipped AABB, a child window will typically be clipped by its parent (not always)
local hit_padding
if bit32.band(window.Flags, bit32.bor(2 --[[ImGuiWindowFlags.NoResize]], 64 --[[ImGuiWindowFlags.AlwaysAutoResize]])) ~= 0 then
hit_padding = padding_regular
else
hit_padding = padding_for_resize
end
if not window.OuterRectClipped:ContainsWithPad(pos, hit_padding) then
break
end
-- Support for one rectangular hole in any given window
-- FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)
if window.HitTestHoleSize.x ~= 0 then
local hx, hy = window.Pos.x + window.HitTestHoleOffset.x, window.Pos.y + window.HitTestHoleOffset.y
if pos.x >= hx and pos.y >= hy and pos.x < hx + window.HitTestHoleSize.x and pos.y < hy + window.HitTestHoleSize.y then
break -- continue
end
end
if find_first_and_in_any_viewport then
hovered_window = window
stop_6075 = true; break
else
if hovered_window == nil then
hovered_window = window
end
if hovered_window_under_moving_window == nil and (not g.MovingWindow or window.RootWindowDockTree ~= g.MovingWindow.RootWindowDockTree) then
hovered_window_under_moving_window = window
end
if hovered_window and hovered_window_under_moving_window then
stop_6075 = true; break
end
end
until true
if stop_6075 then break end
end
if (find_first_and_in_any_viewport == false and g.MovingWindow) then
g.MovingWindow.Viewport = backup_moving_window_viewport
end
return hovered_window, hovered_window_under_moving_window
end
-- TODO:
-- The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app)
function ImGui.UpdateHoveredWindowAndCaptureFlags(mouse_pos)
local g = GImGui
local io = g.IO
-- FIXME-DPI: This storage was added on 2021/03/31 for test engine, but if we want to multiply WINDOWS_HOVER_PADDING
-- by DpiScale, we need to make this window-agnostic anyhow, maybe need storing inside ImGuiWindow.
g.WindowsBorderHoverPadding = ImMax(ImMax(g.Style.TouchExtraPadding.x, g.Style.TouchExtraPadding.y), g.Style.WindowBorderHoverPadding)
-- Find the window hovered by mouse:
-- - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
-- - When moving a window we can skip the search, which also conveniently bypasses the fact that window.WindowRectClipped is lagging as this point of the frame.
-- - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms.
local clear_hovered_windows = false
g.HoveredWindow, g.HoveredWindowUnderMovingWindow = FindHoveredWindowEx(mouse_pos, false)
assert(g.HoveredWindow == nil or g.HoveredWindow == g.MovingWindow or g.HoveredWindow.Viewport == g.MouseViewport)
g.HoveredWindowBeforeClear = g.HoveredWindow
-- Modal windows prevents mouse from hovering behind them.
local modal_window = ImGui.GetTopMostPopupModal()
if modal_window and g.HoveredWindow and not ImGui.IsWindowWithinBeginStackOf(g.HoveredWindow.RootWindow, modal_window) then -- FIXME-MERGE: RootWindowDockTree ?
clear_hovered_windows = true
end
-- Disabled mouse hovering (we don't currently clear MousePos, we could)
if bit32.band(io.ConfigFlags, 16 --[[ImGuiConfigFlags.NoMouse]]) ~= 0 then
clear_hovered_windows = true
end
-- We track click ownership. When clicked outside of a window the click is owned by the application and
-- won't report hovering nor request capture even while dragging over our windows afterward.
local has_open_popup = (g.OpenPopupStack.Size > 0)
local has_open_modal = (modal_window ~= nil)
local mouse_earliest_down = -1
local mouse_any_down = false
for i = 0, 2 do -- IM_COUNTOF(io.MouseDown)
if io.MouseClicked[i] then
io.MouseDownOwned[i] = (g.HoveredWindow ~= nil) or has_open_popup
io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow ~= nil) or has_open_modal
end
mouse_any_down = mouse_any_down or io.MouseDown[i]
if (io.MouseDown[i] or io.MouseReleased[i]) then -- Increase release frame for our evaluation of earliest button (#1392)
if (mouse_earliest_down == -1 or (io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down])) then
mouse_earliest_down = i
end
end
end
local mouse_avail = (mouse_earliest_down == -1) or io.MouseDownOwned[mouse_earliest_down]
local mouse_avail_unless_popup_close = (mouse_earliest_down == -1) or io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down]
-- If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
-- FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
local mouse_dragging_extern_payload = g.DragDropActive and bit32.band(g.DragDropSourceFlags, 16 --[[ImGuiDragDropFlags.SourceExtern]]) ~= 0
if not mouse_avail and not mouse_dragging_extern_payload then
clear_hovered_windows = true
end
if clear_hovered_windows then
g.HoveredWindow = nil
g.HoveredWindowUnderMovingWindow = nil
end
-- Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app)
-- Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag
if (g.WantCaptureMouseNextFrame ~= -1) then
io.WantCaptureMouse = (g.WantCaptureMouseNextFrame ~= 0)
io.WantCaptureMouseUnlessPopupClose = io.WantCaptureMouse
else
io.WantCaptureMouse = (mouse_avail and (g.HoveredWindow ~= nil or mouse_any_down)) or has_open_popup
io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close and (g.HoveredWindow ~= nil or mouse_any_down)) or has_open_modal
end
-- Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app)
io.WantCaptureKeyboard = false
if bit32.band(io.ConfigFlags, 64 --[[ImGuiConfigFlags.NoKeyboard]]) == 0 then
if (g.ActiveId ~= 0) or (modal_window ~= nil) then
io.WantCaptureKeyboard = true
elseif io.NavActive and bit32.band(io.ConfigFlags, 1 --[[ImGuiConfigFlags.NavEnableKeyboard]]) ~= 0 and io.ConfigNavCaptureKeyboard then
io.WantCaptureKeyboard = true
end
end
if g.WantCaptureKeyboardNextFrame ~= -1 then -- Manual override
io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame ~= 0)
end
-- Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
io.WantTextInput = (g.WantTextInputNextFrame ~= -1) and (g.WantTextInputNextFrame ~= 0) or false
end
--- @param key ImGuiKey
--- @param v bool
--- @param analog_value float
function ImGui.UpdateAliasKey(key, v, analog_value)
assert(ImGui.IsAliasKey(key))
local key_data = ImGui.GetKeyData(nil, key)
key_data.Down = v
key_data.AnalogValue = analog_value
end
-- [Internal] Do not use directly
function ImGui.GetMergedModsFromKeys()
local g = GImGui
-- Bypass IsKeyDown() for the unlikely case where user used a ImGuiInputFlags.LockXXXX on those.
local mods = 0
if ImGui.GetKeyData(g, 4096 --[[ImGuiMod_Ctrl]]).Down then mods = bit32.bor(mods, 4096 --[[ImGuiMod_Ctrl]]) end
if ImGui.GetKeyData(g, 8192 --[[ImGuiMod_Shift]]).Down then mods = bit32.bor(mods, 8192 --[[ImGuiMod_Shift]]) end
if ImGui.GetKeyData(g, 16384 --[[ImGuiMod_Alt]]).Down then mods = bit32.bor(mods, 16384 --[[ImGuiMod_Alt]]) end
if ImGui.GetKeyData(g, 32768 --[[ImGuiMod_Super]]).Down then mods = bit32.bor(mods, 32768 --[[ImGuiMod_Super]]) end
return mods
end
function ImGui.UpdateKeyboardInputs()
local g = GImGui
local io = g.IO
if bit32.band(io.ConfigFlags, 64 --[[ImGuiConfigFlags.NoKeyboard]]) ~= 0 then
io:ClearInputKeys()
end
-- Update aliases
for n = 0, 2 do -- TODO: ImGuiMouseButton.COUNT - 1
ImGui.UpdateAliasKey(ImGui.MouseButtonToKey(n), io.MouseDown[n], io.MouseDown[n] and 1.0 or 0.0)
end
ImGui.UpdateAliasKey(661 --[[ImGuiKey.MouseWheelX]], io.MouseWheelH ~= 0.0, io.MouseWheelH)
ImGui.UpdateAliasKey(662 --[[ImGuiKey.MouseWheelY]], io.MouseWheel ~= 0.0, io.MouseWheel)
-- Synchronize io.KeyMods and io.KeyCtrl/io.KeyShift/etc. values.
-- - New backends (1.87+): send io.AddKeyEvent(ImGuiMod_XXX) -> -> (here) deriving io.KeyMods + io.KeyXXX from key array.
-- - Legacy backends: set io.KeyXXX bools -> (above) set key array from io.KeyXXX -> (here) deriving io.KeyMods + io.KeyXXX from key array.
-- So with legacy backends the 4 values will do a unnecessary back-and-forth but it makes the code simpler and future facing.
local prev_key_mods = io.KeyMods
io.KeyMods = ImGui.GetMergedModsFromKeys()
io.KeyCtrl = bit32.band(io.KeyMods, 4096 --[[ImGuiMod_Ctrl]]) ~= 0
io.KeyShift = bit32.band(io.KeyMods, 8192 --[[ImGuiMod_Shift]]) ~= 0
io.KeyAlt = bit32.band(io.KeyMods, 16384 --[[ImGuiMod_Alt]]) ~= 0
io.KeySuper = bit32.band(io.KeyMods, 32768 --[[ImGuiMod_Super]]) ~= 0
if prev_key_mods ~= io.KeyMods then
g.LastKeyModsChangeTime = g.Time
end
if prev_key_mods ~= io.KeyMods and prev_key_mods == 0 then
g.LastKeyModsChangeFromNoneTime = g.Time
end
-- Clear gamepad data if disabled
if bit32.band(io.BackendFlags, 1 --[[ImGuiBackendFlags.HasGamepad]]) == 0 then
for key = 632 --[[ImGuiKey_Gamepad_BEGIN]], 656 --[[ImGuiKey_Gamepad_END]] - 1 do
io.KeysData[key - 512 --[[ImGuiKey.NamedKey_BEGIN]]].Down = false
io.KeysData[key - 512 --[[ImGuiKey.NamedKey_BEGIN]]].AnalogValue = 0.0
end
end
-- Update keys
for key = 512 --[[ImGuiKey.NamedKey_BEGIN]], 667 --[[ImGuiKey.NamedKey_END]] - 1 do
local key_data = io.KeysData[key - 512 --[[ImGuiKey.NamedKey_BEGIN]]]
key_data.DownDurationPrev = key_data.DownDuration
if key_data.Down then
if key_data.DownDuration < 0.0 then
key_data.DownDuration = 0.0
else
key_data.DownDuration = key_data.DownDuration + io.DeltaTime
end
else
key_data.DownDuration = -1.0
end
if key_data.DownDuration == 0.0 then
if ImGui.IsKeyboardKey(key) then
g.LastKeyboardKeyPressTime = g.Time
elseif key == 663 --[[ImGuiKey.ReservedForModCtrl]] or key == 664 --[[ImGuiKey.ReservedForModShift]] or key == 665 --[[ImGuiKey.ReservedForModAlt]] or key == 666 --[[ImGuiKey.ReservedForModSuper]] then
g.LastKeyboardKeyPressTime = g.Time
end
end
end
-- Update keys/input owner (named keys only): one entry per key
for key = 512 --[[ImGuiKey.NamedKey_BEGIN]], 667 --[[ImGuiKey.NamedKey_END]] - 1 do
local key_data = io.KeysData[key - 512 --[[ImGuiKey.NamedKey_BEGIN]]]
local owner_data = g.KeysOwnerData[key - 512 --[[ImGuiKey.NamedKey_BEGIN]]]
owner_data.OwnerCurr = owner_data.OwnerNext
if not key_data.Down then -- Important: ownership is released on the frame after a release. Ensure a 'MouseDown -> CloseWindow -> MouseUp' chain doesn't lead to someone else seeing the MouseUp.
owner_data.OwnerNext = 4294967295 --[[ImGuiKeyOwner_NoOwner]]
end
owner_data.LockThisFrame = owner_data.LockUntilRelease and key_data.Down
owner_data.LockUntilRelease = owner_data.LockUntilRelease and key_data.Down -- Clear LockUntilRelease when key is not Down anymore
end
-- Update key routing (for e.g. shortcuts)
ImGui.UpdateKeyRoutingTable(g.KeysRoutingTable)
end
function ImGui.UpdateMouseInputs()
local g = GImGui
local io = g.IO
-- Mouse Wheel swapping flag
-- As a standard behavior holding Shift while using Vertical Mouse Wheel triggers Horizontal scroll instead
-- - We avoid doing it on OSX as it the OS input layer handles this already.
-- - FIXME: However this means when running on OSX over Emscripten, Shift+WheelY will incur two swapping (1 in OS, 1 here), canceling the feature.
-- - FIXME: When we can distinguish e.g. touchpad scroll events from mouse ones, we'll set this accordingly based on input source.
io.MouseWheelRequestAxisSwap = io.KeyShift and not io.ConfigMacOSXBehaviors
-- Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
if (ImGui.IsMousePosValid(io.MousePos)) then
local x_val = ImFloor(io.MousePos.x)
local y_val = ImFloor(io.MousePos.y)
io.MousePos = ImVec2(x_val, y_val)
g.MouseLastValidPos = ImVec2(x_val, y_val)
end
-- If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
if (ImGui.IsMousePosValid(io.MousePos) and ImGui.IsMousePosValid(io.MousePosPrev)) then
io.MouseDelta = io.MousePos - io.MousePosPrev
else
io.MouseDelta = ImVec2(0.0, 0.0)
end
-- Update stationary timer.
-- FIXME: May need to rework again to have some tolerance for occasional small movement, while being functional on high-framerates.
local mouse_stationary_threshold = (io.MouseSource == 0 --[[ImGuiMouseSource.Mouse]]) and 2.0 or 3.0 -- Slightly higher threshold for ImGuiMouseSource.TouchScreen/ImGuiMouseSource.Pen, may need rework.
local mouse_stationary = (ImLengthSqr(io.MouseDelta) <= mouse_stationary_threshold * mouse_stationary_threshold)
g.MouseStationaryTimer = mouse_stationary and (g.MouseStationaryTimer + io.DeltaTime) or 0.0
-- IMGUI_DEBUG_LOG("%.4f\n", g.MouseStationaryTimer)
-- If mouse moved we re-enable mouse hovering in case it was disabled by keyboard/gamepad. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true.
if io.MouseDelta.x ~= 0.0 or io.MouseDelta.y ~= 0.0 then
g.NavHighlightItemUnderNav = false
end
for i = 0, 2 do -- IM_COUNTOF(io.MouseDown)
io.MouseClicked[i] = io.MouseDown[i] and (io.MouseDownDuration[i] < 0.0)
io.MouseClickedCount[i] = 0 -- Will be filled below
io.MouseReleased[i] = not io.MouseDown[i] and (io.MouseDownDuration[i] >= 0.0)
if (io.MouseReleased[i]) then
io.MouseReleasedTime[i] = g.Time
end
io.MouseDownDurationPrev[i] = io.MouseDownDuration[i]
if io.MouseDown[i] then
if io.MouseDownDuration[i] < 0.0 then
io.MouseDownDuration[i] = 0.0
else
io.MouseDownDuration[i] = io.MouseDownDuration[i] + io.DeltaTime
end
else
io.MouseDownDuration[i] = -1.0
end
if io.MouseClicked[i] then
local is_repeated_click = false
if (g.Time - io.MouseClickedTime[i]) < io.MouseDoubleClickTime then
local delta_from_click_pos
if ImGui.IsMousePosValid(io.MousePos) then
delta_from_click_pos = io.MousePos - io.MouseClickedPos[i]
else
delta_from_click_pos = ImVec2(0.0, 0.0)
end
if ImLengthSqr(delta_from_click_pos) < io.MouseDoubleClickMaxDist * io.MouseDoubleClickMaxDist then
is_repeated_click = true
end
end
if is_repeated_click then
io.MouseClickedLastCount[i] = io.MouseClickedLastCount[i] + 1
else
io.MouseClickedLastCount[i] = 1
end
io.MouseClickedTime[i] = g.Time
io.MouseClickedPos[i] = io.MousePos
io.MouseClickedCount[i] = io.MouseClickedLastCount[i]
ImVec2_Copy(io.MouseDragMaxDistanceAbs[i], ImVec2(0.0, 0.0))
io.MouseDragMaxDistanceSqr[i] = 0.0
elseif io.MouseDown[i] then
-- Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
local delta_from_click_pos
if ImGui.IsMousePosValid(io.MousePos) then
delta_from_click_pos = (io.MousePos - io.MouseClickedPos[i])
else
delta_from_click_pos = ImVec2(0.0, 0.0)
end
io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos))
io.MouseDragMaxDistanceAbs[i].x = ImMax(io.MouseDragMaxDistanceAbs[i].x, (delta_from_click_pos.x < 0.0) and -delta_from_click_pos.x or delta_from_click_pos.x)
io.MouseDragMaxDistanceAbs[i].y = ImMax(io.MouseDragMaxDistanceAbs[i].y, (delta_from_click_pos.y < 0.0) and -delta_from_click_pos.y or delta_from_click_pos.y)
end
-- We provide io.MouseDoubleClicked[] as a legacy service
io.MouseDoubleClicked[i] = (io.MouseClickedCount[i] == 2)
-- Clicking any mouse button reactivate mouse hovering which may have been deactivated by keyboard/gamepad navigation
if (io.MouseClicked[i]) then
g.NavHighlightItemUnderNav = false
end
end
end
--- @param window? ImGuiWindow
--- @param wheel_amount float
local function LockWheelingWindow(window, wheel_amount)
local g = GImGui
if window then
g.WheelingWindowReleaseTimer = ImMin(g.WheelingWindowReleaseTimer + ImAbs(wheel_amount) * WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER, WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER)
else
g.WheelingWindowReleaseTimer = 0.0
end
if (g.WheelingWindow == window) then
return
end
-- IMGUI_DEBUG_LOG_IO("[io] LockWheelingWindow() \"%s\"\n", window ? window->Name : "NULL")
g.WheelingWindow = window
ImVec2_Copy(g.WheelingWindowRefMousePos, g.IO.MousePos)
if window == nil then
g.WheelingWindowStartFrame = -1
g.WheelingAxisAvg = ImVec2(0.0, 0.0)
end
end
--- @param wheel ImVec2
--- @return ImGuiWindow?
local function FindBestWheelingWindow(wheel)
-- For each axis, find window in the hierarchy that may want to use scrolling
local g = GImGui
local windows = {nil, nil}
for axis = 1 --[[ImGuiAxis.X]], 2 --[[ImGuiAxis.Y]] do
if wheel[axis] ~= 0.0 then
windows[axis] = g.HoveredWindow
local window = g.HoveredWindow
while bit32.band(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0 do
-- Bubble up into parent window if:
-- - a child window doesn't allow any scrolling.
-- - a child window has the ImGuiWindowFlags.NoScrollWithMouse flag.
-- -- - a child window doesn't need scrolling because it is already at the edge for the direction we are going in (FIXME-WIP)
local has_scrolling = (window.ScrollMax[axis] ~= 0.0)
local inputs_disabled = (bit32.band(window.Flags, 16 --[[ImGuiWindowFlags.NoScrollWithMouse]]) ~= 0) and not (bit32.band(window.Flags, 512 --[[ImGuiWindowFlags.NoMouseInputs]]) ~= 0)
-- local scrolling_past_limits = (wheel_v < 0.0) and (window.Scroll[axis] <= 0.0) or (window.Scroll[axis] >= window.ScrollMax[axis])
if has_scrolling and not inputs_disabled then -- and not scrolling_past_limits
break -- select this window
end
windows[axis] = window.ParentWindow
window = window.ParentWindow
end
end
end
if windows[1] == nil and windows[2] == nil then
return nil
end
-- If there's only one window or only one axis then there's no ambiguity
if (windows[1] == windows[2] or windows[1] == nil or windows[2] == nil) then
return windows[2] and windows[2] or windows[1]
end
-- If candidate are different windows we need to decide which one to prioritize
-- - First frame: only find a winner if one axis is zero.
-- - Subsequent frames: only find a winner when one is more than the other.
if (g.WheelingWindowStartFrame == -1) then
g.WheelingWindowStartFrame = g.FrameCount
end
if ((g.WheelingWindowStartFrame == g.FrameCount and wheel.x ~= 0.0 and wheel.y ~= 0.0) or (g.WheelingAxisAvg.x == g.WheelingAxisAvg.y)) then
ImVec2_Copy(g.WheelingWindowWheelRemainder, wheel)
return nil
end
return (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) and windows[1] or windows[2]
end
-- Called by NewFrame()
function ImGui.UpdateMouseWheel()
-- Reset the locked window if we move the mouse or after the timer elapses.
-- FIXME: Ideally we could refactor to have one timer for "changing window w/ same axis" and a shorter timer for "changing window or axis w/ other axis" (#3795)
local g = GImGui
if g.WheelingWindow ~= nil then
g.WheelingWindowReleaseTimer = g.WheelingWindowReleaseTimer - g.IO.DeltaTime
if ImGui.IsMousePosValid() and ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold then
g.WheelingWindowReleaseTimer = 0.0
end
if g.WheelingWindowReleaseTimer <= 0.0 then
LockWheelingWindow(nil, 0.0)
end
end
local mouse_window = g.WheelingWindow and g.WheelingWindow or g.HoveredWindow
if not mouse_window or mouse_window.Collapsed then
return
end
local owner_id = mouse_window.ID
local wheel = ImVec2()
wheel.x = ImGui.TestKeyOwner(661 --[[ImGuiKey.MouseWheelX]], owner_id) and g.IO.MouseWheelH or 0.0
wheel.y = ImGui.TestKeyOwner(662 --[[ImGuiKey.MouseWheelY]], owner_id) and g.IO.MouseWheel or 0.0
-- IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y)
if g.WheelingWindow ~= nil then
ImGui.SetKeyOwner(661 --[[ImGuiKey.MouseWheelX]], owner_id)
ImGui.SetKeyOwner(662 --[[ImGuiKey.MouseWheelY]], owner_id)
end
-- Zoom / Scale window
-- FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
if wheel.y ~= 0.0 and g.IO.KeyCtrl and g.IO.FontAllowUserScaling then
LockWheelingWindow(mouse_window, wheel.y)
local window = mouse_window
local new_font_scale = ImClamp(window.FontWindowScale + g.IO.MouseWheel * 0.10, 0.50, 2.50)
local scale = new_font_scale / window.FontWindowScale
window.FontWindowScale = new_font_scale
if window == window.RootWindow then
local offset = window.Size * (1.0 - scale) * (g.IO.MousePos - window.Pos) / window.Size
ImGui.SetWindowPos(window, window.Pos + offset, 0)
-- MarkIniSettingsDirty(window)
end
return
end
if g.IO.KeyCtrl then
return
end
-- Mouse wheel scrolling
-- Read about io.MouseWheelRequestAxisSwap and its issue on Mac+Emscripten in UpdateMouseInputs()
if (g.IO.MouseWheelRequestAxisSwap) then
wheel = ImVec2(wheel.y, 0.0)
end
-- Maintain a rough average of moving magnitude on both axes
-- FIXME: should by based on wall clock time rather than frame-counter
g.WheelingAxisAvg.x = ImStd.ImExponentialMovingAverage(g.WheelingAxisAvg.x, ImAbs(wheel.x), 30)
g.WheelingAxisAvg.y = ImStd.ImExponentialMovingAverage(g.WheelingAxisAvg.y, ImAbs(wheel.y), 30)
-- In the rare situation where FindBestWheelingWindow() had to defer first frame of wheeling due to ambiguous main axis, reinject it now.
wheel = wheel + g.WheelingWindowWheelRemainder
g.WheelingWindowWheelRemainder = ImVec2(0.0, 0.0)
if (wheel.x == 0.0 and wheel.y == 0.0) then
return
end
-- Mouse wheel scrolling: find target and apply
-- - don't renew lock if axis doesn't apply on the window.
-- - select a main axis when both axes are being moved.
local window
if g.WheelingWindow then
window = g.WheelingWindow
else
window = FindBestWheelingWindow(wheel)
end
if window then
if not (bit32.band(window.Flags, 16 --[[ImGuiWindowFlags.NoScrollWithMouse]]) ~= 0) and not (bit32.band(window.Flags, 512 --[[ImGuiWindowFlags.NoMouseInputs]]) ~= 0) then
local do_scroll = { wheel.x ~= 0.0 and window.ScrollMax.x ~= 0.0, wheel.y ~= 0.0 and window.ScrollMax.y ~= 0.0 }
if do_scroll[1 --[[ImGuiAxis.X]]] and do_scroll[2 --[[ImGuiAxis.Y]]] then
do_scroll[(g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) and 2 --[[ImGuiAxis.Y]] or 1 --[[ImGuiAxis.X]]] = false
end
if do_scroll[1 --[[ImGuiAxis.X]]] then
LockWheelingWindow(window, wheel.x)
local max_step = window.InnerRect:GetWidth() * 0.67
local scroll_step = ImTrunc(ImMin(2 * window.FontRefSize, max_step))
ImGui.SetScrollX(window, window.Scroll.x - wheel.x * scroll_step)
g.WheelingWindowScrolledFrame = g.FrameCount
end
if do_scroll[2 --[[ImGuiAxis.Y]]] then
LockWheelingWindow(window, wheel.y)
local max_step = window.InnerRect:GetHeight() * 0.67
local scroll_step = ImTrunc(ImMin(5 * window.FontRefSize, max_step))
ImGui.SetScrollY(window, window.Scroll.y - wheel.y * scroll_step)
g.WheelingWindowScrolledFrame = g.FrameCount
end
end
end
end
-- Called once a frame. Followed by SetCurrentFont() which sets up the remaining data.
-- FIXME-VIEWPORT: the concept of a single ClipRectFullscreen is not ideal!
local function SetupDrawListSharedData()
local g = GImGui
local virtual_space = ImRect(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]], -3.4028234663853e+38 --[[FLT_MAX]], -3.4028234663853e+38 --[[FLT_MAX]])
for _, viewport in g.Viewports:iter() do
virtual_space:Add(viewport:GetMainRect())
end
g.DrawListSharedData.ClipRectFullscreen = virtual_space:ToVec4()
g.DrawListSharedData.CurveTessellationMaxError = g.Style.CurveTessellationMaxError
g.DrawListSharedData:SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError)
g.DrawListSharedData.InitialDrawFlags = 0 --[[ImDrawFlags.None]]
if bit32.band(g.IO.Fonts.Flags, 4 --[[ImFontAtlasFlags.NoBakedLines]]) ~= 0 then
g.Style.AntiAliasedLines = false; g.Style.AntiAliasedLineEnds = false
end
if g.Style.AntiAliasedFill then
g.DrawListSharedData.InitialDrawFlags = bit32.bor(g.DrawListSharedData.InitialDrawFlags, 4096 --[[ImDrawFlags.AAFill]])
end
if g.Style.AntiAliasedLines then
g.DrawListSharedData.InitialDrawFlags = bit32.bor(g.DrawListSharedData.InitialDrawFlags, 8192 --[[ImDrawFlags.AALines]])
end
if g.Style.AntiAliasedLineEnds then
g.DrawListSharedData.InitialDrawFlags = bit32.bor(g.DrawListSharedData.InitialDrawFlags, 16384 --[[ImDrawFlags.AALineEnds]])
end
if g.IO.ConfigDebugDrawListDefaultsToStrokeLegacy then
g.DrawListSharedData.InitialDrawFlags = bit32.bor(g.DrawListSharedData.InitialDrawFlags, 917504 --[[ImDrawFlags.StrokeLegacy]])
end
if bit32.band(g.IO.Fonts.Flags, 8 --[[ImFontAtlasFlags.NoBakedRoundCorners]]) == 0 then
g.DrawListSharedData.InitialDrawFlags = bit32.bor(g.DrawListSharedData.InitialDrawFlags, 2097152 --[[ImDrawFlags.UseTexForRoundCorners]], 8388608 --[[ImDrawFlags.AllowTexForRoundCorners_]])
end
if bit32.band(g.IO.BackendFlags, 8 --[[ImGuiBackendFlags.RendererHasVtxOffset]]) ~= 0 then
g.DrawListSharedData.InitialDrawFlags = bit32.bor(g.DrawListSharedData.InitialDrawFlags, 4194304 --[[ImDrawFlags.UseVtxOffset]])
end
end
--- @param viewport ImGuiViewportP
local function InitViewportDrawData(viewport)
local g = GImGui
local draw_data = viewport.DrawDataP
viewport.DrawData = draw_data -- Make publicly accessible
viewport.DrawDataBuilder.Layers[1] = draw_data.CmdLists
viewport.DrawDataBuilder.Layers[2] = viewport.DrawDataBuilder.LayerData1
viewport.DrawDataBuilder.Layers[1]:resize(0)
viewport.DrawDataBuilder.Layers[2]:resize(0)
-- When minimized, we report draw_data->DisplaySize as zero to be consistent with non-viewport mode,
-- and to allow applications/backends to easily skip rendering.
-- FIXME: Note that we however do NOT attempt to report "zero drawlist / vertices" into the ImDrawData structure.
-- This is because the work has been done already, and its wasted! We should fix that and add optimizations for
-- it earlier in the pipeline, rather than pretend to hide the data at the end of the pipeline.
local is_minimized = bit32.band(viewport.Flags, 4096 --[[ImGuiViewportFlags.IsMinimized]]) ~= 0
draw_data.Valid = true
draw_data.FrameCount = g.FrameCount
draw_data.TotalVtxCount = 0
draw_data.TotalIdxCount = 0
draw_data.DisplayPos = viewport.Pos
if is_minimized then
draw_data.DisplaySize = ImVec2(0.0, 0.0)
else
ImVec2_Copy(draw_data.DisplaySize, viewport.Size)
end
if viewport.FramebufferScale.x ~= 0.0 then
draw_data.FramebufferScale = viewport.FramebufferScale
else
draw_data.FramebufferScale = g.IO.DisplayFramebufferScale
end
draw_data.OwnerViewport = viewport
draw_data.Textures = g.PlatformIO.Textures
end
-- FIXME-DPI: PLEASE DO NOT USE. I don't want to formalize this just yet. Because reasons.
function ImGui.GetScale()
local g = GImGui
return g.Style._MainScale
end
--- @return ImGuiWindow
function ImGui.GetCurrentWindowRead()
local g = GImGui
return g.CurrentWindow
end
--- @return ImGuiWindow
@native
function ImGui.GetCurrentWindow()
local g = GImGui
g.CurrentWindow.WriteAccessed = true
return g.CurrentWindow
end
-- Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.
-- - When using this function it is sane to ensure that float are perfectly rounded to integer values,
-- so that e.g. (int)(max.x-min.x) in user's render produce correct result.
-- - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect():
-- some frequently called functions which to modify both channels and clipping simultaneously tend to use the
-- more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds.
-- - This is analogous to PushFont()/PopFont() in the sense that are a mixing a global stack and a window stack,
-- which in the case of ClipRect is not so problematic but tends to be more restrictive for fonts.
--- @param clip_rect_min ImVec2
--- @param clip_rect_max ImVec2
--- @param intersect_with_current_clip_rect? bool
@native
function ImGui.PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect)
local window = GImGui.CurrentWindow -- [Luau] inline GetCurrentWindow()
window.WriteAccessed = true
local draw_list = window.DrawList
draw_list:PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect)
local stack = draw_list._ClipRectStack
ImRect_CopyFromV4(window.ClipRect, stack.Data[stack.Size]) -- [Luau] inline back()
end
function ImGui.PopClipRect()
local window = GImGui.CurrentWindow -- [Luau] inline GetCurrentWindow()
window.WriteAccessed = true
local draw_list = window.DrawList
draw_list:PopClipRect()
local stack = draw_list._ClipRectStack
ImRect_CopyFromV4(window.ClipRect, stack.Data[stack.Size]) -- [Luau] inline back()
end
--- @param viewport ImGuiViewportP
--- @param drawlist_no size_t # background(1), foreground(2)
--- @param drawlist_name string
--- @return ImDrawList
local function GetViewportBgFgDrawList(viewport, drawlist_no, drawlist_name)
-- Create the draw list on demand, because they are not frequently used for all viewports
local g = GImGui
assert(drawlist_no <= 2) -- IM_COUNTOF(viewport->BgFgDrawLists)
local draw_list = viewport.BgFgDrawLists[drawlist_no]
if draw_list == nil then
draw_list = ImDrawList(g.DrawListSharedData)
draw_list._OwnerName = drawlist_name
viewport.BgFgDrawLists[drawlist_no] = draw_list
end
-- Our ImDrawList system requires that there is always a command
if viewport.BgFgDrawListsLastFrameActive[drawlist_no] ~= g.FrameCount then
draw_list:_ResetForNewFrame()
draw_list:_SetPixelDensity(viewport.FramebufferScale.x)
draw_list:PushTexture(g.IO.Fonts.TexRef)
draw_list:PushClipRect(viewport.Pos, viewport.Pos + viewport.Size, false)
viewport.BgFgDrawListsLastFrameActive[drawlist_no] = g.FrameCount
viewport.BgFgDrawListsLastTimeActive[drawlist_no] = g.Time
end
return draw_list
end
--- @param viewport? ImGuiViewport
function ImGui.GetBackgroundDrawList(viewport)
local g = GImGui
if (viewport == nil) then
viewport = g.CurrentWindow.Viewport
end
--- @cast viewport ImGuiViewportP
return GetViewportBgFgDrawList(viewport, 1, "##Background")
end
--- @param viewport? ImGuiViewport
function ImGui.GetForegroundDrawList(viewport)
local g = GImGui
if (viewport == nil) then
viewport = g.CurrentWindow.Viewport
end
--- @cast viewport ImGuiViewportP
return GetViewportBgFgDrawList(viewport, 2, "##Foreground")
end
-- FIXME: Add a more explicit sort order in the window structure.
--- @param lhs ImGuiWindow
--- @param rhs ImGuiWindow
local function ChildWindowComparer(lhs, rhs)
local a, b = lhs, rhs
local d = (bit32.band(a.Flags, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0 and 1 or 0) - (bit32.band(b.Flags, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0 and 1 or 0)
if d ~= 0 then
return (d < 0)
end
d = (bit32.band(a.Flags, 33554432 --[[ImGuiWindowFlags.Tooltip]]) ~= 0 and 1 or 0) - (bit32.band(b.Flags, 33554432 --[[ImGuiWindowFlags.Tooltip]]) ~= 0 and 1 or 0)
if d ~= 0 then
return (d < 0)
end
return (a.BeginOrderWithinParent - b.BeginOrderWithinParent < 0)
end
--- @param out_sorted_windows ImVector<ImGuiWindow>
--- @param window ImGuiWindow
local function AddWindowToSortBuffer(out_sorted_windows, window)
out_sorted_windows:push_back(window)
if window.Active then
local count = window.DC.ChildWindows.Size
ImStd.ImQsort(window.DC.ChildWindows.Data, count, ChildWindowComparer)
for i = 1, count do
local child = window.DC.ChildWindows[i]
if child.Active then
AddWindowToSortBuffer(out_sorted_windows, child)
end
end
end
end
local function AddWindowToDrawData(window, layer)
local g = GImGui
local viewport = window.Viewport
g.IO.MetricsRenderWindows = g.IO.MetricsRenderWindows + 1
if window.DrawList._Splitter._Count > 1 then
window.DrawList:ChannelsMerge() -- Merge if user forgot to merge back. Also required in Docking branch for ImGuiWindowFlags.DockNodeHost windows.
end
ImGui.AddDrawListToDrawDataEx(viewport.DrawDataP, viewport.DrawDataBuilder.Layers[layer], window.DrawList)
for _, child in window.DC.ChildWindows:iter() do
if (ImGui.IsWindowActiveAndVisible(child)) then -- Clipped children may have been marked not active
AddWindowToDrawData(child, layer)
end
end
end
--- @param window ImGuiWindow
local function GetWindowDisplayLayer(window)
return (bit32.band(window.Flags, 33554432 --[[ImGuiWindowFlags.Tooltip]]) ~= 0) and 2 or 1
end
-- Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
--- @param window ImGuiWindow
local function AddRootWindowToDrawData(window)
AddWindowToDrawData(window, GetWindowDisplayLayer(window))
end
--- @param builder ImDrawDataBuilder
local function FlattenDrawDataIntoSingleLayer(builder)
local n = builder.Layers[1].Size
local full_size = n
for i = 2, #builder.Layers do
full_size = full_size + builder.Layers[i].Size
end
builder.Layers[1]:resize(full_size)
for layer_n = 2, #builder.Layers do
local layer = builder.Layers[layer_n]
if not layer:empty() then
for i = 1, layer.Size do
builder.Layers[1].Data[n + i] = layer.Data[i]
end
n = n + layer.Size
layer:resize(0)
end
end
end
function ImGui.NewFrame()
assert(GImGui ~= nil, "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?")
local g = GImGui
-- Remove pending delete hooks before frame start.
-- This deferred removal avoid issues of removal while iterating the hook vector
for n = g.Hooks.Size, 1, -1 do
if g.Hooks.Data[n].Type == 7 --[[ImGuiContextHookType.PendingRemoval_]] then
g.Hooks:erase(n)
end
end
ImGui.CallContextHooks(g, 0 --[[ImGuiContextHookType.NewFramePre]])
-- Check and assert for various common IO and Configuration mistakes
g.ConfigFlagsLastFrame = g.ConfigFlagsCurrFrame
ImGui.ErrorCheckNewFrameSanityChecks()
g.ConfigFlagsCurrFrame = g.IO.ConfigFlags
-- Load settings on first frame, save settings when modified (after a delay)
ImGui.UpdateSettings()
g.Time = g.Time + g.IO.DeltaTime
g.FrameCount = g.FrameCount + 1
g.SessionDate = ImGuiPackedDate(g.PlatformIO.Platform_SessionDate)
g.TooltipOverrideCount = 0
g.WindowsActiveCount = 0
g.MenusIdSubmittedThisFrame:resize(0)
-- Calculate frame-rate for the user, as a purely luxurious feature
-- FIXME: are lines below correct and necessary
g.FramerateSecPerFrameAccum = g.FramerateSecPerFrameAccum + (g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx])
g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime
g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % 60
g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, 60)
if g.FramerateSecPerFrameAccum > 0 then
g.IO.Framerate = (1.0 / (g.FramerateSecPerFrameAccum / g.FramerateSecPerFrameCount))
else
g.IO.Framerate = 3.4028234663853e+38 --[[FLT_MAX]]
end
-- Process input queue (trickle as many events as possible), turn events into writes to IO structure
g.InputEventsTrail:resize(0)
ImGui.UpdateInputEvents(g.IO.ConfigInputTrickleEventQueue)
-- Update viewports (after processing input queue, so io.MouseHoveredViewport is set)
ImGui.UpdateViewportsNewFrame()
-- Update texture list (collect destroyed textures, etc.)
ImGui.UpdateTexturesNewFrame()
-- Setup current font and draw list shared data
SetupDrawListSharedData()
ImGui.UpdateFontsNewFrame()
g.WithinFrameScope = true
-- Mark rendering data as invalid to prevent user who may have a handle on it to use it.
for _, viewport in g.Viewports:iter() do
viewport.DrawData = nil
viewport.DrawDataP.Valid = false
end
-- Drag and drop keep the source ID alive so even if the source disappear our state is consistent
if g.DragDropActive and g.DragDropPayload.SourceId == g.ActiveId then
ImGui.KeepAliveID(g.DragDropPayload.SourceId)
end
-- [DEBUG]
if not g.IO.ConfigDebugHighlightIdConflicts or not g.IO.KeyCtrl then -- Count is locked while holding Ctrl
g.DebugDrawIdConflictsId = 0
end
if g.IO.ConfigDebugHighlightIdConflicts and g.HoveredIdPreviousFrameItemCount > 1 then
g.DebugDrawIdConflictsId = g.HoveredIdPreviousFrame
end
-- Update HoveredId data
if not g.HoveredIdPreviousFrame then
g.HoveredIdTimer = 0.0
end
if not g.HoveredIdPreviousFrame or (g.HoveredId and g.ActiveId == g.HoveredId) then
g.HoveredIdNotActiveTimer = 0.0
end
if g.HoveredId then
g.HoveredIdTimer = g.HoveredIdTimer + g.IO.DeltaTime
end
if g.HoveredId and g.ActiveId ~= g.HoveredId then
g.HoveredIdNotActiveTimer = g.HoveredIdNotActiveTimer + g.IO.DeltaTime
end
g.HoveredIdPreviousFrame = g.HoveredId
g.HoveredIdPreviousFrameItemCount = 0
g.HoveredId = 0
g.HoveredIdAllowOverlap = false
g.HoveredIdIsDisabled = false
-- Clear ActiveID if the item is not alive anymore.
-- In 1.87, the common most call to KeepAliveID() was moved from GetID() to ItemAdd().
-- As a result, custom widget using ButtonBehavior() _without_ ItemAdd() need to call KeepAliveID() themselves.
if (g.ActiveId ~= 0 and g.ActiveIdIsAlive ~= g.ActiveId and g.ActiveIdPreviousFrame == g.ActiveId) then
IMGUI_DEBUG_LOG_ACTIVEID("NewFrame(): ClearActiveID() 0x%08X because it isn't marked alive anymore!", g.ActiveId)
ImGui.ClearActiveID()
end
-- Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)
if g.ActiveId then
g.ActiveIdTimer = g.ActiveIdTimer + g.IO.DeltaTime
end
if g.ActiveId and g.ActiveId == g.LastActiveId then
g.LastActiveIdWasSelected = g.ActiveIdWasSelected
g.LastActiveIdWasSoleSelected = g.ActiveIdWasSoleSelected
end
g.LastActiveIdTimer = g.LastActiveIdTimer + g.IO.DeltaTime
g.ActiveIdPreviousFrame = g.ActiveId
g.ActiveIdIsAlive = 0
g.AnyIdHasBeenEditedThisFrame = false
g.ActiveIdHasBeenEditedThisFrame = false
g.ActiveIdIsJustActivated = false
if g.TempInputId ~= 0 and g.ActiveId ~= g.TempInputId then
g.TempInputId = 0
end
if g.InputTextReactivateId ~= 0 and g.InputTextReactivateId ~= g.DeactivatedItemData.ID then
g.InputTextReactivateId = 0
end
if g.ActiveId == 0 then
g.ActiveIdUsingNavDirMask = 0x00
g.ActiveIdUsingAllKeyboardKeys = false
end
if g.DeactivatedItemData.ElapseFrame < g.FrameCount then
g.DeactivatedItemData.ID = 0
end
g.DeactivatedItemData.IsAlive = false
if g.InputTextDeactivatedState.ElapseFrame < g.FrameCount then
g.InputTextDeactivatedState.ID = 0
end
-- Record when we have been stationary as this state is preserved while over same item.
-- FIXME: The way this is expressed means user cannot alter HoverStationaryDelay during the frame to use varying values.
-- To allow this we should store HoverItemMaxStationaryTime+ID and perform the >= check in IsItemHovered() function.
if g.HoverItemDelayId ~= 0 and g.MouseStationaryTimer >= g.Style.HoverStationaryDelay then
g.HoverItemUnlockedStationaryId = g.HoverItemDelayId
elseif g.HoverItemDelayId == 0 then
g.HoverItemUnlockedStationaryId = 0
end
if g.HoveredWindow ~= nil and g.MouseStationaryTimer >= g.Style.HoverStationaryDelay then
g.HoverWindowUnlockedStationaryId = g.HoveredWindow.ID
elseif g.HoveredWindow == nil then
g.HoverWindowUnlockedStationaryId = 0
end
-- Update hover delay for IsItemHovered() with delays and tooltips
g.HoverItemDelayIdPreviousFrame = g.HoverItemDelayId
if g.HoverItemDelayId ~= 0 then
g.HoverItemDelayTimer = g.HoverItemDelayTimer + g.IO.DeltaTime
g.HoverItemDelayClearTimer = 0.0
g.HoverItemDelayId = 0
elseif g.HoverItemDelayTimer > 0.0 then
-- This gives a little bit of leeway before clearing the hover timer, allowing mouse to cross gaps
-- We could expose 0.25f as style.HoverClearDelay but I am not sure of the logic yet, this is particularly subtle.
g.HoverItemDelayClearTimer = g.HoverItemDelayClearTimer + g.IO.DeltaTime
if g.HoverItemDelayClearTimer >= ImMax(0.25, g.IO.DeltaTime * 2.0) then -- ~7 frames at 30 Hz + allow for low framerate
g.HoverItemDelayTimer = 0.0
g.HoverItemDelayClearTimer = 0.0 -- May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer.
end
end
-- Close popups on focus lost (currently wip/opt-in)
-- if (g.IO.AppFocusLost)
-- ClosePopupsExceptModals();
-- Update keyboard input state
ImGui.UpdateKeyboardInputs()
-- IM_ASSERT(g.IO.KeyCtrl == IsKeyDown(ImGuiKey_LeftCtrl) || IsKeyDown(ImGuiKey_RightCtrl));
-- IM_ASSERT(g.IO.KeyShift == IsKeyDown(ImGuiKey_LeftShift) || IsKeyDown(ImGuiKey_RightShift));
-- IM_ASSERT(g.IO.KeyAlt == IsKeyDown(ImGuiKey_LeftAlt) || IsKeyDown(ImGuiKey_RightAlt));
-- IM_ASSERT(g.IO.KeySuper == IsKeyDown(ImGuiKey_LeftSuper) || IsKeyDown(ImGuiKey_RightSuper));
-- Drag and drop
g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr
g.DragDropAcceptIdCurr = 0
g.DragDropAcceptFlagsPrev = g.DragDropAcceptFlagsCurr
g.DragDropAcceptFlagsCurr = 0 --[[ImGuiDragDropFlags.None]]
g.DragDropAcceptIdCurrRectSurface = 3.4028234663853e+38 --[[FLT_MAX]]
g.DragDropWithinSource = false
g.DragDropWithinTarget = false
g.DragDropHoldJustPressedId = 0
if g.DragDropActive then
-- Also works when g.ActiveId==0 (aka leftover payload in progress, no active id)
-- You may disable this externally by hijacking the input route:
-- 'if (GetDragDropPayload() ~= nil) then Shortcut(ImGuiKey.Escape, ImGuiInputFlags.RouteGlobal | ImGuiInputFlags.RouteOverActive) end
-- but you will not get a return value from Shortcut() due to ActiveIdUsingAllKeyboardKeys logic. You can however poll IsKeyPressed(ImGuiKey.Escape) afterwards.
local owner_id = (g.ActiveId ~= 0) and g.ActiveId or ImHashStr("##DragDropCancelHandler")
if ImGui.Shortcut(526 --[[ImGuiKey.Escape]], 4096 --[[ImGuiInputFlags.RouteGlobal]], owner_id) then
ImGui.ClearActiveID()
ImGui.ClearDragDrop()
end
end
g.TooltipPreviousWindow = nil
-- Update keyboard/gamepad navigation
ImGui.NavUpdate()
-- Update mouse input state
ImGui.UpdateMouseInputs()
-- Undocking
-- (needs to be before UpdateMouseMovingWindowNewFrame so the window is already offset and following the mouse on the detaching frame)
ImGui.DockContextNewFrameUpdateUndocking(g)
-- Mark all windows as not visible and compact unused memory.
assert(g.WindowsFocusOrder.Size <= g.Windows.Size)
local gc_all = g.GcCompactAll or g.IO.ConfigMemoryCompactTimer < 0.0
local memory_compact_start_time = gc_all and 3.4028234663853e+38 --[[FLT_MAX]] or (g.Time - g.IO.ConfigMemoryCompactTimer)
for _, window in g.Windows:iter() do
window.WasActive = window.Active
window.Active = false
window.WriteAccessed = false
window.BeginCountPreviousFrame = window.BeginCount
window.BeginCount = 0
-- Garbage collect transient buffers of recently unused windows
if (not window.WasActive or gc_all) and not window.MemoryCompacted and window.LastTimeActive < memory_compact_start_time then
ImGui.GcCompactTransientWindowBuffers(window)
end
end
-- Find hovered window
-- (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
-- (currently needs to be done after the WasActive=Active loop and FindHoveredWindowEx uses ->Active)
ImGui.UpdateHoveredWindowAndCaptureFlags(g.IO.MousePos)
-- Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
ImGui.UpdateMouseMovingWindowNewFrame()
-- Background darkening/whitening
if ImGui.GetTopMostPopupModal() ~= nil or (g.NavWindowingTarget ~= nil and g.NavWindowingHighlightAlpha > 0.0) then
g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0, 1.0)
else
g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0, 0.0)
end
g.MouseCursor = 0 --[[ImGuiMouseCursor.Arrow]]
g.WantCaptureMouseNextFrame = -1
g.WantCaptureKeyboardNextFrame = -1
g.WantTextInputNextFrame = -1
-- Platform IME data: reset for the frame
ImGuiPlatformImeData_Copy(g.PlatformImeDataPrev, g.PlatformImeData)
g.PlatformImeData.WantVisible = false
g.PlatformImeData.WantTextInput = false
-- Mouse wheel scrolling, scale
ImGui.UpdateMouseWheel()
-- Garbage collect transient buffers of recently unused tables
for i = 1, g.TablesLastTimeActive.Size do
if g.TablesLastTimeActive.Data[i] >= 0.0 and g.TablesLastTimeActive.Data[i] < memory_compact_start_time then
ImGui.TableGcCompactTransientBuffers(g.Tables:GetByIndex(i - 1))
end
end
for _, table_temp_data in g.TablesTempData:iter() do
table_temp_data.ReconcileColumnsRequests:clear() -- Unusual: clear every frame because this is rarely used.
if table_temp_data.LastTimeActive >= 0.0 and table_temp_data.LastTimeActive < memory_compact_start_time then
ImGui.TableGcCompactTransientBuffers(table_temp_data)
end
end
if g.GcCompactAll then
ImGui.GcCompactTransientMiscBuffers()
end
g.GcCompactAll = false
-- Closing the focused window restore focus to the first active root window in descending z-order
if (g.NavWindow and not g.NavWindow.WasActive) then
ImGui.FocusTopMostWindowUnderOne(nil, nil, nil, 1 --[[ImGuiFocusRequestFlags.RestoreFocusedChild]])
end
-- No window should be open at the beginning of the frame.
-- But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
g.CurrentWindowStack:resize(0)
g.BeginPopupStack:resize(0)
g.ItemFlagsStack:resize(0)
g.ItemFlagsStack:push_back(144 --[[ImGuiItemFlags.Default_]]) -- Default flags
g.CurrentItemFlags = g.ItemFlagsStack:back()
g.GroupStack:resize(0)
-- Docking
ImGui.DockContextNewFrameUpdateDocking(g)
-- [DEBUG] Update debug features
if not IMGUI_DISABLE_DEBUG_TOOLS then
ImGui.UpdateDebugToolItemPicker()
ImGui.UpdateDebugToolItemPathQuery()
ImGui.UpdateDebugToolFlashStyleColor()
if g.DebugLocateFrames > 0 then
g.DebugLocateFrames = g.DebugLocateFrames - 1
if g.DebugLocateFrames == 0 then
g.DebugLocateId = 0
g.DebugBreakInLocateId = false
end
end
if (g.DebugLogAutoDisableFrames or 0) > 0 then
g.DebugLogAutoDisableFrames = g.DebugLogAutoDisableFrames - 1
if g.DebugLogAutoDisableFrames == 0 then
ImGui.DebugLog("(Debug Log: Auto-disabled some ImGuiDebugLogFlags after 2 frames)\n")
g.DebugLogFlags = bit32.band(g.DebugLogFlags, bit32.bnot(g.DebugLogAutoDisableFlags))
g.DebugLogAutoDisableFlags = 0 --[[ImGuiDebugLogFlags.None]]
end
end
end
-- Create implicit/fallback window - which we will only render it if the user has added something to it.
-- We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
-- This fallback is particularly important as it prevents ImGui:: calls from crashing.
g.WithinFrameScopeWithImplicitWindow = true
ImGui.SetNextWindowSize(ImVec2(400, 400), 4 --[[ImGuiCond.FirstUseEver]])
ImGui.Begin("Debug##Default")
assert(g.CurrentWindow.IsFallbackWindow == true)
-- Store stack sizes
g.ErrorCountCurrentFrame = 0
g.StackSizesInNewFrame = g.StackSizesInNewFrame or ImGuiErrorRecoveryState()
ImGui.ErrorRecoveryStoreState(g.StackSizesInNewFrame)
-- [DEBUG] When io.ConfigDebugBeginReturnValue is set, we make Begin()/BeginChild() return false at different level of the window-stack,
-- allowing to validate correct Begin/End behavior in user code.
if g.IO.ConfigDebugBeginReturnValueLoop then
g.DebugBeginReturnValueCullDepth = (g.DebugBeginReturnValueCullDepth == -1) and 0 or ((g.DebugBeginReturnValueCullDepth + (((g.FrameCount % 4) == 0) and 1 or 0)) % 10)
else
g.DebugBeginReturnValueCullDepth = -1
end
ImGui.CallContextHooks(g, 1 --[[ImGuiContextHookType.NewFramePost]])
end
-- This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal.
function ImGui.EndFrame()
local g = GImGui
assert(g.Initialized)
-- Don't process EndFrame() multiple times.
if g.FrameCountEnded == g.FrameCount then
return
end
IM_ASSERT_USER_ERROR(g.WithinFrameScope, "Forgot to call ImGui::NewFrame()?")
ImGui.CallContextHooks(g, 2 --[[ImGuiContextHookType.EndFramePre]])
-- [EXPERIMENTAL] Recover from errors
if g.IO.ConfigErrorRecovery and g.StackSizesInNewFrame then
ImGui.ErrorRecoveryTryToRecoverState(g.StackSizesInNewFrame)
end
ImGui.ErrorCheckEndFrameSanityChecks()
ImGui.ErrorCheckEndFrameFinalizeErrorTooltip()
-- Notify Platform/OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
local ime_data = g.PlatformImeData
if g.PlatformIO.Platform_SetImeDataFn ~= nil and not ImGuiPlatformImeData_Compare(ime_data, g.PlatformImeDataPrev) then
local viewport = ImGui.FindViewportByID(ime_data.ViewportId)
if viewport == nil then
viewport = ImGui.GetMainViewport()
end
-- IMGUI_DEBUG_LOG_IO("[io] Calling Platform_SetImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f) for Viewport 0x%08X", ime_data.WantVisible, ime_data.InputPos.x, ime_data.InputPos.y, viewport.ID)
g.PlatformIO.Platform_SetImeDataFn(g, viewport, ime_data)
end
g.WantTextInputNextFrame = ime_data.WantTextInput
-- Hide implicit/fallback "Debug" window if it hasn't been used
g.WithinFrameScopeWithImplicitWindow = false
if (g.CurrentWindow and g.CurrentWindow.IsFallbackWindow and g.CurrentWindow.WriteAccessed == false) then
g.CurrentWindow.Active = false
end
ImGui.End()
-- Update navigation: Ctrl+Tab, wrap-around requests
ImGui.NavEndFrame()
-- Update docking
ImGui.DockContextEndFrame(g)
ImGui.SetCurrentViewport(nil, nil)
-- Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
if g.DragDropActive then
local is_delivered = g.DragDropPayload.Delivery
local is_elapsed = (g.DragDropSourceFrameCount + 1 < g.FrameCount) and (bit32.band(g.DragDropSourceFlags, 32 --[[ImGuiDragDropFlags.PayloadAutoExpire]]) ~= 0 or g.DragDropMouseButton == -1 or not ImGui.IsMouseDown(g.DragDropMouseButton))
if is_delivered or is_elapsed then ImGui.ClearDragDrop() end
end
-- Drag and Drop: Fallback for missing source tooltip. This is not ideal but better than nothing.
-- If you want to handle source item disappearing: instead of submitting your description tooltip
-- in the BeginDragDropSource() block of the dragged item, you can submit them from a safe single spot
-- (e.g. end of your item loop, or before EndFrame) by reading payload data.
-- In the typical case, the contents of drag tooltip should be possible to infer solely from payload data.
if g.DragDropActive and g.DragDropSourceFrameCount + 1 < g.FrameCount and (bit32.band(g.DragDropSourceFlags, 1 --[[ImGuiDragDropFlags.SourceNoPreviewTooltip]]) == 0) then
g.DragDropWithinSource = true
ImGui.SetTooltip("...")
g.DragDropWithinSource = false
end
-- End frame
g.WithinFrameScope = false
g.FrameCountEnded = g.FrameCount
ImGui.UpdateFontsEndFrame()
-- Initiate moving window + handle left-click and right-click focus
ImGui.UpdateMouseMovingWindowEndFrame()
-- Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some)
ImGui.UpdateViewportsEndFrame()
-- Sort the window list so that all child windows are after their parent
-- We cannot do that on FocusWindow() because children may not exist yet
g.WindowsTempSortBuffer:resize(0)
g.WindowsTempSortBuffer:reserve(g.Windows.Size)
for _, window in g.Windows:iter() do
repeat
if window.Active and bit32.band(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0 then -- if a child is active its parent will add it
break
end
AddWindowToSortBuffer(g.WindowsTempSortBuffer, window)
until true
end
-- This usually assert if there is a mismatch between the ImGuiWindowFlags.ChildWindow / ParentWindow values and DC.ChildWindows[] in parents, aka we've done something wrong.
assert(g.Windows.Size == g.WindowsTempSortBuffer.Size)
g.Windows:swap(g.WindowsTempSortBuffer)
g.IO.MetricsActiveWindows = g.WindowsActiveCount
ImGui.UpdateTexturesEndFrame()
-- Unlock font atlas
for _, atlas in g.FontAtlases:iter() do
atlas.Locked = false
end
-- Clear Input data for next frame
ImVec2_Copy(g.IO.MousePosPrev, g.IO.MousePos)
g.IO.AppFocusLost = false
g.IO.MouseWheel = 0.0
g.IO.MouseWheelH = 0.0
g.IO.InputQueueCharacters:resize(0)
ImGui.CallContextHooks(g, 3 --[[ImGuiContextHookType.EndFramePost]])
end
-- TODO:
-- Prepare the data for rendering so you can call GetDrawData()
-- (As with anything within the ImGui:: namespace this doesn't touch your GPU or graphics API at all:
-- it is the role of the ImGui_ImplXXXX_RenderDrawData() function provided by the renderer backend)
function ImGui.Render()
local g = GImGui
assert(g.Initialized)
if g.FrameCountEnded ~= g.FrameCount then
ImGui.EndFrame()
end
if g.FrameCountRendered == g.FrameCount then return end
g.FrameCountRendered = g.FrameCount
g.IO.MetricsRenderWindows = 0
ImGui.CallContextHooks(g, 4 --[[ImGuiContextHookType.RenderPre]])
-- Add background ImDrawList (for each active viewport)
for _, viewport in g.Viewports:iter() do
InitViewportDrawData(viewport)
if viewport.BgFgDrawLists[1] ~= nil and viewport.BgFgDrawListsLastFrameActive[1] == g.FrameCount then
ImGui.AddDrawListToDrawDataEx(viewport.DrawDataP, viewport.DrawDataBuilder.Layers[1], ImGui.GetBackgroundDrawList(viewport))
end
end
-- Draw modal/window whitening backgrounds
ImGui.RenderDimmedBackgrounds()
-- Add ImDrawList to render
local windows_to_render_top_most = {nil, nil}
windows_to_render_top_most[1] = (g.NavWindowingTarget and (bit32.band(g.NavWindowingTarget.Flags, 8192 --[[ImGuiWindowFlags.NoBringToFrontOnFocus]]) == 0)) and g.NavWindowingTarget.RootWindowDockTree or nil
windows_to_render_top_most[2] = g.NavWindowingTarget and g.NavWindowingListWindow or nil
for _, window in g.Windows:iter() do
if ImGui.IsWindowActiveAndVisible(window) and (bit32.band(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) == 0) and window ~= windows_to_render_top_most[1] and window ~= windows_to_render_top_most[2] then
AddRootWindowToDrawData(window)
end
end
for n = 1, 2 do
if windows_to_render_top_most[n] and ImGui.IsWindowActiveAndVisible(windows_to_render_top_most[n]) then -- NavWindowingTarget is always temporarily displayed as the top-most window
AddRootWindowToDrawData(windows_to_render_top_most[n])
end
end
-- Draw software mouse cursor if requested by io.MouseDrawCursor flag
if g.IO.MouseDrawCursor and g.MouseCursor ~= (-1) --[[ImGuiMouseCursor.None]] then
ImGui.RenderMouseCursor(g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, 4294967295 --[[IM_COL32_WHITE]], 4278190080 --[[IM_COL32_BLACK]], IM_COL32(0, 0, 0, 48))
end
-- Setup ImDrawData structures for end-user
g.IO.MetricsRenderVertices = 0
g.IO.MetricsRenderIndices = 0
for _, viewport in g.Viewports:iter() do
FlattenDrawDataIntoSingleLayer(viewport.DrawDataBuilder)
-- Add foreground ImDrawList (for each active viewport)
if viewport.BgFgDrawLists[2] ~= nil and viewport.BgFgDrawListsLastFrameActive[2] == g.FrameCount then
ImGui.AddDrawListToDrawDataEx(viewport.DrawDataP, viewport.DrawDataBuilder.Layers[1], ImGui.GetForegroundDrawList(viewport))
end
-- We call _PopUnusedDrawCmd() last thing, as RenderDimmedBackgrounds() rely on a valid command being there (especially in docking branch).
local draw_data = viewport.DrawDataP
for _, draw_list in draw_data.CmdLists:iter() do
draw_list:_PopUnusedDrawCmd()
end
g.IO.MetricsRenderVertices = g.IO.MetricsRenderVertices + draw_data.TotalVtxCount
g.IO.MetricsRenderIndices = g.IO.MetricsRenderIndices + draw_data.TotalIdxCount
end
ImGui.CallContextHooks(g, 5 --[[ImGuiContextHookType.RenderPost]])
end
-- Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
-- CalcTextSize("") should return ImVec2(0.0, g.FontSize)
--- @param text ImString
--- @param text_end? int # Exclusive upper bound
--- @param hide_text_after_double_hash? bool
--- @param wrap_width? float
--- @return ImVec2
@native
function ImGui.CalcTextSize(text, text_end, hide_text_after_double_hash, wrap_width)
return ImGui.CalcTextSizeEx(text, 1, text_end, hide_text_after_double_hash, wrap_width)
end
--- @param text ImString
--- @param text_begin int
--- @param text_end? int # Exclusive upper bound
--- @param hide_text_after_double_hash? bool
--- @param wrap_width? float
--- @return ImVec2
@native
function ImGui.CalcTextSizeEx(text, text_begin, text_end, hide_text_after_double_hash, wrap_width)
if hide_text_after_double_hash == nil then hide_text_after_double_hash = false end
if wrap_width == nil then wrap_width = -1.0 end
local g = GImGui
local text_display_end
if hide_text_after_double_hash then
text_display_end = ImGui.FindRenderedTextEnd(text, text_end) -- Hide anything after a '##' string
else
text_display_end = text_end
end
local font = g.Font
local font_size = g.FontSize
if text == "" or (text_end and text_end <= text_begin) then
return ImVec2(0.0, font_size)
end
local text_size = ImFontCalcTextSizeEx(font, font_size, 3.4028234663853e+38 --[[FLT_MAX]], wrap_width, text, text_begin, text_display_end, text_display_end, nil, 0 --[[ImDrawTextFlags.None]]) -- [Luau] inline font:CalcTextSizeA()
-- Round
-- (see 7b0bf230, 4622fa4b6, #791 for details about this.)
-- FIXME: Add a way to disable this.
text_size.x = math.ceil(text_size.x) -- [Luau] == ImCeilFast() for all finite values, builtin
return text_size
end
-- Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame()
function ImGui.GetDrawData()
local g = GImGui
local viewport = g.Viewports[1]
return viewport.DrawDataP.Valid and viewport.DrawDataP or nil
end
function ImGui.GetTime()
return GImGui.Time
end
-- This function is merely here to free heap allocations.
function ImGui.Shutdown()
local g = GImGui
IM_ASSERT_USER_ERROR(g.IO.BackendPlatformUserData == nil, "Forgot to shutdown Platform backend?")
IM_ASSERT_USER_ERROR(g.IO.BackendRendererUserData == nil, "Forgot to shutdown Renderer backend?")
for _, viewport in g.Viewports:iter() do
-- IM_UNUSED(viewport)
IM_ASSERT_USER_ERROR(viewport.RendererUserData == nil and viewport.PlatformUserData == nil and viewport.PlatformHandle == nil, "Backend or app forgot to call DestroyPlatformWindows()?")
end
-- The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame)
for _, atlas in g.FontAtlases:iter() do
ImGui.UnregisterFontAtlas(atlas)
if atlas.OwnerContext == g then
assert(atlas.RefCount == 0, "Destroying context owning a ImFontAtlas which is still used elsewhere!")
if atlas.RefCount == 0 then
atlas.Locked = false
end
end
end
g.DrawListSharedData.TempBuffer:clear()
-- Cleanup of other data are conditional on actually having initialized Dear ImGui.
if not g.Initialized then
return
end
-- Shutdown extensions
ImGui.DockContextShutdown(g)
ImGui.CallContextHooks(g, 6 --[[ImGuiContextHookType.Shutdown]])
-- Clear everything else
-- [Luau] clear_delete()/clear_destruct() are plain clear(): the GC frees the elements
g.Windows:clear()
g.WindowsFocusOrder:clear()
g.WindowsTempSortBuffer:clear()
g.CurrentWindow = nil
g.CurrentWindowStack:clear()
g.WindowsById = {}
g.NavWindow = nil
g.HoveredWindow = nil; g.HoveredWindowUnderMovingWindow = nil
g.ActiveIdWindow = nil
g.MovingWindow = nil
g.KeysRoutingTable:Clear()
g.ColorStack:clear()
g.StyleVarStack:clear()
g.FontStack:clear()
g.OpenPopupStack:clear()
g.BeginPopupStack:clear()
g.TreeNodeStack:clear()
g.CurrentViewport = nil; g.MouseViewport = nil; g.MouseLastHoveredViewport = nil
g.Viewports:clear()
g.TabBars:Clear()
g.CurrentTabBarStack:clear()
g.ShrinkWidthBuffer:clear()
g.ClipperTempData:clear()
g.Tables:Clear()
g.TablesTempData:clear()
g.DrawChannelsTempMergeBuffer:clear()
g.MultiSelectStorage:Clear()
g.MultiSelectTempData:clear()
g.MenusIdSubmittedThisFrame:clear()
g.InputTextState:ClearFreeMemory()
g.InputTextLineIndex:clear()
g.InputTextDeactivatedState:ClearFreeMemory()
g.SettingsWindows:clear()
g.SettingsHandlers:clear()
if g.LogFile ~= nil then
if g.LogFile ~= "stdout" and ImStd.ImFileClose ~= nil then ImStd.ImFileClose(g.LogFile) end
g.LogFile = nil
end
g.LogBuffer:clear()
g.DebugLogBuf:clear()
g.DebugLogLines = { "" }
g.Initialized = false
end
-- When using multiple context it can be helpful to give name a name.
-- (A) Will be visible in debugger, (B) Will be included in all IMGUI_DEBUG_LOG() calls, (C) Should be <= 15 characters long.
--- @param ctx ImGuiContext
--- @param name string
function ImGui.SetContextName(ctx, name)
ctx.ContextName = string.sub(name, 1, 15) -- [Luau] ImStrncpy() into char[16]
end
-- No specific ordering/dependency support, will see as needed
--- @param ctx ImGuiContext
--- @param hook ImGuiContextHook
--- @return ImGuiID
function ImGui.AddContextHook(ctx, hook)
local g = ctx
assert(hook.Callback ~= nil and (hook.HookId or 0) == 0 and hook.Type ~= 7 --[[ImGuiContextHookType.PendingRemoval_]])
local copy = ImGuiContextHook() -- [Luau] g.Hooks.push_back(*hook): store a copy
copy.Type = hook.Type; copy.Owner = hook.Owner or 0; copy.Callback = hook.Callback; copy.UserData = hook.UserData
g.Hooks:push_back(copy)
g.HookIdNext = g.HookIdNext + 1
g.Hooks:back().HookId = g.HookIdNext
return g.HookIdNext
end
-- Deferred removal, avoiding issue with changing vector while iterating it
--- @param ctx ImGuiContext
--- @param hook_id ImGuiID
function ImGui.RemoveContextHook(ctx, hook_id)
local g = ctx
assert(hook_id ~= 0)
for _, hook in g.Hooks:iter() do
if hook.HookId == hook_id then
hook.Type = 7 --[[ImGuiContextHookType.PendingRemoval_]]
end
end
end
-- Call context hooks (used by e.g. test engine)
-- We assume a small number of hooks so all stored in same array
--- @param ctx ImGuiContext
--- @param hook_type ImGuiContextHookType
function ImGui.CallContextHooks(ctx, hook_type)
local g = ctx
local hooks = g.Hooks
if hooks.Size == 0 then return end -- [Luau] fast path
for n = 1, hooks.Size do
local hook = hooks.Data[n]
if hook.Type == hook_type then
hook.Callback(g, hook)
end
end
end
-- A conformant backend should return nil on failure (e.g. clipboard data is not text).
--- @return string?
function ImGui.GetClipboardText()
local g = GImGui
return g.PlatformIO.Platform_GetClipboardTextFn and g.PlatformIO.Platform_GetClipboardTextFn(g) or nil
end
--- @param text string
function ImGui.SetClipboardText(text)
local g = GImGui
if g.PlatformIO.Platform_SetClipboardTextFn ~= nil then
g.PlatformIO.Platform_SetClipboardTextFn(g, text)
end
end
function ImGui.GetIO() return GImGui.IO end
--- @return ImGuiPlatformIO
function ImGui.GetPlatformIO()
assert(GImGui ~= nil, "No current context. Did you call ImGui.CreateContext() and ImGui.SetCurrentContext()?")
return GImGui.PlatformIO
end
-- Get desired mouse cursor shape.
-- Important: this is meant to be used by a platform backend, it is reset in ImGui.NewFrame(),
-- updated during the frame, and locked in EndFrame()/Render().
-- If you use software rendering by setting io.MouseDrawCursor then Dear ImGui will render those for you
function ImGui.GetMouseCursor()
local g = GImGui
return g.MouseCursor
end
-- We intentionally accept values of ImGuiMouseCursor that are outside our bounds, in case users needs to hack-in a custom cursor value.
-- Custom cursors may be handled by custom backends. If you are using a standard backend and want to hack in a custom cursor, you may
-- handle it before the backend _NewFrame() call and temporarily set ImGuiConfigFlags.NoMouseCursorChange during the backend _NewFrame() call.
--- @param cursor_type ImGuiMouseCursor
function ImGui.SetMouseCursor(cursor_type)
local g = GImGui
g.MouseCursor = cursor_type
end
-------------------------------------------------------------------------------
-- [SECTION] MISC HELPERS/UTILITIES (Color functions)
-- Note: The Convert functions are early design which are not consistent with other API.
-------------------------------------------------------------------------------
--- @param col_a ImU32
--- @param col_b ImU32
--- @return ImU32
function ImStd.ImAlphaBlendColors(col_a, col_b)
local t = bit32.band(bit32.rshift(col_b, 24 --[[IM_COL32_A_SHIFT]]), 0xFF) / 255.0
local r = ImLerp(bit32.band(bit32.rshift(col_a, 0 --[[IM_COL32_R_SHIFT]]), 0xFF), bit32.band(bit32.rshift(col_b, 0 --[[IM_COL32_R_SHIFT]]), 0xFF), t)
local g = ImLerp(bit32.band(bit32.rshift(col_a, 8 --[[IM_COL32_G_SHIFT]]), 0xFF), bit32.band(bit32.rshift(col_b, 8 --[[IM_COL32_G_SHIFT]]), 0xFF), t)
local b = ImLerp(bit32.band(bit32.rshift(col_a, 16 --[[IM_COL32_B_SHIFT]]), 0xFF), bit32.band(bit32.rshift(col_b, 16 --[[IM_COL32_B_SHIFT]]), 0xFF), t)
return IM_COL32(r, g, b, 0xFF)
end
--- @param in_col ImU32
--- @param out_col ImVec4?
--- @return ImVec4?
function ImGui.ColorConvertU32ToFloat4(in_col, out_col)
local s = 1.0 / 255.0
local r = bit32.band(bit32.rshift(in_col, 0 --[[IM_COL32_R_SHIFT]]), 0xFF) * s
local g = bit32.band(bit32.rshift(in_col, 8 --[[IM_COL32_G_SHIFT]]), 0xFF) * s
local b = bit32.band(bit32.rshift(in_col, 16 --[[IM_COL32_B_SHIFT]]), 0xFF) * s
local a = bit32.band(bit32.rshift(in_col, 24 --[[IM_COL32_A_SHIFT]]), 0xFF) * s
if out_col then
out_col[1] = r; out_col[2] = g; out_col[3] = b; out_col[4] = a
else
return ImVec4(r, g, b, a)
end
end
--- @param in_col ImVec4
--- @return ImU32
local function F32ToU8Sat(v) if v < 0.0 then v = 0.0 elseif v > 1.0 then v = 1.0 end return math.floor(v * 255.0 + 0.5) end
@native
function ImGui.ColorConvertFloat4ToU32(in_col)
return bit32.bor(F32ToU8Sat(in_col.x), bit32.lshift(F32ToU8Sat(in_col.y), 8), bit32.lshift(F32ToU8Sat(in_col.z), 16), bit32.lshift(F32ToU8Sat(in_col.w), 24))
end
-- Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
-- Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
--- @param r float
--- @param g float
--- @param b float
--- @return float, float, float
@native
function ImGui.ColorConvertRGBtoHSV(r, g, b)
local K = 0.0
if g < b then
g, b = b, g
K = -1.0
end
if r < g then
r, g = g, r
K = -2.0 / 6.0 - K
end
local chroma = r - (g < b and g or b)
local out_h = ImFabs(K + (g - b) / (6.0 * chroma + 1e-20))
local out_s = chroma / (r + 1e-20)
local out_v = r
return out_h, out_s, out_v
end
-- Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
-- also http://en.wikipedia.org/wiki/HSL_and_HSV
--- @param h float
--- @param s float
--- @param v float
--- @return float, float, float
@native
function ImGui.ColorConvertHSVtoRGB(h, s, v)
if s == 0.0 then
-- gray
return v, v, v
end
h = ImFmod(h, 1.0) / (60.0 / 360.0)
local i = math.floor(h)
local f = h - i
local p = v * (1.0 - s)
local q = v * (1.0 - s * f)
local t = v * (1.0 - s * (1.0 - f))
if i == 0 then
return v, t, p
elseif i == 1 then
return q, v, p
elseif i == 2 then
return p, v, t
elseif i == 3 then
return p, q, v
elseif i == 4 then
return t, p, v
else -- i == 5 or default
return v, p, q
end
end
-- Generic helper, equivalent to old ImGui.CalcListClipping() but stateless
--- @param clip_rect ImRect
--- @param pos ImVec2
--- @param items_height float
function ImGui.CalcClipRectVisibleItemsY(clip_rect, pos, items_height)
-- [Port] returns (out_visible_start, out_visible_end), 0-based like upstream
local out_visible_start = ImMax(ImTrunc((clip_rect.Min.y - pos.y) / items_height), 0)
local out_visible_end = ImMax(ImTrunc(ImCeil((clip_rect.Max.y - pos.y) / items_height)), out_visible_start)
return out_visible_start, out_visible_end
end
-------------------------------------------------------------------------------
-- [SECTION] STYLING
-------------------------------------------------------------------------------
function ImGui.GetStyle()
assert(GImGui ~= nil, "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?")
return GImGui.Style
end
do
local c = ImVec4()
--- @param col ImGuiCol|ImVec4|ImU32
--- @param alpha_mul? float
--- @param col_is_u32? bool # must be truthy if `col` is `ImU32`
--- @return ImU32
@native
function ImGui.GetColorU32(col, alpha_mul, col_is_u32)
if alpha_mul == nil then alpha_mul = 1.0 end
local style = GImGui.Style
if not col_is_u32 then
--- @cast col ImVec4|ImGuiCol
local w
if type(col) == "number" then
col = style.Colors[col]
w = col.w * style.Alpha * alpha_mul
else --- @cast col ImVec4
w = col.w * style.Alpha
end
-- [Luau] inline F32ToU8Sat() x4
local x, y, z = col.x, col.y, col.z
if x < 0.0 then x = 0.0 elseif x > 1.0 then x = 1.0 end
if y < 0.0 then y = 0.0 elseif y > 1.0 then y = 1.0 end
if z < 0.0 then z = 0.0 elseif z > 1.0 then z = 1.0 end
if w < 0.0 then w = 0.0 elseif w > 1.0 then w = 1.0 end
return bit32.bor(math.floor(x * 255.0 + 0.5), bit32.lshift(math.floor(y * 255.0 + 0.5), 8), bit32.lshift(math.floor(z * 255.0 + 0.5), 16), bit32.lshift(math.floor(w * 255.0 + 0.5), 24))
else
--- @cast col ImU32
alpha_mul = alpha_mul * style.Alpha
if alpha_mul >= 1.0 then
return col
end
local a = bit32.rshift(bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]), 24 --[[IM_COL32_A_SHIFT]])
a = math.floor(a * alpha_mul) -- We don't need to clamp 0..255 because alpha is in 0..1 range.
return bit32.bor(bit32.band(col, bit32.bnot(4278190080 --[[IM_COL32_A_MASK]])), bit32.lshift(a, 24 --[[IM_COL32_A_SHIFT]]))
end
end
end
-- FIXME: This may incur a round-trip (if the end user got their data from a float4) but eventually we aim to store the in-flight colors as ImU32
--- @param idx ImGuiCol
--- @param col ImU32|ImVec4
function ImGui.PushStyleColor(idx, col)
local g = GImGui
local backup = ImGuiColorMod(idx, g.Style.Colors[idx])
-- [Luau] inline g.ColorStack:push_back(backup)
local stack = g.ColorStack
local n = stack.Size + 1
if n > stack.Capacity then stack:push_back(backup) else stack.Data[n] = backup; stack.Size = n end
if g.DebugFlashStyleColorIdx ~= idx then
if type(col) == "number" then
ImGui.ColorConvertU32ToFloat4(col, g.Style.Colors[idx])
else --- @cast col ImVec4
ImVec4_Copy(g.Style.Colors[idx], col)
end
end
end
--- @param count? int
function ImGui.PopStyleColor(count)
if count == nil then count = 1 end
local g = GImGui
if g.ColorStack.Size < count then
IM_ASSERT_USER_ERROR(false, "Calling PopStyleColor() too many times!")
count = g.ColorStack.Size
end
local stack = g.ColorStack
while count > 0 do
local backup = stack.Data[stack.Size] -- [Luau] inline back()/pop_back()
ImVec4_Copy(g.Style.Colors[backup.Col], backup.BackupValue)
stack.Size = stack.Size - 1
count = count - 1
end
end
local GStyleVarsInfo = {
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "Alpha"), -- ImGuiStyleVar.Alpha
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "DisabledAlpha"), -- ImGuiStyleVar.DisabledAlpha
ImGuiStyleVarInfo(2, ImGuiDataType.Float, "WindowPadding"), -- ImGuiStyleVar.WindowPadding
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "WindowRounding"), -- ImGuiStyleVar.WindowRounding
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "WindowBorderSize"), -- ImGuiStyleVar.WindowBorderSize
ImGuiStyleVarInfo(2, ImGuiDataType.Float, "WindowMinSize"), -- ImGuiStyleVar.WindowMinSize
ImGuiStyleVarInfo(2, ImGuiDataType.Float, "WindowTitleAlign"), -- ImGuiStyleVar.WindowTitleAlign
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "ChildRounding"), -- ImGuiStyleVar.ChildRounding
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "ChildBorderSize"), -- ImGuiStyleVar.ChildBorderSize
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "PopupRounding"), -- ImGuiStyleVar.PopupRounding
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "PopupBorderSize"), -- ImGuiStyleVar.PopupBorderSize
ImGuiStyleVarInfo(2, ImGuiDataType.Float, "FramePadding"), -- ImGuiStyleVar.FramePadding
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "FrameRounding"), -- ImGuiStyleVar.FrameRounding
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "FrameBorderSize"), -- ImGuiStyleVar.FrameBorderSize
ImGuiStyleVarInfo(2, ImGuiDataType.Float, "ItemSpacing"), -- ImGuiStyleVar.ItemSpacing
ImGuiStyleVarInfo(2, ImGuiDataType.Float, "ItemInnerSpacing"), -- ImGuiStyleVar.ItemInnerSpacing
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "IndentSpacing"), -- ImGuiStyleVar.IndentSpacing
ImGuiStyleVarInfo(2, ImGuiDataType.Float, "CellPadding"), -- ImGuiStyleVar.CellPadding
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "ScrollbarSize"), -- ImGuiStyleVar.ScrollbarSize
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "ScrollbarRounding"), -- ImGuiStyleVar.ScrollbarRounding
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "ScrollbarPadding"), -- ImGuiStyleVar.ScrollbarPadding
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "GrabMinSize"), -- ImGuiStyleVar.GrabMinSize
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "GrabRounding"), -- ImGuiStyleVar.GrabRounding
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "ImageRounding"), -- ImGuiStyleVar.ImageRounding
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "ImageBorderSize"), -- ImGuiStyleVar.ImageBorderSize
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "TabRounding"), -- ImGuiStyleVar.TabRounding
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "TabBorderSize"), -- ImGuiStyleVar.TabBorderSize
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "TabMinWidthBase"), -- ImGuiStyleVar.TabMinWidthBase
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "TabMinWidthShrink"), -- ImGuiStyleVar.TabMinWidthShrink
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "TabBarBorderSize"), -- ImGuiStyleVar.TabBarBorderSize
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "TabBarOverlineSize"), -- ImGuiStyleVar.TabBarOverlineSize
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "TableAngledHeadersAngle"), -- ImGuiStyleVar.TableAngledHeadersAngle
ImGuiStyleVarInfo(2, ImGuiDataType.Float, "TableAngledHeadersTextAlign"), -- ImGuiStyleVar.TableAngledHeadersTextAlign
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "TreeLinesSize"), -- ImGuiStyleVar.TreeLinesSize
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "TreeLinesRounding"), -- ImGuiStyleVar.TreeLinesRounding
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "MenuItemRounding"), -- ImGuiStyleVar.MenuItemRounding
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "SelectableRounding"), -- ImGuiStyleVar.SelectableRounding
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "DragDropTargetRounding"), -- ImGuiStyleVar.DragDropTargetRounding
ImGuiStyleVarInfo(2, ImGuiDataType.Float, "ButtonTextAlign"), -- ImGuiStyleVar.ButtonTextAlign
ImGuiStyleVarInfo(2, ImGuiDataType.Float, "SelectableTextAlign"), -- ImGuiStyleVar.SelectableTextAlign
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "SeparatorSize"), -- ImGuiStyleVar.SeparatorSize
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "SeparatorTextBorderSize"), -- ImGuiStyleVar.SeparatorTextBorderSize
ImGuiStyleVarInfo(2, ImGuiDataType.Float, "SeparatorTextAlign"), -- ImGuiStyleVar.SeparatorTextAlign
ImGuiStyleVarInfo(2, ImGuiDataType.Float, "SeparatorTextPadding"), -- ImGuiStyleVar.SeparatorTextPadding
ImGuiStyleVarInfo(1, ImGuiDataType.Float, "DockingSeparatorSize"), -- ImGuiStyleVar.DockingSeparatorSize
}
assert(#GStyleVarsInfo == ImGuiStyleVar.COUNT) -- IM_STATIC_ASSERT(IM_COUNTOF(GStyleVarsInfo) == ImGuiStyleVar_COUNT)
--- @param idx ImGuiStyleVar
--- @return ImGuiStyleVarInfo
function ImGui.GetStyleVarInfo(idx)
assert(idx >= 0 and idx < 45 --[[ImGuiStyleVar.COUNT]])
return GStyleVarsInfo[idx + 1]
end
--- @param idx ImGuiStyleVar
--- @param val float|ImVec2
function ImGui.PushStyleVar(idx, val)
local g = GImGui
local var_info = ImGui.GetStyleVarInfo(idx)
if type(val) == "number" then
IM_ASSERT_USER_ERROR_RET(var_info.DataType == 6 --[[ImGuiDataType.Float]] and var_info.Count == 1, "Calling PushStyleVar() with wrong type!")
else -- ImVec2
IM_ASSERT_USER_ERROR_RET(var_info.DataType == 6 --[[ImGuiDataType.Float]] and var_info.Count == 2, "Calling PushStyleVar() with wrong type!")
end
local var = g.Style[var_info.Key]
g.StyleVarStack:push_back(ImGuiStyleMod(idx, var))
if type(val) == "number" then
g.Style[var_info.Key] = val
else -- copy into the style's own ImVec2: storing the caller's vector would alias it into the style forever
var.x = val.x; var.y = val.y
end
end
--- @param idx ImGuiStyleVar
--- @param val_x float
function ImGui.PushStyleVarX(idx, val_x)
local g = GImGui
local var_info = ImGui.GetStyleVarInfo(idx)
IM_ASSERT_USER_ERROR_RET(var_info.DataType == 6 --[[ImGuiDataType.Float]] and var_info.Count == 2, "Calling PushStyleVarX() with wrong type!")
local pvar = g.Style[var_info.Key]
g.StyleVarStack:push_back(ImGuiStyleMod(idx, pvar))
pvar.x = val_x
end
--- @param idx ImGuiStyleVar
--- @param val_y float
function ImGui.PushStyleVarY(idx, val_y)
local g = GImGui
local var_info = ImGui.GetStyleVarInfo(idx)
IM_ASSERT_USER_ERROR_RET(var_info.DataType == 6 --[[ImGuiDataType.Float]] and var_info.Count == 2, "Calling PushStyleVarY() with wrong type!")
local pvar = g.Style[var_info.Key]
g.StyleVarStack:push_back(ImGuiStyleMod(idx, pvar))
pvar.y = val_y
end
--- @param count? int
function ImGui.PopStyleVar(count)
if count == nil then count = 1 end
local g = GImGui
if g.StyleVarStack.Size < count then
IM_ASSERT_USER_ERROR(0, "Calling PopStyleVar() too many times!")
count = g.StyleVarStack.Size
end
local stack = g.StyleVarStack
while count > 0 do
local backup = stack.Data[stack.Size] -- [Luau] inline back()/pop_back()
local var_info = ImGui.GetStyleVarInfo(backup.VarIdx)
local data = g.Style[var_info.Key]
if (var_info.DataType == 6 --[[ImGuiDataType.Float]] and var_info.Count == 1) then
g.Style[var_info.Key] = backup.BackupVal[1]
elseif (var_info.DataType == 6 --[[ImGuiDataType.Float]] and var_info.Count == 2) then
data.x = backup.BackupVal[1]; data.y = backup.BackupVal[2]
end
stack.Size = stack.Size - 1
count = count - 1
end
end
-- ATTENTION THIS IS IN LEGACY LOCAL SPACE.
--- @param wrap_local_pos_x float
function ImGui.PushTextWrapPos(wrap_local_pos_x)
local g = GImGui
local window = g.CurrentWindow
-- [Luau] inline window.DC.TextWrapPosStack:push_back(window.DC.TextWrapPos)
local dc = window.DC
local stack = dc.TextWrapPosStack
local n = stack.Size + 1
if n > stack.Capacity then stack:push_back(dc.TextWrapPos) else stack.Data[n] = dc.TextWrapPos; stack.Size = n end
dc.TextWrapPos = wrap_local_pos_x
end
function ImGui.PopTextWrapPos()
local g = GImGui
local window = g.CurrentWindow
local stack = window.DC.TextWrapPosStack
IM_ASSERT_USER_ERROR_RET(stack.Size > 0, "Calling PopTextWrapPos() too many times!")
window.DC.TextWrapPos = stack.Data[stack.Size] -- [Luau] inline back()/pop_back()
stack.Size = stack.Size - 1
end
--- @param window ImGuiWindow
--- @param popup_hierarchy bool
--- @param dock_hierarchy bool
local function GetCombinedRootWindow(window, popup_hierarchy, dock_hierarchy)
local last_window = nil
while last_window ~= window do
last_window = window
window = window.RootWindow
if popup_hierarchy then
window = window.RootWindowPopupTree
end
if dock_hierarchy then
window = window.RootWindowDockTree
end
end
return window
end
--- @param window ImGuiWindow
--- @param potential_parent ImGuiWindow
--- @param popup_hierarchy bool
--- @param dock_hierarchy bool
function ImGui.IsWindowChildOf(window, potential_parent, popup_hierarchy, dock_hierarchy)
local window_root = GetCombinedRootWindow(window, popup_hierarchy, dock_hierarchy)
if window_root == potential_parent then
return true
end
while window ~= nil do
if window == potential_parent then
return true
end
if window == window_root then -- end of chain
return false
end
window = window.ParentWindow
end
return false
end
--- @param window ImGuiWindow
function ImGui.IsWindowInBeginStack(window)
local g = GImGui
for n = g.CurrentWindowStack.Size, 1, -1 do
if g.CurrentWindowStack.Data[n].Window == window then
return true
end
end
return false
end
--- @param window ImGuiWindow
--- @param potential_parent ImGuiWindow
function ImGui.IsWindowWithinBeginStackOf(window, potential_parent)
if window.RootWindow == potential_parent then
return true
end
while window ~= nil do
if window == potential_parent then
return true
end
window = window.ParentWindowInBeginStack
end
return false
end
--- @param potential_above ImGuiWindow
--- @param potential_below ImGuiWindow
--- @return bool
function ImGui.IsWindowAbove(potential_above, potential_below)
local g = GImGui
-- It would be saner to ensure that display layer is always reflected in the g.Windows[] order, which would likely requires altering all manipulations of that array
local display_layer_delta = GetWindowDisplayLayer(potential_above) - GetWindowDisplayLayer(potential_below)
if display_layer_delta ~= 0 then
return display_layer_delta > 0
end
for i = g.Windows.Size, 1, -1 do
local candidate_window = g.Windows.Data[i]
if candidate_window == potential_above then
return true
end
if candidate_window == potential_below then
return false
end
end
return false
end
-- Is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags for options.
-- IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,
-- you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!
-- Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
--- @param flags? ImGuiHoveredFlags
function ImGui.IsWindowHovered(flags)
if flags == nil then flags = 0 end
local g = GImGui
IM_ASSERT_USER_ERROR(bit32.band(flags, bit32.bnot(12479 --[[ImGuiHoveredFlags.AllowedMaskForIsWindowHovered]])) == 0, "Invalid flags for IsWindowHovered()!")
local ref_window = g.HoveredWindow
local cur_window = g.CurrentWindow
if ref_window == nil then
return false
end
if bit32.band(flags, 4 --[[ImGuiHoveredFlags.AnyWindow]]) == 0 then
assert(cur_window) -- Not inside a Begin()/End()
local popup_hierarchy = bit32.band(flags, 8 --[[ImGuiHoveredFlags.NoPopupHierarchy]]) == 0
local dock_hierarchy = bit32.band(flags, 16 --[[ImGuiHoveredFlags.DockHierarchy]]) ~= 0
if bit32.band(flags, 2 --[[ImGuiHoveredFlags.RootWindow]]) ~= 0 then
cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy)
end
local result
if bit32.band(flags, 1 --[[ImGuiHoveredFlags.ChildWindows]]) ~= 0 then
result = ImGui.IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy)
else
result = (ref_window == cur_window)
end
if not result then
return false
end
end
if not ImGui.IsWindowContentHoverable(ref_window, flags) then
return false
end
if bit32.band(flags, 128 --[[ImGuiHoveredFlags.AllowWhenBlockedByActiveItem]]) == 0 then
if g.ActiveId ~= 0 and not g.ActiveIdAllowOverlap and g.ActiveId ~= ref_window.MoveId then
return false
end
end
-- When changing hovered window we requires a bit of stationary delay before activating hover timer.
-- FIXME: We don't support delay other than stationary one for now, other delay would need a way
-- to fulfill the possibility that multiple IsWindowHovered() with varying flag could return true
-- for different windows of the hierarchy. Possibly need a Hash(Current+Flags) ==> (Timer) cache.
-- We can implement this for _Stationary because the data is linked to HoveredWindow rather than CurrentWindow.
if bit32.band(flags, 4096 --[[ImGuiHoveredFlags.ForTooltip]]) ~= 0 then
flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse)
end
if bit32.band(flags, 8192 --[[ImGuiHoveredFlags.Stationary]]) ~= 0 and g.HoverWindowUnlockedStationaryId ~= ref_window.ID then
return false
end
return true
end
-- Similar to IsWindowHovered()
--- @param flags? ImGuiFocusedFlags
function ImGui.IsWindowFocused(flags)
if flags == nil then flags = 0 end
local g = GImGui
local ref_window = g.NavWindow
local cur_window = g.CurrentWindow
if ref_window == nil then
return false
end
if bit32.band(flags, 4 --[[ImGuiFocusedFlags.AnyWindow]]) ~= 0 then
return true
end
assert(cur_window) -- Not inside a Begin()/End()
local popup_hierarchy = bit32.band(flags, 8 --[[ImGuiFocusedFlags.NoPopupHierarchy]]) == 0
local dock_hierarchy = bit32.band(flags, 16 --[[ImGuiFocusedFlags.DockHierarchy]]) ~= 0
if bit32.band(flags, 2 --[[ImGuiFocusedFlags.RootWindow]]) ~= 0 then
cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy)
end
if bit32.band(flags, 1 --[[ImGuiFocusedFlags.ChildWindows]]) ~= 0 then
return ImGui.IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy)
else
return ref_window == cur_window
end
end
--- @param window ImGuiWindow
--- @param scale float
local function ScaleWindow(window, scale)
local origin = window.Viewport.Pos
ImVec2_Copy(window.Pos, ImFloor((window.Pos - origin) * scale + origin))
ImVec2_Copy(window.Size, ImTrunc(window.Size * scale))
ImVec2_Copy(window.SizeFull, ImTrunc(window.SizeFull * scale))
ImVec2_Copy(window.ContentSize, ImTrunc(window.ContentSize * scale))
end
-- Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!)
--- @param viewport ImGuiViewportP
--- @param scale float
function ImGui.ScaleWindowsInViewport(viewport, scale)
local g = GImGui
-- IMGUI_DEBUG_LOG_VIEWPORT("[viewport] ScaleWindowsInViewport 0x%08X\n", viewport.ID)
if viewport.Window then
ScaleWindow(viewport.Window, scale)
else
for _, window in g.Windows:iter() do
if window.Viewport == viewport then
ScaleWindow(window, scale)
end
end
end
end
-------------------------------------------------------------------------------
-- [SECTION] SCROLLING
-------------------------------------------------------------------------------
-- Helper to snap on edges when aiming at an item very close to the edge,
-- So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling.
-- When we refactor the scrolling API this may be configurable with a flag?
-- Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default.
--- @param target float
--- @param snap_min float
--- @param snap_max float
--- @param snap_threshold float
--- @param center_ratio float
local function CalcScrollEdgeSnap(target, snap_min, snap_max, snap_threshold, center_ratio)
if target <= snap_min + snap_threshold then
return ImLerp(snap_min, target, center_ratio)
end
if target >= snap_max - snap_threshold then
return ImLerp(target, snap_max, center_ratio)
end
return target
end
--- @param window ImGuiWindow
function CalcNextScrollFromScrollTargetAndClamp(window)
local scroll = ImVec2()
ImVec2_Copy(scroll, window.Scroll)
local decoration_size = ImVec2(window.DecoOuterSizeX1 + window.DecoInnerSizeX1 + window.DecoOuterSizeX2, window.DecoOuterSizeY1 + window.DecoInnerSizeY1 + window.DecoOuterSizeY2)
for axis_n = 1 --[[ImGuiAxis.X]], 2 --[[ImGuiAxis.Y]] do
local axis = (axis_n == 1 --[[ImGuiAxis.X]]) and "x" or "y" -- [Luau] named key: ImVec2[1]/[2] go through IM_VEC2.__index
if window.ScrollTarget[axis] < 3.4028234663853e+38 --[[FLT_MAX]] then
local center_ratio = window.ScrollTargetCenterRatio[axis]
local scroll_target = window.ScrollTarget[axis]
if window.ScrollTargetEdgeSnapDist[axis] > 0.0 then
local snap_min = 0.0
local snap_max = window.ScrollMax[axis] + window.SizeFull[axis] - decoration_size[axis]
scroll_target = CalcScrollEdgeSnap(scroll_target, snap_min, snap_max, window.ScrollTargetEdgeSnapDist[axis], center_ratio)
end
scroll[axis] = scroll_target - center_ratio * (window.SizeFull[axis] - decoration_size[axis])
end
scroll[axis] = ImRound64(ImMax(scroll[axis], 0.0))
if not window.Collapsed and not window.SkipItems then
scroll[axis] = ImMin(scroll[axis], window.ScrollMax[axis])
end
end
return scroll
end
-- Scroll to keep newly navigated item fully into view
--- @param window ImGuiWindow
--- @param item_rect ImRect
--- @param flags? ImGuiScrollFlags
function ImGui.ScrollToRectEx(window, item_rect, flags)
if flags == nil then flags = 0 end
local g = GImGui
local scroll_rect = ImRect(window.InnerRect.Min - ImVec2(1, 1), window.InnerRect.Max + ImVec2(1, 1))
scroll_rect.Min.x = ImMin(scroll_rect.Min.x + window.DecoInnerSizeX1, scroll_rect.Max.x)
scroll_rect.Min.y = ImMin(scroll_rect.Min.y + window.DecoInnerSizeY1, scroll_rect.Max.y)
-- GetForegroundDrawList(window):AddRect(item_rect.Min, item_rect.Max, IM_COL32(255,0,0,255), 0.0, 0, 5.0) -- [DEBUG]
-- GetForegroundDrawList(window):AddRect(scroll_rect.Min, scroll_rect.Max, IM_COL32_WHITE) -- [DEBUG]
-- Check that only one behavior is selected per axis
assert(bit32.band(flags, 21 --[[ImGuiScrollFlags.MaskX_]]) == 0 or ImIsPowerOfTwo(bit32.band(flags, 21 --[[ImGuiScrollFlags.MaskX_]])))
assert(bit32.band(flags, 42 --[[ImGuiScrollFlags.MaskY_]]) == 0 or ImIsPowerOfTwo(bit32.band(flags, 42 --[[ImGuiScrollFlags.MaskY_]])))
-- Defaults
local in_flags = flags
if bit32.band(flags, 21 --[[ImGuiScrollFlags.MaskX_]]) == 0 and window.ScrollbarX then
flags = bit32.bor(flags, 1 --[[ImGuiScrollFlags.KeepVisibleEdgeX]])
end
if bit32.band(flags, 42 --[[ImGuiScrollFlags.MaskY_]]) == 0 then
flags = bit32.bor(flags, window.Appearing and 32 --[[ImGuiScrollFlags.AlwaysCenterY]] or 2 --[[ImGuiScrollFlags.KeepVisibleEdgeY]])
end
local fully_visible_x = item_rect.Min.x >= scroll_rect.Min.x and item_rect.Max.x <= scroll_rect.Max.x
local fully_visible_y = item_rect.Min.y >= scroll_rect.Min.y and item_rect.Max.y <= scroll_rect.Max.y
local can_be_fully_visible_x = (item_rect:GetWidth() + g.Style.ItemSpacing.x * 2.0) <= scroll_rect:GetWidth() or (window.AutoFitFramesX > 0) or bit32.band(window.Flags, 64 --[[ImGuiWindowFlags.AlwaysAutoResize]]) ~= 0
local can_be_fully_visible_y = (item_rect:GetHeight() + g.Style.ItemSpacing.y * 2.0) <= scroll_rect:GetHeight() or (window.AutoFitFramesY > 0) or bit32.band(window.Flags, 64 --[[ImGuiWindowFlags.AlwaysAutoResize]]) ~= 0
if bit32.band(flags, 1 --[[ImGuiScrollFlags.KeepVisibleEdgeX]]) ~= 0 and not fully_visible_x then
if item_rect.Min.x < scroll_rect.Min.x or not can_be_fully_visible_x then
ImGui.SetScrollFromPosX(window, item_rect.Min.x - g.Style.ItemSpacing.x - window.Pos.x, 0.0)
elseif item_rect.Max.x >= scroll_rect.Max.x then
ImGui.SetScrollFromPosX(window, item_rect.Max.x + g.Style.ItemSpacing.x - window.Pos.x, 1.0)
end
elseif (bit32.band(flags, 4 --[[ImGuiScrollFlags.KeepVisibleCenterX]]) ~= 0 and not fully_visible_x) or bit32.band(flags, 16 --[[ImGuiScrollFlags.AlwaysCenterX]]) ~= 0 then
if can_be_fully_visible_x then
ImGui.SetScrollFromPosX(window, ImTrunc((item_rect.Min.x + item_rect.Max.x) * 0.5) - window.Pos.x, 0.5)
else
ImGui.SetScrollFromPosX(window, item_rect.Min.x - window.Pos.x, 0.0)
end
end
if bit32.band(flags, 2 --[[ImGuiScrollFlags.KeepVisibleEdgeY]]) ~= 0 and not fully_visible_y then
if item_rect.Min.y < scroll_rect.Min.y or not can_be_fully_visible_y then
ImGui.SetScrollFromPosY(window, item_rect.Min.y - g.Style.ItemSpacing.y - window.Pos.y, 0.0)
elseif item_rect.Max.y >= scroll_rect.Max.y then
ImGui.SetScrollFromPosY(window, item_rect.Max.y + g.Style.ItemSpacing.y - window.Pos.y, 1.0)
end
elseif (bit32.band(flags, 8 --[[ImGuiScrollFlags.KeepVisibleCenterY]]) ~= 0 and not fully_visible_y) or bit32.band(flags, 32 --[[ImGuiScrollFlags.AlwaysCenterY]]) ~= 0 then
if can_be_fully_visible_y then
ImGui.SetScrollFromPosY(window, ImTrunc((item_rect.Min.y + item_rect.Max.y) * 0.5) - window.Pos.y, 0.5)
else
ImGui.SetScrollFromPosY(window, item_rect.Min.y - window.Pos.y, 0.0)
end
end
local next_scroll = CalcNextScrollFromScrollTargetAndClamp(window)
local delta_scroll = next_scroll - window.Scroll
-- Also scroll parent window to keep us into view if necessary
if bit32.band(flags, 64 --[[ImGuiScrollFlags.NoScrollParent]]) == 0 and bit32.band(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0 then
-- FIXME-SCROLL: May be an option?
if bit32.band(in_flags, bit32.bor(16 --[[ImGuiScrollFlags.AlwaysCenterX]], 4 --[[ImGuiScrollFlags.KeepVisibleCenterX]])) ~= 0 then
in_flags = bit32.bor(bit32.band(in_flags, bit32.bnot(21 --[[ImGuiScrollFlags.MaskX_]])), 1 --[[ImGuiScrollFlags.KeepVisibleEdgeX]])
end
if bit32.band(in_flags, bit32.bor(32 --[[ImGuiScrollFlags.AlwaysCenterY]], 8 --[[ImGuiScrollFlags.KeepVisibleCenterY]])) ~= 0 then
in_flags = bit32.bor(bit32.band(in_flags, bit32.bnot(42 --[[ImGuiScrollFlags.MaskY_]])), 2 --[[ImGuiScrollFlags.KeepVisibleEdgeY]])
end
local parent_delta = ImGui.ScrollToRectEx(window.ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll), in_flags)
delta_scroll = delta_scroll + parent_delta
end
return delta_scroll
end
--- @return float
function ImGui.GetScrollMaxY()
local window = GImGui.CurrentWindow
return window.ScrollMax.y
end
function ImGui.GetScrollMaxX()
return GImGui.CurrentWindow.ScrollMax.x
end
function ImGui.GetScrollX()
return GImGui.CurrentWindow.Scroll.x
end
function ImGui.GetScrollY()
return GImGui.CurrentWindow.Scroll.y
end
--- @param flags? ImGuiScrollFlags
function ImGui.ScrollToItem(flags)
local g = GImGui
ImGui.ScrollToRectEx(g.CurrentWindow, g.LastItemData.NavRect, flags or 0)
end
--- @param window ImGuiWindow
--- @param item_rect ImRect
--- @param flags? ImGuiScrollFlags
function ImGui.ScrollToRect(window, item_rect, flags)
ImGui.ScrollToRectEx(window, item_rect, flags or 0)
end
-- center_x_ratio: 0.0 left of last item, 0.5 horizontal center of last item, 1.0 right of last item.
--- @param center_x_ratio? float
function ImGui.SetScrollHereX(center_x_ratio)
if center_x_ratio == nil then center_x_ratio = 0.5 end
local g = GImGui
local window = g.CurrentWindow
local spacing_x = ImMax(window.WindowPadding.x, g.Style.ItemSpacing.x)
local target_pos_x = ImLerp(g.LastItemData.Rect.Min.x - spacing_x, g.LastItemData.Rect.Max.x + spacing_x, center_x_ratio)
ImGui.SetScrollFromPosX(window, target_pos_x - window.Pos.x, center_x_ratio) -- Convert from absolute to local pos
-- Tweak: snap on edges when aiming at an item very close to the edge
window.ScrollTargetEdgeSnapDist.x = ImMax(0.0, window.WindowPadding.x - spacing_x)
end
-- center_y_ratio: 0.0 top of last item, 0.5 vertical center of last item, 1.0 bottom of last item.
--- @param center_y_ratio? float
function ImGui.SetScrollHereY(center_y_ratio)
if center_y_ratio == nil then center_y_ratio = 0.5 end
local g = GImGui
local window = g.CurrentWindow
local spacing_y = ImMax(window.WindowPadding.y, g.Style.ItemSpacing.y)
local target_pos_y = ImLerp(window.DC.CursorPosPrevLine.y - spacing_y, window.DC.CursorPosPrevLine.y + window.DC.PrevLineSize.y + spacing_y, center_y_ratio)
ImGui.SetScrollFromPosY(window, target_pos_y - window.Pos.y, center_y_ratio) -- Convert from absolute to local pos
-- Tweak: snap on edges when aiming at an item very close to the edge
window.ScrollTargetEdgeSnapDist.y = ImMax(0.0, window.WindowPadding.y - spacing_y)
end
-- Overloads: SetScrollX(scroll_x) / SetScrollX(window, scroll_x)
--- @param window ImGuiWindow|float
--- @param scroll_x float?
function ImGui.SetScrollX(window, scroll_x)
if type(window) == "number" then window, scroll_x = GImGui.CurrentWindow, window end
window.ScrollTarget.x = scroll_x
window.ScrollTargetCenterRatio.x = 0.0
window.ScrollTargetEdgeSnapDist.x = 0.0
end
--- @param window ImGuiWindow
--- @param scroll_y float
function ImGui.SetScrollY(window, scroll_y)
if type(window) == "number" then window, scroll_y = GImGui.CurrentWindow, window end
window.ScrollTarget.y = scroll_y
window.ScrollTargetCenterRatio.y = 0.0
window.ScrollTargetEdgeSnapDist.y = 0.0
end
-- Note that a local position will vary depending on initial scroll value,
-- This is a little bit confusing so bear with us:
-- - local_pos = (absolution_pos - window.Pos)
-- - So local_x/local_y are 0.0 for a position at the upper-left corner of a window,
-- and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area.
-- - They mostly exist because of legacy API.
-- Following the rules above, when trying to work with scrolling code, consider that:
-- - SetScrollFromPosY(0.0) == SetScrollY(0.0 + scroll.y) == has no effect!
-- - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0) == reset scroll. Of course writing SetScrollY(0.0) directly then makes more sense
-- We store a target position so centering and clamping can occur on the next frame when we are guaranteed to have a known window size
--- @param window ImGuiWindow
--- @param local_x float
--- @param center_x_ratio float
function ImGui.SetScrollFromPosX(window, local_x, center_x_ratio)
if type(window) == "number" then window, local_x, center_x_ratio = GImGui.CurrentWindow, window, local_x end
if center_x_ratio == nil then center_x_ratio = 0.5 end
assert(center_x_ratio >= 0.0 and center_x_ratio <= 1.0)
window.ScrollTarget.x = IM_TRUNC(local_x - window.DecoOuterSizeX1 - window.DecoInnerSizeX1 + window.Scroll.x) -- Convert local position to scroll offset
window.ScrollTargetCenterRatio.x = center_x_ratio
window.ScrollTargetEdgeSnapDist.x = 0.0
end
--- @param window ImGuiWindow
--- @param local_y float
--- @param center_y_ratio float
function ImGui.SetScrollFromPosY(window, local_y, center_y_ratio)
if type(window) == "number" then window, local_y, center_y_ratio = GImGui.CurrentWindow, window, local_y end
if center_y_ratio == nil then center_y_ratio = 0.5 end
assert(center_y_ratio >= 0.0 and center_y_ratio <= 1.0)
window.ScrollTarget.y = IM_TRUNC(local_y - window.DecoOuterSizeY1 - window.DecoInnerSizeY1 + window.Scroll.y) -- Convert local position to scroll offset
window.ScrollTargetCenterRatio.y = center_y_ratio
window.ScrollTargetEdgeSnapDist.y = 0.0
end
-------------------------------------------------------------------------------
-- [SECTION] TOOLTIPS
-------------------------------------------------------------------------------
--- @param tooltip_flags ImGuiTooltipFlags
--- @param extra_window_flags ImGuiWindowFlags
function ImGui.BeginTooltipEx(tooltip_flags, extra_window_flags)
local g = GImGui
local is_dragdrop_tooltip = g.DragDropWithinSource or g.DragDropWithinTarget
if is_dragdrop_tooltip then
-- Drag and Drop tooltips are positioning differently than other tooltips:
-- - offset visibility to increase visibility around mouse.
-- - never clamp within outer viewport boundary.
-- We call SetNextWindowPos() to enforce position and disable clamping.
-- See FindBestWindowPosForPopup() for positioning logic of other tooltips (not drag and drop ones).
-- local tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding
local is_touchscreen = (g.IO.MouseSource == 1 --[[ImGuiMouseSource.TouchScreen]])
if bit32.band(g.NextWindowData.HasFlags, 1 --[[ImGuiNextWindowDataFlags.HasPos]]) == 0 then
local tooltip_pos
if is_touchscreen then
tooltip_pos = g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_TOUCH * g.Style.MouseCursorScale
else
tooltip_pos = g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_MOUSE * g.Style.MouseCursorScale
end
local tooltip_pivot = is_touchscreen and TOOLTIP_DEFAULT_PIVOT_TOUCH or ImVec2(0.0, 0.0)
ImGui.SetNextWindowPos(tooltip_pos, 0 --[[ImGuiCond.None]], tooltip_pivot)
end
local bg_alpha = g.Style.Colors[4 --[[ImGuiCol.PopupBg]]].w * 0.60
ImGui.SetNextWindowBgAlpha(bg_alpha)
-- PushStyleVar(ImGuiStyleVar.Alpha, g.Style.Alpha * 0.60) -- This would be nice but e.g ColorButton with checkerboard has issue with transparent colors :(
tooltip_flags = bit32.bor(tooltip_flags, 2 --[[ImGuiTooltipFlags.OverridePrevious]])
end
-- Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
if (bit32.band(tooltip_flags, 2 --[[ImGuiTooltipFlags.OverridePrevious]]) ~= 0) and g.TooltipPreviousWindow ~= nil and g.TooltipPreviousWindow.Active and not ImGui.IsWindowInBeginStack(g.TooltipPreviousWindow) then
-- IMGUI_DEBUG_LOG("[tooltip] '%s' already active, using +1 for this frame\n", window_name)
ImGui.SetWindowHiddenAndSkipItemsForCurrentFrame(g.TooltipPreviousWindow)
g.TooltipOverrideCount = g.TooltipOverrideCount + 1
end
local window_name_template = is_dragdrop_tooltip and "##Tooltip_DragDrop_%02d" or "##Tooltip_%02d"
local window_name = ImFormatString(window_name_template, g.TooltipOverrideCount)
local flags = bit32.bor(33554432 --[[ImGuiWindowFlags.Tooltip]], 197120 --[[ImGuiWindowFlags.NoInputs]], 1 --[[ImGuiWindowFlags.NoTitleBar]], 4 --[[ImGuiWindowFlags.NoMove]], 2 --[[ImGuiWindowFlags.NoResize]], 256 --[[ImGuiWindowFlags.NoSavedSettings]], 64 --[[ImGuiWindowFlags.AlwaysAutoResize]])
ImGui.Begin(window_name, nil, bit32.bor(flags, extra_window_flags))
-- 2023-03-09: Added bool return value to the API, but currently always returning true.
-- If this ever returns false we need to update BeginDragDropSource() accordingly.
-- if (!ret)
-- End();
-- return ret;
return true
end
function ImGui.BeginTooltip()
return ImGui.BeginTooltipEx(0 --[[ImGuiTooltipFlags.None]], 0 --[[ImGuiWindowFlags.None]])
end
function ImGui.BeginItemTooltip()
if not ImGui.IsItemHovered(4096 --[[ImGuiHoveredFlags.ForTooltip]]) then
return false
end
return ImGui.BeginTooltipEx(0 --[[ImGuiTooltipFlags.None]], 0 --[[ImGuiWindowFlags.None]])
end
function ImGui.EndTooltip()
assert(bit32.band(ImGui.GetCurrentWindowRead().Flags, 33554432 --[[ImGuiWindowFlags.Tooltip]]) ~= 0) -- Mismatched BeginTooltip()/EndTooltip() calls
ImGui.End()
end
--- @param fmt string
--- @param ... any
function ImGui.SetTooltip(fmt, ...)
ImGui.SetTooltipV(fmt, ...)
end
-- [Luau] va_list -> varargs (same convention as TextV())
function ImGui.SetTooltipV(fmt, ...)
if not ImGui.BeginTooltipEx(2 --[[ImGuiTooltipFlags.OverridePrevious]], 0 --[[ImGuiWindowFlags.None]]) then
return
end
ImGui.TextV(fmt, ...)
ImGui.EndTooltip()
end
-- Shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav'.
-- Defaults to == ImGuiHoveredFlags.Stationary | ImGuiHoveredFlags.DelayShort when using the mouse.
--- @param fmt string
--- @param ... any
function ImGui.SetItemTooltip(fmt, ...)
ImGui.SetItemTooltipV(fmt, ...)
end
function ImGui.SetItemTooltipV(fmt, ...)
if ImGui.IsItemHovered(4096 --[[ImGuiHoveredFlags.ForTooltip]]) then
ImGui.SetTooltipV(fmt, ...)
end
end
-------------------------------------------------------------------------------
-- [SECTION] POPUPS
-------------------------------------------------------------------------------
-- When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags.NoFocusOnAppearing)
-- should be positioned behind that modal window, unless the window was created inside the modal begin-stack.
-- In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent.
-- - WindowA -- FindBlockingModal() returns Modal1
-- - WindowB -- .. returns Modal1
-- - Modal1 -- .. returns Modal2
-- - WindowC -- .. returns Modal2
-- - WindowD -- .. returns Modal2
-- - Modal2 -- .. returns Modal2
-- - WindowE -- .. returns nil
-- Notes:
-- - FindBlockingModal(nil) == nil is generally equivalent to GetTopMostPopupModal() == nil.
-- Only difference is here we check for .Active/WasActive but it may be unnecessary.
--- @param window? ImGuiWindow
function ImGui.FindBlockingModal(window)
local g = GImGui
if g.OpenPopupStack.Size <= 0 then
return nil
end
-- Find a modal that has common parent with specified window. Specified window should be positioned behind that modal.
for i = 1, g.OpenPopupStack.Size do
repeat
local popup_data = g.OpenPopupStack.Data[i]
local popup_window = popup_data.Window
if popup_window == nil or bit32.band(popup_window.Flags, 134217728 --[[ImGuiWindowFlags.Modal]]) == 0 then
break
end
if not popup_window.Active and not popup_window.WasActive then -- Check WasActive, because this code may run before popup renders on current frame, also check Active to handle newly created windows.
break
end
if window == nil then -- FindBlockingModal(nil) test for if FocusWindow(nil) is naturally possible via a mouse click.
return popup_window
end
if ImGui.IsWindowWithinBeginStackOf(window, popup_window) then -- Window may be over modal
break
end
do return popup_window end -- Place window right below first block modal
until true
end
return nil
end
--- @param id ImGuiID|string
--- @param popup_flags? ImGuiPopupFlags
function ImGui.OpenPopup(id, popup_flags)
if popup_flags == nil then popup_flags = 0 end
if type(id) == "number" then
return ImGui.OpenPopupEx(id, popup_flags)
else --- @cast id string
local g = GImGui
local _id = g.CurrentWindow:GetID(id)
IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopup(\"%s\" -> 0x%08X)", id, _id)
return ImGui.OpenPopupEx(_id, popup_flags)
end
end
-- Mark popup as open (toggle toward open state).
-- - Return true when the popup is toggled open, which allows you to capture local state if needed.
-- You may also call IsWindowAppearing() inside the later BeginPopup() scope if you need to prepare/compute data for the popup.
-- - Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
-- - Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
-- - One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to nil).
--- @param id ImGuiID
--- @param popup_flags ImGuiPopupFlags
function ImGui.OpenPopupEx(id, popup_flags)
local g = GImGui
local parent_window = g.CurrentWindow
local current_stack_size = g.BeginPopupStack.Size
if bit32.band(popup_flags, 128 --[[ImGuiPopupFlags.NoOpenOverExistingPopup]]) ~= 0 then
if ImGui.IsPopupOpen(0, 1024 --[[ImGuiPopupFlags.AnyPopupId]]) then
return false
end
end
local popup_ref = ImGuiPopupData() -- Tagged as new ref as Window will be set back to nil if we write this into OpenPopupStack.
popup_ref.PopupId = id
popup_ref.Window = nil
popup_ref.RestoreNavWindow = g.NavWindow -- When popup closes focus may be restored to NavWindow (depend on window type).
popup_ref.OpenFrameCount = g.FrameCount
popup_ref.OpenParentId = parent_window.IDStack:back()
ImVec2_Copy(popup_ref.OpenPopupPos, ImGui.NavCalcPreferredRefPos(67108864 --[[ImGuiWindowFlags.Popup]]))
if ImGui.IsMousePosValid(g.IO.MousePos) then
ImVec2_Copy(popup_ref.OpenMousePos, g.IO.MousePos)
else
ImVec2_Copy(popup_ref.OpenMousePos, popup_ref.OpenPopupPos)
end
-- IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopupEx(0x%08X)\n", id)
if g.OpenPopupStack.Size < current_stack_size + 1 then
g.OpenPopupStack:push_back(popup_ref)
return true
else
-- Gently handle the user mistakenly calling OpenPopup() every frames: it is likely a programming mistake!
-- However, if we were to run the regular code path, the ui would become completely unusable because the popup will always be
-- in hidden-while-calculating-size state _while_ claiming focus. Which is extremely confusing situation for the programmer.
-- Instead, for successive frames calls to OpenPopup(), we silently avoid reopening even if ImGuiPopupFlags.NoReopen is not specified.
local keep_existing = false
if g.OpenPopupStack.Data[current_stack_size + 1].PopupId == id then
if (g.OpenPopupStack.Data[current_stack_size + 1].OpenFrameCount == g.FrameCount - 1) or (bit32.band(popup_flags, 32 --[[ImGuiPopupFlags.NoReopen]]) ~= 0) then
keep_existing = true
end
end
if keep_existing then
-- No reopen
g.OpenPopupStack.Data[current_stack_size + 1].OpenFrameCount = popup_ref.OpenFrameCount
else
-- Reopen: close child popups if any, then flag popup for open/reopen (set position, focus, init navigation)
ImGui.ClosePopupToLevel(current_stack_size, true)
g.OpenPopupStack:push_back(popup_ref)
end
-- When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
-- This is equivalent to what ClosePopupToLevel() does.
-- if (g.OpenPopupStack[current_stack_size + 1].PopupId == id)
-- FocusWindow(parent_window);
return not keep_existing
end
end
-- When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
-- This function closes any popups that are over 'ref_window'.
--- @param ref_window? ImGuiWindow
--- @param restore_focus_to_window_under_popup bool
function ImGui.ClosePopupsOverWindow(ref_window, restore_focus_to_window_under_popup)
local g = GImGui
if g.OpenPopupStack.Size == 0 then
return
end
-- Don't close our own child popup windows.
-- IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\") restore_under=%d\n", ref_window and ref_window.Name or "<NULL>", restore_focus_to_window_under_popup)
local popup_count_to_keep = 0
if ref_window then
-- Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
local ref_window_is_descendent_of_popup
local stop_8041 = false
while popup_count_to_keep < g.OpenPopupStack.Size do
repeat
local popup = g.OpenPopupStack.Data[popup_count_to_keep + 1]
if not popup.Window then
break
end
assert(bit32.band(popup.Window.Flags, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0)
-- Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)
-- - Clicking/Focusing Window2 won't close Popup1:
-- Window -> Popup1 -> Window2(Ref)
-- - Clicking/focusing Popup1 will close Popup2 and Popup3:
-- Window -> Popup1(Ref) -> Popup2 -> Popup3
-- - Each popups may contain child windows, which is why we compare .RootWindowDockTree!
-- Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child
-- We step through every popup from bottom to top to validate their position relative to reference window.
ref_window_is_descendent_of_popup = false
for n = popup_count_to_keep, g.OpenPopupStack.Size - 1 do
local popup_window = g.OpenPopupStack.Data[n + 1].Window
-- if (popup_window.RootWindowDockTree == ref_window.RootWindowDockTree) -- FIXME-MERGE
if popup_window and ImGui.IsWindowWithinBeginStackOf(ref_window, popup_window) then
ref_window_is_descendent_of_popup = true
break
end
end
if not ref_window_is_descendent_of_popup then
stop_8041 = true; break
end
until true
if stop_8041 then break end
popup_count_to_keep = popup_count_to_keep + 1
end
end
if popup_count_to_keep < g.OpenPopupStack.Size then -- This test is not required but it allows to set a convenient breakpoint on the statement below
-- IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\")\n", ref_window and ref_window.Name or "<NULL>")
ImGui.ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup)
end
end
--- @param remaining int # 0-based
--- @param restore_focus_to_window_under_popup bool
function ImGui.ClosePopupToLevel(remaining, restore_focus_to_window_under_popup)
local g = GImGui
-- IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupToLevel(%d), restore_under=%d\n", remaining, restore_focus_to_window_under_popup)
assert(remaining >= 0 and remaining < g.OpenPopupStack.Size)
-- if bit32.band(g.DebugLogFlags, ImGuiDebugLogFlags.EventPopup) ~= 0 then
-- for n = remaining + 1, g.OpenPopupStack.Size do
-- local popup = g.OpenPopupStack.Data[n]
-- IMGUI_DEBUG_LOG_POPUP("[popup] - Closing PopupID 0x%08X Window \"%s\"", popup.PopupId, popup.Window and popup.Window.Name or nil)
-- end
-- end
-- Trim open popup stack
-- FIXME: is it safer to full copy here?
local prev_popup = g.OpenPopupStack.Data[remaining + 1]
g.OpenPopupStack:resize(remaining)
-- Restore focus (unless popup window was not yet submitted, and didn't have a chance to take focus anyhow. See #7325 for an edge case)
if restore_focus_to_window_under_popup and prev_popup.Window then
local popup_window = prev_popup.Window
local focus_window
if bit32.band(popup_window.Flags, 268435456 --[[ImGuiWindowFlags.ChildMenu]]) ~= 0 then
focus_window = popup_window.ParentWindow
else
focus_window = prev_popup.RestoreNavWindow
end
if focus_window and not focus_window.WasActive then
ImGui.FocusTopMostWindowUnderOne(popup_window, nil, nil, 1 --[[ImGuiFocusRequestFlags.RestoreFocusedChild]]) -- Fallback
else
local focus_flags = (g.NavLayer == 0 --[[ImGuiNavLayer.Main]]) and 1 --[[ImGuiFocusRequestFlags.RestoreFocusedChild]] or 0 --[[ImGuiFocusRequestFlags.None]]
ImGui.FocusWindow(focus_window, focus_flags)
end
end
end
-- Close the popup we have begin-ed into.
function ImGui.CloseCurrentPopup()
local g = GImGui
local popup_idx = g.BeginPopupStack.Size
if popup_idx < 1 or popup_idx > g.OpenPopupStack.Size or g.BeginPopupStack.Data[popup_idx].PopupId ~= g.OpenPopupStack.Data[popup_idx].PopupId then
return
end
-- Closing a menu closes its top-most parent popup (unless a modal)
while popup_idx > 1 do
local popup_window = g.OpenPopupStack.Data[popup_idx].Window
local parent_popup_window = g.OpenPopupStack.Data[popup_idx - 1].Window
local close_parent = false
if popup_window and bit32.band(popup_window.Flags, 268435456 --[[ImGuiWindowFlags.ChildMenu]]) ~= 0 then
if parent_popup_window and not (bit32.band(parent_popup_window.Flags, 1024 --[[ImGuiWindowFlags.MenuBar]]) ~= 0) then
close_parent = true
end
end
if not close_parent then
break
end
popup_idx = popup_idx - 1
end
-- IMGUI_DEBUG_LOG_POPUP("[popup] CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size, popup_idx)
ImGui.ClosePopupToLevel(popup_idx - 1, true)
-- A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
-- To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
-- Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
local window = g.NavWindow
if window then
window.DC.NavHideHighlightOneFrame = true
end
end
-- Attention! BeginPopup() adds default flags when calling BeginPopupEx()!
--- @param id ImGuiID
--- @param extra_window_flags ImGuiWindowFlags
function ImGui.BeginPopupEx(id, extra_window_flags)
local g = GImGui
if not ImGui.IsPopupOpen(id, 0 --[[ImGuiPopupFlags.None]]) then
g.NextWindowData:ClearFlags() -- We behave like Begin() and need to consume those values
return false
end
assert(bit32.band(extra_window_flags, 268435456 --[[ImGuiWindowFlags.ChildMenu]]) == 0) -- Use BeginPopupMenuEx()
local name = ImFormatString("##Popup_%08x", id) -- No recycling, so we can close/open during the same frame
local is_open
_, is_open = ImGui.Begin(name, nil, bit32.bor(extra_window_flags, 67108864 --[[ImGuiWindowFlags.Popup]], 524288 --[[ImGuiWindowFlags.NoDocking]]))
if not is_open then -- NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
ImGui.EndPopup()
end
-- g.CurrentWindow.FocusRouteParentWindow = g.CurrentWindow.ParentWindowInBeginStack
return is_open
end
--- @param id ImGuiID
--- @param label any
--- @param extra_window_flags ImGuiWindowFlags
function ImGui.BeginPopupMenuEx(id, label, extra_window_flags)
local g = GImGui
if not ImGui.IsPopupOpen(id, 0 --[[ImGuiPopupFlags.None]]) then
g.NextWindowData:ClearFlags() -- We behave like Begin() and need to consume those values
return false
end
-- As we bypass BeginChild(), set ImGuiChildFlags.AlwaysAutoResize as it is checked independently from ImGuiWindowFlags.AlwaysAutoResize for now (see #9355)
-- Ideally we should remove setting ImGuiWindowFlags.AlwaysAutoResize in BeginChild().
if bit32.band(extra_window_flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0 and bit32.band(extra_window_flags, 64 --[[ImGuiWindowFlags.AlwaysAutoResize]]) ~= 0 then
ImGui.SetNextWindowChildFlags(64 --[[ImGuiChildFlags.AlwaysAutoResize]], true)
end
assert(bit32.band(extra_window_flags, 268435456 --[[ImGuiWindowFlags.ChildMenu]]) ~= 0)
local name = ImFormatString("%s###Menu_%02d", label, g.BeginMenuDepth) -- Recycle windows based on depth
local is_open
_, is_open = ImGui.Begin(name, nil, bit32.bor(extra_window_flags, 67108864 --[[ImGuiWindowFlags.Popup]]))
if not is_open then -- NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
ImGui.EndPopup()
end
-- g.CurrentWindow.FocusRouteParentWindow = g.CurrentWindow.ParentWindowInBeginStack
return is_open
end
--- @param str_id string
--- @param flags? ImGuiWindowFlags
function ImGui.BeginPopup(str_id, flags)
if flags == nil then flags = 0 end
local g = GImGui
if g.OpenPopupStack.Size <= g.BeginPopupStack.Size then -- Early out for performance
g.NextWindowData:ClearFlags() -- We behave like Begin() and need to consume those values
return false
end
flags = bit32.bor(flags, 64 --[[ImGuiWindowFlags.AlwaysAutoResize]], 1 --[[ImGuiWindowFlags.NoTitleBar]], 256 --[[ImGuiWindowFlags.NoSavedSettings]])
local id = g.CurrentWindow:GetID(str_id)
return ImGui.BeginPopupEx(id, flags)
end
function ImGui.EndPopup()
local g = GImGui
local window = g.CurrentWindow
IM_ASSERT_USER_ERROR_RET((bit32.band(window.Flags, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0) and (g.BeginPopupStack.Size > 0), "Calling EndPopup() in wrong window!")
-- Make all menus and popups wrap around for now, may need to expose that policy (e.g. focus scope could include wrap/loop policy flags used by new move requests)
if g.NavWindow == window then
ImGui.NavMoveRequestTryWrapping(window, 2 --[[ImGuiNavMoveFlags.LoopY]])
end
-- Child-popups don't need to be laid out
local backup_within_end_popup_id = g.WithinEndPopupID
local backup_within_end_child_id = g.WithinEndChildID
g.WithinEndPopupID = window.ID
if bit32.band(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0 then
g.WithinEndChildID = window.ID
end
ImGui.End()
g.WithinEndPopupID = backup_within_end_popup_id
g.WithinEndChildID = backup_within_end_child_id
end
-- r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.)
-- r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it.
-- (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor
-- information are available, it may represent the entire platform monitor from the frame of reference of the current viewport.
-- this allows us to have tooltips/popups displayed out of the parent viewport.)
--- @param ref_pos ImVec2
--- @param size ImVec2
--- @param last_dir ImGuiDir
--- @param r_outer ImRect
--- @param r_avoid ImRect
--- @param policy ImGuiPopupPositionPolicy
--- @return ImVec2 # Best Window Pos For Popup
--- @return int # Updated last_dir
--- @nodiscard
function ImGui.FindBestWindowPosForPopupEx(ref_pos, size, last_dir, r_outer, r_avoid, policy)
local base_pos_clamped
-- Combo Box policy (we want a connecting edge)
if policy == 1 --[[ImGuiPopupPositionPolicy.ComboBox]] then
local dir_preferred_order = {3 --[[ImGuiDir.Down]], 1 --[[ImGuiDir.Right]], 0 --[[ImGuiDir.Left]], 2 --[[ImGuiDir.Up]]}
for n = (last_dir ~= (-1) --[[ImGuiDir.None]]) and 0 or 1, 4 --[[ImGuiDir.COUNT]] do
repeat
local dir = (n == 0) and last_dir or dir_preferred_order[n]
if n ~= 0 and dir == last_dir then -- Already tried this direction?
break
end
local pos
if dir == 3 --[[ImGuiDir.Down]] then pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y) -- Below, Toward Right (default)
elseif dir == 1 --[[ImGuiDir.Right]] then pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y) -- Above, Toward Right
elseif dir == 0 --[[ImGuiDir.Left]] then pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y) -- Below, Toward Left
elseif dir == 2 --[[ImGuiDir.Up]] then pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y) -- Above, Toward Left
end
if not r_outer:Contains(ImRect(pos, pos + size)) then
break
end
last_dir = dir
do return pos, last_dir end
until true
end
end
-- Tooltip and Default popup policy
-- (Always first try the direction we used on the last frame, if any)
if policy == 2 --[[ImGuiPopupPositionPolicy.Tooltip]] or policy == 0 --[[ImGuiPopupPositionPolicy.Default]] then
local dir_preferred_order = {1 --[[ImGuiDir.Right]], 3 --[[ImGuiDir.Down]], 2 --[[ImGuiDir.Up]], 0 --[[ImGuiDir.Left]]}
for n = (last_dir ~= (-1) --[[ImGuiDir.None]]) and 0 or 1, 4 --[[ImGuiDir.COUNT]] do
repeat
local dir = (n == 0) and last_dir or dir_preferred_order[n]
if n ~= 0 and dir == last_dir then -- Already tried this direction?
break
end
local avail_w = ((dir == 0 --[[ImGuiDir.Left]]) and r_avoid.Min.x or r_outer.Max.x) - ((dir == 1 --[[ImGuiDir.Right]]) and r_avoid.Max.x or r_outer.Min.x)
local avail_h = ((dir == 2 --[[ImGuiDir.Up]]) and r_avoid.Min.y or r_outer.Max.y) - ((dir == 3 --[[ImGuiDir.Down]]) and r_avoid.Max.y or r_outer.Min.y)
-- If there's not enough room on one axis, there's no point in positioning on a side on this axis (e.g. when not enough width, use a top/bottom position to maximize available width)
if avail_w < size.x and (dir == 0 --[[ImGuiDir.Left]] or dir == 1 --[[ImGuiDir.Right]]) then
break
end
if avail_h < size.y and (dir == 2 --[[ImGuiDir.Up]] or dir == 3 --[[ImGuiDir.Down]]) then
break
end
base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size)
local pos = ImVec2()
pos.x = (dir == 0 --[[ImGuiDir.Left]]) and (r_avoid.Min.x - size.x) or ((dir == 1 --[[ImGuiDir.Right]]) and r_avoid.Max.x or base_pos_clamped.x)
pos.y = (dir == 2 --[[ImGuiDir.Up]]) and (r_avoid.Min.y - size.y) or ((dir == 3 --[[ImGuiDir.Down]]) and r_avoid.Max.y or base_pos_clamped.y)
-- Clamp top-left corner of popup
pos.x = ImMax(pos.x, r_outer.Min.x)
pos.y = ImMax(pos.y, r_outer.Min.y)
last_dir = dir
do return pos, last_dir end
until true
end
end
-- Fallback when not enough room:
last_dir = (-1) --[[ImGuiDir.None]]
-- For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
if policy == 2 --[[ImGuiPopupPositionPolicy.Tooltip]] then
return ref_pos + ImVec2(2, 2), last_dir
end
-- Otherwise try to keep within display
local pos = ImVec2(ref_pos.x, ref_pos.y)
pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x)
pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y)
return pos, last_dir
end
-- Note that this is used for popups, which can overlap the non work-area of individual viewports.
--- @param window ImGuiWindow
--- @return ImRect
--- @nodiscard
function ImGui.GetPopupAllowedExtentRect(window)
local g = GImGui
local r_screen = ImRect()
if window.ViewportAllowPlatformMonitorExtend >= 1 then
-- Extent with be in the frame of reference of the given viewport (so Min is likely to be negative here)
local monitor = g.PlatformIO.Monitors.Data[window.ViewportAllowPlatformMonitorExtend]
ImVec2_Copy(r_screen.Min, monitor.WorkPos)
ImVec2_Copy(r_screen.Max, monitor.WorkPos + monitor.WorkSize)
else
-- Use the full viewport area (not work area) for popups
r_screen = window.Viewport:GetMainRect()
end
local padding = g.Style.DisplaySafeAreaPadding
r_screen:Expand(ImVec2((r_screen:GetWidth() > padding.x * 2) and -padding.x or 0.0, (r_screen:GetHeight() > padding.y * 2) and -padding.y or 0.0))
return r_screen
end
--- @param window ImGuiWindow
--- @return ImVec2
--- @nodiscard
function ImGui.FindBestWindowPosForPopup(window)
local g = GImGui
local r_outer = ImGui.GetPopupAllowedExtentRect(window)
if bit32.band(window.Flags, 268435456 --[[ImGuiWindowFlags.ChildMenu]]) ~= 0 then
-- Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
-- This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
local parent_window = window.ParentWindow
local horizontal_overlap = g.Style.ItemInnerSpacing.x -- We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x).
local r_avoid
if parent_window.DC.MenuBarAppending then
r_avoid = ImRect(-3.4028234663853e+38 --[[FLT_MAX]], parent_window.ClipRect.Min.y, 3.4028234663853e+38 --[[FLT_MAX]], parent_window.ClipRect.Max.y) -- Avoid parent menu-bar. If we wanted multi-line menu-bar, we may instead want to have the calling window setup e.g. a NextWindowData.PosConstraintAvoidRect field
else
r_avoid = ImRect(parent_window.Pos.x + horizontal_overlap, -3.4028234663853e+38 --[[FLT_MAX]], parent_window.Pos.x + parent_window.Size.x - horizontal_overlap - parent_window.ScrollbarSizes.x, 3.4028234663853e+38 --[[FLT_MAX]])
end
local pos
pos, window.AutoPosLastDirection = ImGui.FindBestWindowPosForPopupEx(window.Pos, window.Size, window.AutoPosLastDirection, r_outer, r_avoid, 0 --[[ImGuiPopupPositionPolicy.Default]])
return pos
end
if bit32.band(window.Flags, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0 then
local pos
pos, window.AutoPosLastDirection = ImGui.FindBestWindowPosForPopupEx(window.Pos, window.Size, window.AutoPosLastDirection, r_outer, ImRect(window.Pos, window.Pos), 0 --[[ImGuiPopupPositionPolicy.Default]]) -- Ideally we'd disable r_avoid here
return pos
end
if bit32.band(window.Flags, 33554432 --[[ImGuiWindowFlags.Tooltip]]) ~= 0 then
-- Position tooltip (always follows mouse + clamp within outer boundaries)
-- FIXME:
-- - Too many paths. One problem is that FindBestWindowPosForPopupEx() doesn't allow passing a suggested position (so touch screen path doesn't use it by default).
-- - Drag and drop tooltips are not using this path either: BeginTooltipEx() manually sets their position.
-- - Require some tidying up. In theory we could handle both cases in same location, but requires a bit of shuffling
-- as drag and drop tooltips are calling SetNextWindowPos() leading to 'window_pos_set_by_api' being set in Begin().
assert(g.CurrentWindow == window)
local scale = g.Style.MouseCursorScale
local ref_pos = ImGui.NavCalcPreferredRefPos(33554432 --[[ImGuiWindowFlags.Tooltip]])
if g.IO.MouseSource == 1 --[[ImGuiMouseSource.TouchScreen]] and ImGui.NavCalcPreferredRefPosSource(33554432 --[[ImGuiWindowFlags.Tooltip]]) == 1 --[[ImGuiInputSource.Mouse]] then
local tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_TOUCH * scale - TOOLTIP_DEFAULT_PIVOT_TOUCH * window.Size
if r_outer:Contains(ImRect(tooltip_pos, tooltip_pos + window.Size)) then
return tooltip_pos
end
end
local tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_MOUSE * scale
local r_avoid
if g.NavCursorVisible and g.NavHighlightItemUnderNav and not g.IO.ConfigNavMoveSetMousePos then
r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8)
else
r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * scale, ref_pos.y + 24 * scale) -- FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
end
-- ImGui.GetForegroundDrawList():AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255, 0, 255, 255))
local pos
pos, window.AutoPosLastDirection = ImGui.FindBestWindowPosForPopupEx(tooltip_pos, window.Size, window.AutoPosLastDirection, r_outer, r_avoid, 2 --[[ImGuiPopupPositionPolicy.Tooltip]])
return pos
end
assert(false)
return window.Pos
end
--- @param flags ImGuiPopupFlags
--- @return ImGuiMouseButton
function ImGui.GetMouseButtonFromPopupFlags(flags)
assert(bit32.band(flags, 3 --[[ImGuiPopupFlags.InvalidMask_]]) == 0)
if bit32.band(flags, 12 --[[ImGuiPopupFlags.MouseButtonMask_]]) ~= 0 then
return bit32.rshift(bit32.band(flags, 12 --[[ImGuiPopupFlags.MouseButtonMask_]]), 2 --[[ImGuiPopupFlags.MouseButtonShift_]]) - 1
end
return 1 --[[ImGuiMouseButton.Right]] -- Default == 1
end
--- @param popup_flags ImGuiPopupFlags
--- @param id ImGuiID
@native
function ImGui.IsPopupOpenRequestForItem(popup_flags, id)
local g = GImGui
local mouse_button = ImGui.GetMouseButtonFromPopupFlags(popup_flags)
if ImGui.IsMouseReleased(mouse_button) and ImGui.IsItemHovered(32 --[[ImGuiHoveredFlags.AllowWhenBlockedByPopup]]) then
return true
end
if g.NavOpenContextMenuItemId == id and (ImGui.IsItemFocused() or id == g.CurrentWindow.MoveId) then
return true
end
return false
end
-- Helper to open a popup if mouse button is released over the item
-- - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup()
--- @param str_id? string
--- @param popup_flags? ImGuiPopupFlags
@native
function ImGui.OpenPopupOnItemClick(str_id, popup_flags)
if popup_flags == nil then popup_flags = 0 end
local g = GImGui
if ImGui.IsPopupOpenRequestForItem(popup_flags, g.LastItemData.ID) then
local window = g.CurrentWindow
local id
if str_id then id = window:GetID(str_id) else id = g.LastItemData.ID end -- If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
assert(id ~= 0) -- You cannot pass a nil str_id if the last item has no identifier (e.g. a Text() item)
return ImGui.OpenPopupEx(id, popup_flags)
end
return false
end
-- This is a helper to handle the simplest case of associating one named popup to one given widget.
-- - To create a popup associated to the last item, you generally want to pass a nil value to str_id.
-- - To create a popup with a specific identifier, pass it in str_id.
-- - This is useful when using BeginPopupContextItem() on an item which doesn't have an identifier, e.g. a Text() call.
-- - This is useful when multiple code locations may want to manipulate/open the same popup, given an explicit id.
-- - You may want to handle the whole on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
-- This is essentially the same as:
-- id = str_id ? GetID(str_id) : GetItemID();
-- OpenPopupOnItemClick(str_id, ImGuiPopupFlags_MouseButtonRight);
-- return BeginPopup(id);
-- Which is essentially the same as:
-- id = str_id ? GetID(str_id) : GetItemID();
-- if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
-- OpenPopup(id);
-- return BeginPopup(id);
-- The main difference being that this is tweaked to avoid computing the ID twice.
--- @param str_id? string
--- @param popup_flags? ImGuiPopupFlags
--- @return bool
function ImGui.BeginPopupContextItem(str_id, popup_flags)
if popup_flags == nil then popup_flags = 0 end
local g = GImGui
local window = g.CurrentWindow
if window.SkipItems then
return false
end
local id
if str_id then id = window:GetID(str_id) else id = g.LastItemData.ID end -- If user hasn't passed an ID, we can use the LastItem ID. Using LastItem ID as a Popup ID won't conflict!
assert(id ~= 0) -- You cannot pass a nil str_id if the last item has no identifier (e.g. a Text() item)
if ImGui.IsPopupOpenRequestForItem(popup_flags, g.LastItemData.ID) then
ImGui.OpenPopupEx(id, popup_flags)
end
return ImGui.BeginPopupEx(id, bit32.bor(64 --[[ImGuiWindowFlags.AlwaysAutoResize]], 1 --[[ImGuiWindowFlags.NoTitleBar]], 256 --[[ImGuiWindowFlags.NoSavedSettings]]))
end
-------------------------------------------------------------------------------
-- [SECTION] KEYBOARD/GAMEPAD NAVIGATION
-------------------------------------------------------------------------------
-- FIXME-NAV: The existence of SetNavID vs SetFocusID vs FocusWindow() needs to be clarified/reworked.
-- In our terminology those should be interchangeable, yet right now this is super confusing.
-- Those two functions are merely a legacy artifact, so at minimum naming should be clarified.
--- @param id ImGuiID
function ImGui.PushFocusScope(id)
local g = GImGui
local data = ImGuiFocusScopeData()
data.ID = id
data.WindowID = g.CurrentWindow.ID
g.FocusScopeStack:push_back(data)
g.CurrentFocusScopeId = id
end
function ImGui.PopFocusScope()
local g = GImGui
-- TODO: IM_ASSERT_USER_ERROR_RET(g.FocusScopeStack.Size > g.StackSizesInBeginForCurrentWindow.SizeOfFocusScopeStack, "Calling PopFocusScope() too many times!")
g.FocusScopeStack:pop_back()
if g.FocusScopeStack.Size > 0 then
g.CurrentFocusScopeId = g.FocusScopeStack:back().ID
else
g.CurrentFocusScopeId = 0
end
end
--- @param focus_scope_id ImGuiID
function ImGui.IsInNavFocusRoute(focus_scope_id)
local g = GImGui
if g.NavFocusScopeId == focus_scope_id then
return true
end
for _, focus_scope in g.NavFocusRoute:iter() do
if focus_scope.ID == focus_scope_id then
return true
end
end
return false
end
--- @param focus_scope_id ImGuiID
function ImGui.SetNavFocusScope(focus_scope_id)
local g = GImGui
g.NavFocusScopeId = focus_scope_id
g.NavFocusRoute:resize(0) -- Invalidate
if focus_scope_id == 0 then
return
end
assert(g.NavWindow ~= nil)
-- Store current path (in reverse order)
if focus_scope_id == g.CurrentFocusScopeId then
-- Top of focus stack contains local focus scopes inside current window
for n = g.FocusScopeStack.Size, 1, -1 do
local data = g.FocusScopeStack.Data[n]
if data.WindowID ~= g.CurrentWindow.ID then
break
end
g.NavFocusRoute:push_back(data)
end
elseif focus_scope_id == g.NavWindow.NavRootFocusScopeId then
local data = ImGuiFocusScopeData()
data.ID = focus_scope_id
data.WindowID = g.NavWindow.ID
g.NavFocusRoute:push_back(data)
else
return
end
-- Then follow on manually set ParentWindowForFocusRoute field (#6798)
local window = g.NavWindow.ParentWindowForFocusRoute
while window ~= nil do
local data = ImGuiFocusScopeData()
data.ID = window.NavRootFocusScopeId
data.WindowID = window.ID
g.NavFocusRoute:push_back(data)
window = window.ParentWindowForFocusRoute
end
assert(g.NavFocusRoute.Size < 100) -- Maximum depth is technically 251 as per CalcRoutingScore(): 254 - 3
end
function ImGui.SetItemDefaultFocus()
local g = GImGui
local window = g.CurrentWindow
if not window.Appearing then
return
end
if g.NavWindow ~= window.RootWindowForNav or (not g.NavInitRequest and g.NavInitResult.ID == 0) or g.NavLayer ~= window.DC.NavLayerCurrent then
return
end
g.NavInitRequest = false
ImGui.NavApplyItemToResult(g.NavInitResult)
ImGui.NavUpdateAnyRequestFlag()
-- Scroll could be done in NavInitRequestApplyResult() via an opt-in flag (we however don't want regular init requests to scroll)
if not window.ClipRect:Contains(g.LastItemData.Rect) then
ImGui.ScrollToRectEx(window, g.LastItemData.Rect, 0 --[[ImGuiScrollFlags.None]])
end
end
-- (was called NavRestoreHighlightAfterMove() before 1.91.4)
function ImGui.SetNavCursorVisibleAfterMove()
local g = GImGui
if g.NavWindow and (bit32.band(g.NavWindow.Flags, 65536 --[[ImGuiWindowFlags.NoNavInputs]]) ~= 0) then
g.NavCursorVisible = false
elseif g.NavInputSource == 2 --[[ImGuiInputSource.Keyboard]] and (bit32.band(g.IO.ConfigFlags, 1 --[[ImGuiConfigFlags.NavEnableKeyboard]]) == 0) then
g.NavCursorVisible = false
elseif g.NavInputSource == 3 --[[ImGuiInputSource.Gamepad]] and (bit32.band(g.IO.ConfigFlags, 2 --[[ImGuiConfigFlags.NavEnableGamepad]]) == 0) then
g.NavCursorVisible = false
elseif g.IO.ConfigNavCursorVisibleAuto then
g.NavCursorVisible = true
end
g.NavHighlightItemUnderNav = true
g.NavMousePosDirty = true
end
--- @param window? ImGuiWindow
function ImGui.SetNavWindow(window)
local g = GImGui
if g.NavWindow ~= window then
-- IMGUI_DEBUG_LOG_FOCUS("[focus] SetNavWindow(\"%s\")", window and window.Name or "<NULL>")
g.NavWindow = window
g.NavLastValidSelectionUserData = (-1) --[[ImGuiSelectionUserData_Invalid]]
end
g.NavInitRequest = false
g.NavMoveSubmitted = false
g.NavMoveScoringItems = false
ImGui.NavUpdateAnyRequestFlag()
end
--- @param axis ImGuiAxis
function ImGui.NavClearPreferredPosForAxis(axis)
local g = GImGui
g.NavWindow.RootWindowForNav.NavPreferredScoringPosRel[g.NavLayer][axis] = 3.4028234663853e+38 --[[FLT_MAX]]
end
--- @param id ImGuiID
--- @param nav_layer ImGuiNavLayer
--- @param focus_scope_id ImGuiID
--- @param rect_rel ImRect
function ImGui.SetNavID(id, nav_layer, focus_scope_id, rect_rel)
local g = GImGui
assert(g.NavWindow ~= nil)
assert(nav_layer == 0 --[[ImGuiNavLayer.Main]] or nav_layer == 1 --[[ImGuiNavLayer.Menu]])
g.NavId = id
g.NavLayer = nav_layer
ImGui.SetNavFocusScope(focus_scope_id)
g.NavWindow.NavLastIds[nav_layer] = id
ImRect_Copy(g.NavWindow.NavRectRel[nav_layer], rect_rel)
-- Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
ImGui.NavClearPreferredPosForAxis(1 --[[ImGuiAxis.X]])
ImGui.NavClearPreferredPosForAxis(2 --[[ImGuiAxis.Y]])
end
-- FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
-- This way we could find the last focused window among our children. It would be much less confusing this way?
--- @param nav_window ImGuiWindow
function ImGui.NavSaveLastChildNavWindowIntoParent(nav_window)
local parent = nav_window
while parent and parent.RootWindow ~= parent and bit32.band(parent.Flags, bit32.bor(67108864 --[[ImGuiWindowFlags.Popup]], 268435456 --[[ImGuiWindowFlags.ChildMenu]])) == 0 do
parent = parent.ParentWindow
end
if parent and parent ~= nav_window then
parent.NavLastChildNavWindow = nav_window
end
end
-- Restore the last focused child.
-- Call when we are expected to land on the Main Layer (0) after FocusWindow()
--- @param window ImGuiWindow
function ImGui.NavRestoreLastChildNavWindow(window)
if window.NavLastChildNavWindow and window.NavLastChildNavWindow.WasActive then
return window.NavLastChildNavWindow
end
return window
end
--- @param layer ImGuiNavLayer
function ImGui.NavRestoreLayer(layer)
local g = GImGui
if layer == 0 --[[ImGuiNavLayer.Main]] then
local prev_nav_window = g.NavWindow
g.NavWindow = ImGui.NavRestoreLastChildNavWindow(g.NavWindow) -- FIXME-NAV: Should clear ongoing nav requests?
g.NavLastValidSelectionUserData = (-1) --[[ImGuiSelectionUserData_Invalid]]
-- if (prev_nav_window) IMGUI_DEBUG_LOG_FOCUS("[focus] NavRestoreLayer: from \"%s\" to SetNavWindow(\"%s\")\n", prev_nav_window->Name, g.NavWindow->Name)
end
local window = g.NavWindow
if window.NavLastIds[layer] ~= 0 then
ImGui.SetNavID(window.NavLastIds[layer], layer, 0, window.NavRectRel[layer])
else
g.NavLayer = layer
ImGui.NavInitWindow(window, true)
end
end
--- @param id ImGuiID
function ImGui.NavHighlightActivated(id)
local g = GImGui
g.NavHighlightActivatedId = id
g.NavHighlightActivatedTimer = 0.1 --[[NAV_ACTIVATE_HIGHLIGHT_TIMER]]
end
function ImGui.NavUpdateAnyRequestFlag()
local g = GImGui
g.NavAnyRequest = g.NavMoveScoringItems or g.NavInitRequest or (IMGUI_DEBUG_NAV_SCORING and g.NavWindow ~= nil)
if g.NavAnyRequest then
assert(g.NavWindow ~= nil)
end
end
-- This needs to be called before we submit any widget (aka in or before Begin)
--- @param window ImGuiWindow
--- @param force_reinit bool
function ImGui.NavInitWindow(window, force_reinit)
-- FIXME: ChildWindow test here is wrong for docking
local g = GImGui
assert(window == g.NavWindow)
if bit32.band(window.Flags, 65536 --[[ImGuiWindowFlags.NoNavInputs]]) ~= 0 then
g.NavId = 0
ImGui.SetNavFocusScope(window.NavRootFocusScopeId)
return
end
local init_for_nav = false
if window == window.RootWindow or bit32.band(window.Flags, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0 or window.NavLastIds[0] == 0 or force_reinit then
init_for_nav = true
end
-- IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d", init_for_nav, window->Name, g.NavLayer)
if init_for_nav then
ImGui.SetNavID(0, g.NavLayer, window.NavRootFocusScopeId, ImRect())
g.NavInitRequest = true
g.NavInitRequestFromMove = false
g.NavInitResult.ID = 0
ImGui.NavUpdateAnyRequestFlag()
else
g.NavId = window.NavLastIds[0]
ImGui.SetNavFocusScope(window.NavRootFocusScopeId)
end
end
-- Positioning logic altered slightly for remote activation: for Popup we want to use item rect, for Tooltip we leave things alone. (#9138)
-- When calling for ImGuiWindowFlags.Popup we use LastItemData.
--- @param window_type ImGuiWindowFlags
--- @return ImGuiInputSource
function ImGui.NavCalcPreferredRefPosSource(window_type)
local g = GImGui
local window = g.NavWindow
local activated_shortcut = g.ActiveId ~= 0 and g.ActiveIdFromShortcut and g.ActiveId == g.LastItemData.ID
if (bit32.band(window_type, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0) and activated_shortcut then
return 2 --[[ImGuiInputSource.Keyboard]]
end
if not g.NavCursorVisible or not g.NavHighlightItemUnderNav or not window then
return 1 --[[ImGuiInputSource.Mouse]]
else
return 2 --[[ImGuiInputSource.Keyboard]] -- or Nav in general
end
end
--- @param window_type ImGuiWindowFlags
--- @return ImVec2
--- @nodiscard
function ImGui.NavCalcPreferredRefPos(window_type)
local g = GImGui
local window = g.NavWindow
local source = ImGui.NavCalcPreferredRefPosSource(window_type)
if source == 1 --[[ImGuiInputSource.Mouse]] then
-- Mouse (we need a fallback in case the mouse becomes invalid after being used)
-- The +1.0f offset when stored by OpenPopupEx() allows reopening this or another popup (same or another mouse button) while not moving the mouse, it is pretty standard.
-- In theory we could move that +1.0f offset in OpenPopupEx()
local p = ImGui.IsMousePosValid(g.IO.MousePos) and g.IO.MousePos or g.MouseLastValidPos
return ImVec2(p.x + 1.0, p.y)
else
-- When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item
local activated_shortcut = g.ActiveId ~= 0 and g.ActiveIdFromShortcut and g.ActiveId == g.LastItemData.ID
local ref_rect = ImRect()
if activated_shortcut and (bit32.band(window_type, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0) then
ImRect_Copy(ref_rect, g.LastItemData.NavRect)
elseif window ~= nil then
ImRect_Copy(ref_rect, ImGui.WindowRectRelToAbs(window, window.NavRectRel[g.NavLayer]))
end
-- Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?)
if window ~= nil and window.LastFrameActive ~= g.FrameCount and (window.ScrollTarget.x ~= 3.4028234663853e+38 --[[FLT_MAX]] or window.ScrollTarget.y ~= 3.4028234663853e+38 --[[FLT_MAX]]) then
local next_scroll = CalcNextScrollFromScrollTargetAndClamp(window)
ref_rect:Translate(window.Scroll - next_scroll)
end
local pos = ImVec2(ref_rect.Min.x + ImMin(g.Style.FramePadding.x * 4, ref_rect:GetWidth()), ref_rect.Max.y - ImMin(g.Style.FramePadding.y, ref_rect:GetHeight()))
if window ~= nil then
local viewport = window.Viewport
if viewport then
pos = ImClamp(pos, viewport.Pos, viewport.Pos + viewport.Size)
end
end
return ImTrunc(pos) -- ImTrunc() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta.
end
end
--- @param axis ImGuiAxis
--- @return float
function ImGui.GetNavTweakPressedAmount(axis)
local g = GImGui
local repeat_delay, repeat_rate = ImGui.GetTypematicRepeatRate(8 --[[ImGuiInputFlags.RepeatRateNavTweak]])
local key_less, key_more
if g.NavInputSource == 3 --[[ImGuiInputSource.Gamepad]] then
key_less = (axis == 1 --[[ImGuiAxis.X]]) and 638 --[[ImGuiKey.GamepadDpadLeft]] or 640 --[[ImGuiKey.GamepadDpadUp]]
key_more = (axis == 1 --[[ImGuiAxis.X]]) and 639 --[[ImGuiKey.GamepadDpadRight]] or 641 --[[ImGuiKey.GamepadDpadDown]]
else
key_less = (axis == 1 --[[ImGuiAxis.X]]) and 513 --[[ImGuiKey.LeftArrow]] or 515 --[[ImGuiKey.UpArrow]]
key_more = (axis == 1 --[[ImGuiAxis.X]]) and 514 --[[ImGuiKey.RightArrow]] or 516 --[[ImGuiKey.DownArrow]]
end
local amount = ImGui.GetKeyPressedAmount(key_more, repeat_delay, repeat_rate) - ImGui.GetKeyPressedAmount(key_less, repeat_delay, repeat_rate)
if amount ~= 0.0 and ImGui.IsKeyDown(key_less) and ImGui.IsKeyDown(key_more) then -- Cancel when opposite directions are held, regardless of repeat phase
amount = 0.0
end
return amount
end
do
local nav_gamepad_keys_to_change_source = { ImGuiKey.GamepadFaceRight, ImGuiKey.GamepadFaceLeft, ImGuiKey.GamepadFaceUp, ImGuiKey.GamepadFaceDown, ImGuiKey.GamepadDpadRight, ImGuiKey.GamepadDpadLeft, ImGuiKey.GamepadDpadUp, ImGuiKey.GamepadDpadDown }
local nav_keyboard_keys_to_change_source = { ImGuiKey.Space, ImGuiKey.Enter, ImGuiKey.Escape, ImGuiKey.RightArrow, ImGuiKey.LeftArrow, ImGuiKey.UpArrow, ImGuiKey.DownArrow }
--- @param focus_change_dir int
function ImGui.NavUpdateWindowingTarget(focus_change_dir)
local g = GImGui
assert(g.NavWindowingTarget)
if bit32.band(g.NavWindowingTarget.Flags, 134217728 --[[ImGuiWindowFlags.Modal]]) ~= 0 then
return
end
local i_current = ImGui.FindWindowFocusIndex(g.NavWindowingTarget)
local window_target = ImGui.FindWindowNavFocusable(i_current + focus_change_dir, 0, focus_change_dir)
if window_target == nil then
window_target = ImGui.FindWindowNavFocusable((focus_change_dir < 0) and g.WindowsFocusOrder.Size or 1, i_current, focus_change_dir)
end
if window_target then -- Don't reset windowing target if there's a single window in the list
g.NavWindowingTarget = window_target
g.NavWindowingTargetAnim = window_target
ImVec2_CopyV(g.NavWindowingAccumDeltaPos, 0.0, 0.0)
ImVec2_CopyV(g.NavWindowingAccumDeltaSize, 0.0, 0.0)
end
g.NavWindowingToggleLayer = false
end
-- Apply focus and close overlay
--- @param apply_focus_window ImGuiWindow
function ImGui.NavUpdateWindowingApplyFocus(apply_focus_window)
-- FIXME: Many actions here could be part of a higher-level/reused function. Why aren't they in FocusWindow() ?
-- Investigate for each of them: ClearActiveID(), NavRestoreHighlightAfterMove(), NavRestoreLastChildNavWindow(), ClosePopupsOverWindow(), NavInitWindow()
local g = GImGui
if g.NavWindow == nil or apply_focus_window ~= g.NavWindow.RootWindow then
local previous_viewport = g.NavWindow and g.NavWindow.Viewport or nil
ImGui.ClearActiveID()
ImGui.SetNavCursorVisibleAfterMove()
ImGui.ClosePopupsOverWindow(apply_focus_window, false)
ImGui.FocusWindow(apply_focus_window, 1 --[[ImGuiFocusRequestFlags.RestoreFocusedChild]])
assert(g.NavWindow ~= nil)
apply_focus_window = g.NavWindow
if apply_focus_window.NavLastIds[0 --[[ImGuiNavLayer.Main]]] == 0 then -- FIXME: This is the equivalent of the 'if (g.NavId == 0) { NavInitWindow() }' in DockNodeUpdateTabBar().
ImGui.NavInitWindow(apply_focus_window, false)
end
-- If the window has ONLY a menu layer (no main layer), select it directly
-- Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame,
-- so Ctrl+Tab where the keys are only held for 1 frame will be able to use correct layers mask since
-- the target window as already been previewed once.
-- FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases,
-- we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask*
-- won't be valid.
if apply_focus_window.DC.NavLayersActiveMaskNext == bit32.lshift(1, 1 --[[ImGuiNavLayer.Menu]]) then
g.NavLayer = 1 --[[ImGuiNavLayer.Menu]]
end
-- Request OS level focus
if apply_focus_window.Viewport ~= previous_viewport and g.PlatformIO.Platform_SetWindowFocus then
g.PlatformIO.Platform_SetWindowFocus(apply_focus_window.Viewport)
end
end
g.NavWindowingTarget = nil
end
-- Windowing management mode
-- Keyboard: Ctrl+Tab (change focus/move/resize), Alt (toggle menu layer)
-- Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
function ImGui.NavUpdateWindowing()
local g = GImGui
local io = g.IO
local apply_focus_window = nil
local apply_toggle_layer = false
local modal_window = ImGui.GetTopMostPopupModal()
local allow_windowing = (modal_window == nil) -- FIXME: This prevent Ctrl+Tab from being usable with windows that are inside the Begin-stack of that modal.
if not allow_windowing then
g.NavWindowingTarget = nil
end
-- Fade out
if g.NavWindowingTargetAnim and g.NavWindowingTarget == nil then
g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - io.DeltaTime * 10.0, 0.0)
if g.DimBgRatio <= 0.0 and g.NavWindowingHighlightAlpha <= 0.0 then
g.NavWindowingTargetAnim = nil
end
end
-- Start Ctrl+Tab or Square+L/R window selection
-- (g.ConfigNavWindowingKeyNext/g.ConfigNavWindowingKeyPrev defaults are ImGuiMod_Ctrl|ImGuiKey.Tab and ImGuiMod_Ctrl|ImGuiMod_Shift|ImGuiKey.Tab)
local owner_id = ImHashStr("##NavUpdateWindowing")
local nav_gamepad_active = bit32.band(io.ConfigFlags, 2 --[[ImGuiConfigFlags.NavEnableGamepad]]) ~= 0 and bit32.band(io.BackendFlags, 1 --[[ImGuiBackendFlags.HasGamepad]]) ~= 0
local nav_keyboard_active = bit32.band(io.ConfigFlags, 1 --[[ImGuiConfigFlags.NavEnableKeyboard]]) ~= 0
local keyboard_next_window = allow_windowing and g.ConfigNavWindowingKeyNext ~= 0 and ImGui.Shortcut(g.ConfigNavWindowingKeyNext, bit32.bor(1 --[[ImGuiInputFlags.Repeat]], 8192 --[[ImGuiInputFlags.RouteAlways]]), owner_id)
local keyboard_prev_window = allow_windowing and g.ConfigNavWindowingKeyPrev ~= 0 and ImGui.Shortcut(g.ConfigNavWindowingKeyPrev, bit32.bor(1 --[[ImGuiInputFlags.Repeat]], 8192 --[[ImGuiInputFlags.RouteAlways]]), owner_id)
local start_toggling_with_gamepad = nav_gamepad_active and g.NavWindowingTarget == nil and ImGui.Shortcut(634 --[[ImGuiKey.NavGamepadMenu]], 8192 --[[ImGuiInputFlags.RouteAlways]], owner_id)
local start_windowing_with_gamepad = allow_windowing and start_toggling_with_gamepad
local start_windowing_with_keyboard = allow_windowing and g.NavWindowingTarget == nil and (keyboard_next_window or keyboard_prev_window) -- Note: enabled even without NavEnableKeyboard!
local just_started_windowing_from_null_focus = false
if start_toggling_with_gamepad then
g.NavWindowingToggleLayer = true -- Gamepad starts toggling layer
g.NavWindowingToggleKey = 634 --[[ImGuiKey.NavGamepadMenu]]
g.NavWindowingInputSource = 3 --[[ImGuiInputSource.Gamepad]]
g.NavInputSource = 3 --[[ImGuiInputSource.Gamepad]]
end
if start_windowing_with_gamepad or start_windowing_with_keyboard then
local window = (g.NavWindow and ImGui.IsWindowNavFocusable(g.NavWindow)) and g.NavWindow or ImGui.FindWindowNavFocusable(g.WindowsFocusOrder.Size, 0, -1)
if window then
if start_windowing_with_keyboard or g.ConfigNavWindowingWithGamepad then
g.NavWindowingTarget = window.RootWindow
g.NavWindowingTargetAnim = window.RootWindow -- Current location
end
g.NavWindowingTimer = 0.0
g.NavWindowingHighlightAlpha = 0.0
ImVec2_CopyV(g.NavWindowingAccumDeltaPos, 0.0, 0.0)
ImVec2_CopyV(g.NavWindowingAccumDeltaSize, 0.0, 0.0)
g.NavWindowingInputSource = start_windowing_with_keyboard and 2 --[[ImGuiInputSource.Keyboard]] or 3 --[[ImGuiInputSource.Gamepad]]
g.NavInputSource = g.NavWindowingInputSource
if g.NavWindow == nil then
just_started_windowing_from_null_focus = true
end
-- Manually register ownership of our mods. Using a global route in the Shortcut() calls instead would probably be correct but may have more side-effects.
if keyboard_next_window or keyboard_prev_window then
ImGui.SetKeyOwnersForKeyChord(bit32.band(bit32.bor(g.ConfigNavWindowingKeyNext, g.ConfigNavWindowingKeyPrev), 61440 --[[ImGuiMod_Mask_]]), owner_id)
end
end
end
-- Gamepad update
if (g.NavWindowingTarget or g.NavWindowingToggleLayer) and g.NavWindowingInputSource == 3 --[[ImGuiInputSource.Gamepad]] then
if g.NavWindowingTarget ~= nil then
-- Highlight only appears after a brief time holding the button, so that a fast tap on ImGuiKey.NavGamepadMenu (to toggle NavLayer) doesn't add visual noise
-- However inputs are accepted immediately, so you press ImGuiKey.NavGamepadMenu + L1/R1 fast.
g.NavWindowingTimer = g.NavWindowingTimer + io.DeltaTime
g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - 0.5 --[[NAV_WINDOWING_HIGHLIGHT_DELAY]]) / 0.05))
-- Select window to focus
local focus_change_dir = (ImGui.IsKeyPressedEx(642 --[[ImGuiKey.GamepadL1]], 0 --[[ImGuiInputFlags.None]], 0 --[[ImGuiKeyOwner_Any]]) and 1 or 0) - (ImGui.IsKeyPressedEx(643 --[[ImGuiKey.GamepadR1]], 0 --[[ImGuiInputFlags.None]], 0 --[[ImGuiKeyOwner_Any]]) and 1 or 0)
if focus_change_dir ~= 0 and not just_started_windowing_from_null_focus then
ImGui.NavUpdateWindowingTarget(focus_change_dir)
g.NavWindowingHighlightAlpha = 1.0
end
end
-- Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
if not ImGui.IsKeyDown(634 --[[ImGuiKey.NavGamepadMenu]]) then
g.NavWindowingToggleLayer = g.NavWindowingToggleLayer and (g.NavWindowingHighlightAlpha < 1.0) -- Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
if g.NavWindowingToggleLayer and g.NavWindow then
apply_toggle_layer = true
elseif not g.NavWindowingToggleLayer then
apply_focus_window = g.NavWindowingTarget
end
g.NavWindowingTarget = nil
g.NavWindowingToggleLayer = false
end
end
-- Keyboard: Focus
if g.NavWindowingTarget and g.NavWindowingInputSource == 2 --[[ImGuiInputSource.Keyboard]] then
-- Visuals only appears after a brief time after pressing TAB the first time, so that a fast Ctrl+Tab doesn't add visual noise
local shared_mods = bit32.band(
bit32.band((g.ConfigNavWindowingKeyNext ~= 0) and g.ConfigNavWindowingKeyNext or 61440 --[[ImGuiMod_Mask_]], (g.ConfigNavWindowingKeyPrev ~= 0) and g.ConfigNavWindowingKeyPrev or 61440 --[[ImGuiMod_Mask_]]),
61440 --[[ImGuiMod_Mask_]])
assert(shared_mods ~= 0) -- Next/Prev shortcut currently needs a shared modifier to "hold", otherwise Prev actions would keep cycling between two windows.
g.NavWindowingTimer = g.NavWindowingTimer + io.DeltaTime
g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - 0.5 --[[NAV_WINDOWING_HIGHLIGHT_DELAY]]) / 0.05)) -- 1.0f
if (keyboard_next_window or keyboard_prev_window) and not just_started_windowing_from_null_focus then
ImGui.NavUpdateWindowingTarget(keyboard_next_window and -1 or 1)
elseif bit32.band(io.KeyMods, shared_mods) ~= shared_mods then
apply_focus_window = g.NavWindowingTarget
end
end
-- Keyboard: Press and Release Alt to toggle menu layer
local windowing_toggle_keys = { 529 --[[ImGuiKey.LeftAlt]], 533 --[[ImGuiKey.RightAlt]] }
local windowing_toggle_layer_start = false
if g.NavWindow and bit32.band(g.NavWindow.Flags, 65536 --[[ImGuiWindowFlags.NoNavInputs]]) == 0 then
for _, windowing_toggle_key in ipairs(windowing_toggle_keys) do
if nav_keyboard_active and ImGui.IsKeyPressedEx(windowing_toggle_key, 0 --[[ImGuiInputFlags.None]], 4294967295 --[[ImGuiKeyOwner_NoOwner]]) then
windowing_toggle_layer_start = true
g.NavWindowingToggleLayer = true
g.NavWindowingToggleKey = windowing_toggle_key
g.NavWindowingInputSource = 2 --[[ImGuiInputSource.Keyboard]]
g.NavInputSource = 2 --[[ImGuiInputSource.Keyboard]]
break
end
end
end
if g.NavWindowingToggleLayer and g.NavWindowingInputSource == 2 --[[ImGuiInputSource.Keyboard]] then
-- We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370)
-- We cancel toggling nav layer when other modifiers are pressed. (See #4439)
-- - AltGR is Alt+Ctrl on some layout but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl).
-- We cancel toggling nav layer if an owner has claimed the key.
if io.InputQueueCharacters.Size > 0 or io.KeyCtrl or io.KeyShift or io.KeySuper then
g.NavWindowingToggleLayer = false
elseif windowing_toggle_layer_start == false and g.LastKeyboardKeyPressTime == g.Time then
g.NavWindowingToggleLayer = false
elseif ImGui.TestKeyOwner(g.NavWindowingToggleKey, 4294967295 --[[ImGuiKeyOwner_NoOwner]]) == false or ImGui.TestKeyOwner(16384 --[[ImGuiMod_Alt]], 4294967295 --[[ImGuiKeyOwner_NoOwner]]) == false then
g.NavWindowingToggleLayer = false
end
-- Apply layer toggle on Alt release
-- Important: as before version <18314 we lacked an explicit IO event for focus gain/loss, we also compare mouse validity to detect old backends clearing mouse pos on focus loss.
if ImGui.IsKeyReleased(g.NavWindowingToggleKey) and g.NavWindowingToggleLayer then
if g.ActiveId == 0 or g.ActiveIdAllowOverlap then
if ImGui.IsMousePosValid(io.MousePos) == ImGui.IsMousePosValid(io.MousePosPrev) then
apply_toggle_layer = true
end
end
end
if not ImGui.IsKeyDown(g.NavWindowingToggleKey) then
g.NavWindowingToggleLayer = false
end
end
-- Move window
if g.NavWindowingTarget and bit32.band(g.NavWindowingTarget.Flags, 4 --[[ImGuiWindowFlags.NoMove]]) == 0 then
local nav_move_dir
if g.NavInputSource == 2 --[[ImGuiInputSource.Keyboard]] and not io.KeyShift then
nav_move_dir = ImGui.GetKeyMagnitude2d(513 --[[ImGuiKey.LeftArrow]], 514 --[[ImGuiKey.RightArrow]], 515 --[[ImGuiKey.UpArrow]], 516 --[[ImGuiKey.DownArrow]])
end
if g.NavInputSource == 3 --[[ImGuiInputSource.Gamepad]] then
nav_move_dir = ImGui.GetKeyMagnitude2d(648 --[[ImGuiKey.GamepadLStickLeft]], 649 --[[ImGuiKey.GamepadLStickRight]], 650 --[[ImGuiKey.GamepadLStickUp]], 651 --[[ImGuiKey.GamepadLStickDown]])
end
if nav_move_dir and (nav_move_dir.x ~= 0.0 or nav_move_dir.y ~= 0.0) then
local NAV_MOVE_SPEED = 800.0
local move_step = NAV_MOVE_SPEED * io.DeltaTime * ImGui.GetScale()
ImVec2_Copy(g.NavWindowingAccumDeltaPos, g.NavWindowingAccumDeltaPos + nav_move_dir * move_step)
g.NavHighlightItemUnderNav = true
local accum_floored = ImTrunc(g.NavWindowingAccumDeltaPos)
if accum_floored.x ~= 0.0 or accum_floored.y ~= 0.0 then
local moving_window = g.NavWindowingTarget.RootWindowDockTree
ImGui.SetWindowPos(moving_window, moving_window.Pos + accum_floored, 1 --[[ImGuiCond.Always]])
ImVec2_Copy(g.NavWindowingAccumDeltaPos, g.NavWindowingAccumDeltaPos - accum_floored)
end
end
end
-- Apply final focus
if apply_focus_window then
ImGui.NavUpdateWindowingApplyFocus(apply_focus_window)
end
-- Apply menu/layer toggle
if apply_toggle_layer and g.NavWindow then
ImGui.ClearActiveID()
-- Move to parent menu if necessary
local new_nav_window = g.NavWindow
while new_nav_window.ParentWindow
and bit32.band(new_nav_window.DC.NavLayersActiveMask, bit32.lshift(1, 1 --[[ImGuiNavLayer.Menu]])) == 0
and bit32.band(new_nav_window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0
and bit32.band(new_nav_window.Flags, bit32.bor(67108864 --[[ImGuiWindowFlags.Popup]], 268435456 --[[ImGuiWindowFlags.ChildMenu]])) == 0 do
new_nav_window = new_nav_window.ParentWindow
end
if new_nav_window ~= g.NavWindow then
local old_nav_window = g.NavWindow
ImGui.FocusWindow(new_nav_window)
new_nav_window.NavLastChildNavWindow = old_nav_window
end
-- Toggle layer
local new_nav_layer = (bit32.band(g.NavWindow.DC.NavLayersActiveMask, bit32.lshift(1, 1 --[[ImGuiNavLayer.Menu]])) ~= 0) and bit32.bxor(g.NavLayer, 1) or 0 --[[ImGuiNavLayer.Main]]
if new_nav_layer ~= g.NavLayer then
-- Reinitialize navigation when entering menu bar with the Alt key (FIXME: could be a properly of the layer?)
local preserve_layer_1_nav_id = (new_nav_window.DockNodeAsHost ~= nil)
if new_nav_layer == 1 --[[ImGuiNavLayer.Menu]] and not preserve_layer_1_nav_id then
g.NavWindow.NavLastIds[new_nav_layer] = 0
end
ImGui.NavRestoreLayer(new_nav_layer)
ImGui.SetNavCursorVisibleAfterMove()
end
end
end
function ImGui.NavUpdateContextMenuRequest()
local g = GImGui
local io = g.IO
g.NavOpenContextMenuItemId = 0
g.NavOpenContextMenuWindowId = 0
local nav_keyboard_active = bit32.band(io.ConfigFlags, 1 --[[ImGuiConfigFlags.NavEnableKeyboard]]) ~= 0
local nav_gamepad_active = bit32.band(io.ConfigFlags, 2 --[[ImGuiConfigFlags.NavEnableGamepad]]) ~= 0
if (not nav_keyboard_active and not nav_gamepad_active) or g.NavWindow == nil then
return
end
local request = false
request = request or (nav_keyboard_active and (ImGui.IsKeyReleased(535 --[[ImGuiKey.Menu]], 4294967295 --[[ImGuiKeyOwner_NoOwner]]) or (ImGui.IsKeyPressedEx(581 --[[ImGuiKey.F10]], 0 --[[ImGuiInputFlags.None]], 4294967295 --[[ImGuiKeyOwner_NoOwner]]) and io.KeyMods == 8192 --[[ImGuiMod_Shift]])))
request = request or (nav_gamepad_active and ImGui.IsKeyPressedEx(636 --[[ImGuiKey.NavGamepadContextMenu]], 0 --[[ImGuiInputFlags.None]], 4294967295 --[[ImGuiKeyOwner_NoOwner]]))
if not request then
return
end
g.NavOpenContextMenuItemId = g.NavId
g.NavOpenContextMenuWindowId = g.NavWindow.ID
-- Allow triggering for Begin()..BeginPopupContextItem(). A possible alternative would be to use g.NavLayer == ImGuiNavLayer.Menu.
if g.NavId == g.NavWindow:GetID("#CLOSE") or g.NavId == g.NavWindow:GetID("#COLLAPSE") then
g.NavOpenContextMenuItemId = g.NavWindow.MoveId
end
g.NavInputSource = 2 --[[ImGuiInputSource.Keyboard]]
ImGui.SetNavCursorVisibleAfterMove()
end
-- FIXME: ScoringRect is not set
--- @param move_dir ImGuiDir
--- @param clip_dir ImGuiDir
--- @param move_flags ImGuiNavMoveFlags
--- @param scroll_flags ImGuiScrollFlags
function ImGui.NavMoveRequestSubmit(move_dir, clip_dir, move_flags, scroll_flags)
local g = GImGui
assert(g.NavWindow ~= nil)
-- IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestSubmit: dir %c, window \"%s\"\n", "-WENS"[move_dir + 1], g.NavWindow->Name)
if bit32.band(move_flags, 1024 --[[ImGuiNavMoveFlags.IsTabbing]]) ~= 0 then
move_flags = bit32.bor(move_flags, 16 --[[ImGuiNavMoveFlags.AllowCurrentNavId]])
end
g.NavMoveSubmitted = true; g.NavMoveScoringItems = true
g.NavMoveDir = move_dir
g.NavMoveDirForDebug = move_dir
g.NavMoveClipDir = clip_dir
g.NavMoveFlags = move_flags
g.NavMoveScrollFlags = scroll_flags
g.NavMoveForwardToNextFrame = false
g.NavMoveKeyMods = (bit32.band(move_flags, 512 --[[ImGuiNavMoveFlags.FocusApi]]) ~= 0) and 0 or g.IO.KeyMods
g.NavMoveResultLocal:Clear()
g.NavMoveResultLocalVisible:Clear()
g.NavMoveResultOther:Clear()
g.NavTabbingCounter = 0
g.NavTabbingResultFirst:Clear()
ImGui.NavUpdateAnyRequestFlag()
end
-- Bias scoring rect ahead of scoring + update preferred pos (if missing) using source position
--- @param r ImRect
--- @param preferred_pos_rel ImVec2
--- @param move_dir ImGuiDir
--- @param move_flags ImGuiNavMoveFlags
function ImGui.NavBiasScoringRect(r, preferred_pos_rel, move_dir, move_flags)
-- Bias initial rect
local g = GImGui
local rel_to_abs_offset = g.NavWindow.DC.CursorStartPos
-- Initialize bias on departure if we don't have any. So mouse-click + arrow will record bias.
-- - We default to L/U bias, so moving down from a large source item into several columns will land on left-most column.
-- - But each successful move sets new bias on one axis, only cleared when using mouse.
if bit32.band(move_flags, 128 --[[ImGuiNavMoveFlags.Forwarded]]) == 0 then
if preferred_pos_rel.x == 3.4028234663853e+38 --[[FLT_MAX]] then
preferred_pos_rel.x = ImMin(r.Min.x + 1.0, r.Max.x) - rel_to_abs_offset.x
end
if preferred_pos_rel.y == 3.4028234663853e+38 --[[FLT_MAX]] then
preferred_pos_rel.y = r:GetCenter().y - rel_to_abs_offset.y
end
end
-- Apply general bias on the other axis
if (move_dir == 2 --[[ImGuiDir.Up]] or move_dir == 3 --[[ImGuiDir.Down]]) and preferred_pos_rel.x ~= 3.4028234663853e+38 --[[FLT_MAX]] then
local new_x = preferred_pos_rel.x + rel_to_abs_offset.x
r.Min.x = new_x; r.Max.x = new_x
elseif (move_dir == 0 --[[ImGuiDir.Left]] or move_dir == 1 --[[ImGuiDir.Right]]) and preferred_pos_rel.y ~= 3.4028234663853e+38 --[[FLT_MAX]] then
local new_y = preferred_pos_rel.y + rel_to_abs_offset.y
r.Min.y = new_y; r.Max.y = new_y
end
end
-- Handle PageUp/PageDown/Home/End keys
-- Called from NavUpdateCreateMoveRequest() which will use our output to create a move request
-- FIXME-NAV: This doesn't work properly with NavFlattened siblings as we use NavWindow rectangle for reference
-- FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid?
--- @return float
function ImGui.NavUpdatePageUpPageDown()
local g = GImGui
local window = g.NavWindow
if bit32.band(window.Flags, 65536 --[[ImGuiWindowFlags.NoNavInputs]]) ~= 0 or g.NavWindowingTarget ~= nil then
return 0.0
end
local page_up_held = ImGui.IsKeyDown(517 --[[ImGuiKey.PageUp]], 4294967295 --[[ImGuiKeyOwner_NoOwner]])
local page_down_held = ImGui.IsKeyDown(518 --[[ImGuiKey.PageDown]], 4294967295 --[[ImGuiKeyOwner_NoOwner]])
local home_pressed = ImGui.IsKeyPressedEx(519 --[[ImGuiKey.Home]], 1 --[[ImGuiInputFlags.Repeat]], 4294967295 --[[ImGuiKeyOwner_NoOwner]])
local end_pressed = ImGui.IsKeyPressedEx(520 --[[ImGuiKey.End]], 1 --[[ImGuiInputFlags.Repeat]], 4294967295 --[[ImGuiKeyOwner_NoOwner]])
if page_up_held == page_down_held and home_pressed == end_pressed then -- Proceed if either (not both) are pressed, otherwise early out
return 0.0
end
if g.NavLayer ~= 0 --[[ImGuiNavLayer.Main]] then
ImGui.NavRestoreLayer(0 --[[ImGuiNavLayer.Main]])
end
if bit32.band(window.DC.NavLayersActiveMask, bit32.lshift(1, 0 --[[ImGuiNavLayer.Main]])) == 0 and window.DC.NavWindowHasScrollY then
-- Fallback manual-scroll when window has no navigable item
if ImGui.IsKeyPressedEx(517 --[[ImGuiKey.PageUp]], 1 --[[ImGuiInputFlags.Repeat]], 4294967295 --[[ImGuiKeyOwner_NoOwner]]) then
ImGui.SetScrollY(window, window.Scroll.y - window.InnerRect:GetHeight())
elseif ImGui.IsKeyPressedEx(518 --[[ImGuiKey.PageDown]], 1 --[[ImGuiInputFlags.Repeat]], 4294967295 --[[ImGuiKeyOwner_NoOwner]]) then
ImGui.SetScrollY(window, window.Scroll.y + window.InnerRect:GetHeight())
elseif home_pressed then
ImGui.SetScrollY(window, 0.0)
elseif end_pressed then
ImGui.SetScrollY(window, window.ScrollMax.y)
end
else
local nav_rect_rel = window.NavRectRel[g.NavLayer]
local page_offset_y = ImMax(0.0, window.InnerRect:GetHeight() - window.FontRefSize * 1.0 + nav_rect_rel:GetHeight())
local nav_scoring_rect_offset_y = 0.0
if ImGui.IsKeyPressedEx(517 --[[ImGuiKey.PageUp]], 1 --[[ImGuiInputFlags.Repeat]], 4294967295 --[[ImGuiKeyOwner_NoOwner]]) then
nav_scoring_rect_offset_y = -page_offset_y
g.NavMoveDir = 3 --[[ImGuiDir.Down]] -- Because our scoring rect is offset up, we request the down direction (so we can always land on the last item)
g.NavMoveClipDir = 2 --[[ImGuiDir.Up]]
g.NavMoveFlags = bit32.bor(16 --[[ImGuiNavMoveFlags.AllowCurrentNavId]], 2048 --[[ImGuiNavMoveFlags.IsPageMove]]) -- ImGuiNavMoveFlags.AlsoScoreVisibleSet may be added later
elseif ImGui.IsKeyPressedEx(518 --[[ImGuiKey.PageDown]], 1 --[[ImGuiInputFlags.Repeat]], 4294967295 --[[ImGuiKeyOwner_NoOwner]]) then
nav_scoring_rect_offset_y = page_offset_y
g.NavMoveDir = 2 --[[ImGuiDir.Up]] -- Because our scoring rect is offset down, we request the up direction (so we can always land on the last item)
g.NavMoveClipDir = 3 --[[ImGuiDir.Down]]
g.NavMoveFlags = bit32.bor(16 --[[ImGuiNavMoveFlags.AllowCurrentNavId]], 2048 --[[ImGuiNavMoveFlags.IsPageMove]]) -- ImGuiNavMoveFlags.AlsoScoreVisibleSet may be added later
elseif home_pressed then
-- FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
-- Scrolling will be handled via the ImGuiNavMoveFlags.ScrollToEdgeY flag, we don't scroll immediately to avoid scrolling happening before nav result.
-- Preserve current horizontal position if we have any.
nav_rect_rel.Min.y = 0.0; nav_rect_rel.Max.y = 0.0
if nav_rect_rel:IsInverted() then
nav_rect_rel.Min.x = 0.0; nav_rect_rel.Max.x = 0.0
end
g.NavMoveDir = 3 --[[ImGuiDir.Down]]
g.NavMoveFlags = bit32.bor(16 --[[ImGuiNavMoveFlags.AllowCurrentNavId]], 64 --[[ImGuiNavMoveFlags.ScrollToEdgeY]])
-- FIXME-NAV: MoveClipDir left to _None, intentional?
elseif end_pressed then
nav_rect_rel.Min.y = window.ContentSize.y; nav_rect_rel.Max.y = window.ContentSize.y
if nav_rect_rel:IsInverted() then
nav_rect_rel.Min.x = 0.0; nav_rect_rel.Max.x = 0.0
end
g.NavMoveDir = 2 --[[ImGuiDir.Up]]
g.NavMoveFlags = bit32.bor(16 --[[ImGuiNavMoveFlags.AllowCurrentNavId]], 64 --[[ImGuiNavMoveFlags.ScrollToEdgeY]])
-- FIXME-NAV: MoveClipDir left to _None, intentional?
end
return nav_scoring_rect_offset_y
end
return 0.0
end
function ImGui.NavUpdateCreateMoveRequest()
local g = GImGui
local io = g.IO
local window = g.NavWindow
local nav_gamepad_active = bit32.band(io.ConfigFlags, 2 --[[ImGuiConfigFlags.NavEnableGamepad]]) ~= 0 and bit32.band(io.BackendFlags, 1 --[[ImGuiBackendFlags.HasGamepad]]) ~= 0
local nav_keyboard_active = bit32.band(io.ConfigFlags, 1 --[[ImGuiConfigFlags.NavEnableKeyboard]]) ~= 0
if g.NavMoveForwardToNextFrame and window ~= nil then
-- Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window)
-- (preserve most state, which were already set by the NavMoveRequestForward() function)
assert(g.NavMoveDir ~= (-1) --[[ImGuiDir.None]] and g.NavMoveClipDir ~= (-1) --[[ImGuiDir.None]])
assert(bit32.band(g.NavMoveFlags, 128 --[[ImGuiNavMoveFlags.Forwarded]]) ~= 0)
-- IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir)
else
-- Initiate directional inputs request
g.NavMoveDir = (-1) --[[ImGuiDir.None]]
g.NavMoveFlags = 0 --[[ImGuiNavMoveFlags.None]]
g.NavMoveScrollFlags = 0 --[[ImGuiScrollFlags.None]]
if window and not g.NavWindowingTarget and bit32.band(window.Flags, 65536 --[[ImGuiWindowFlags.NoNavInputs]]) == 0 then
local repeat_mode = bit32.bor(1 --[[ImGuiInputFlags.Repeat]], 4 --[[ImGuiInputFlags.RepeatRateNavMove]])
if not ImGui.IsActiveIdUsingNavDir(0 --[[ImGuiDir.Left]]) and ((nav_gamepad_active and ImGui.IsKeyPressedEx(638 --[[ImGuiKey.GamepadDpadLeft]], repeat_mode, 4294967295 --[[ImGuiKeyOwner_NoOwner]])) or (nav_keyboard_active and ImGui.IsKeyPressedEx(513 --[[ImGuiKey.LeftArrow]], repeat_mode, 4294967295 --[[ImGuiKeyOwner_NoOwner]]))) then g.NavMoveDir = 0 --[[ImGuiDir.Left]] end
if not ImGui.IsActiveIdUsingNavDir(1 --[[ImGuiDir.Right]]) and ((nav_gamepad_active and ImGui.IsKeyPressedEx(639 --[[ImGuiKey.GamepadDpadRight]], repeat_mode, 4294967295 --[[ImGuiKeyOwner_NoOwner]])) or (nav_keyboard_active and ImGui.IsKeyPressedEx(514 --[[ImGuiKey.RightArrow]], repeat_mode, 4294967295 --[[ImGuiKeyOwner_NoOwner]]))) then g.NavMoveDir = 1 --[[ImGuiDir.Right]] end
if not ImGui.IsActiveIdUsingNavDir(2 --[[ImGuiDir.Up]]) and ((nav_gamepad_active and ImGui.IsKeyPressedEx(640 --[[ImGuiKey.GamepadDpadUp]], repeat_mode, 4294967295 --[[ImGuiKeyOwner_NoOwner]])) or (nav_keyboard_active and ImGui.IsKeyPressedEx(515 --[[ImGuiKey.UpArrow]], repeat_mode, 4294967295 --[[ImGuiKeyOwner_NoOwner]]))) then g.NavMoveDir = 2 --[[ImGuiDir.Up]] end
if not ImGui.IsActiveIdUsingNavDir(3 --[[ImGuiDir.Down]]) and ((nav_gamepad_active and ImGui.IsKeyPressedEx(641 --[[ImGuiKey.GamepadDpadDown]], repeat_mode, 4294967295 --[[ImGuiKeyOwner_NoOwner]])) or (nav_keyboard_active and ImGui.IsKeyPressedEx(516 --[[ImGuiKey.DownArrow]], repeat_mode, 4294967295 --[[ImGuiKeyOwner_NoOwner]]))) then g.NavMoveDir = 3 --[[ImGuiDir.Down]] end
end
g.NavMoveClipDir = g.NavMoveDir
ImRect_Copy(g.NavScoringNoClipRect, ImRect(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]], -3.4028234663853e+38 --[[FLT_MAX]], -3.4028234663853e+38 --[[FLT_MAX]]))
end
-- Update PageUp/PageDown/Home/End scroll
-- FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags.NavEnableKeyboard flag?
local scoring_page_offset_y = 0.0
if window and g.NavMoveDir == (-1) --[[ImGuiDir.None]] and nav_keyboard_active then
scoring_page_offset_y = ImGui.NavUpdatePageUpPageDown()
end
-- Submit
g.NavMoveForwardToNextFrame = false
if g.NavMoveDir ~= (-1) --[[ImGuiDir.None]] then
ImGui.NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags)
end
-- Moving with no reference triggers an init request (will be used as a fallback if the direction fails to find a match)
if g.NavMoveSubmitted and g.NavId == 0 then
-- IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", window ? window->Name : "<NULL>", g.NavLayer)
g.NavInitRequest = true; g.NavInitRequestFromMove = true
g.NavInitResult.ID = 0
if g.IO.ConfigNavCursorVisibleAuto then -- NO check for _NoNavInputs here as we assume MoveRequests cannot be created.
g.NavCursorVisible = true
end
end
-- When using gamepad, we project the reference nav bounding box into window visible area.
-- This is to allow resuming navigation inside the visible area after doing a large amount of scrolling,
-- since with gamepad all movements are relative (can't focus a visible object like we can with the mouse).
if g.NavMoveSubmitted and g.NavInputSource == 3 --[[ImGuiInputSource.Gamepad]] and g.NavLayer == 0 --[[ImGuiNavLayer.Main]] and window ~= nil then -- and (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded))
local clamp_x = bit32.band(g.NavMoveFlags, bit32.bor(1 --[[ImGuiNavMoveFlags.LoopX]], 4 --[[ImGuiNavMoveFlags.WrapX]])) == 0
local clamp_y = bit32.band(g.NavMoveFlags, bit32.bor(2 --[[ImGuiNavMoveFlags.LoopY]], 8 --[[ImGuiNavMoveFlags.WrapY]])) == 0
local inner_rect_rel = ImGui.WindowRectAbsToRel(window, ImRect(ImVec2(window.InnerRect.Min.x - 1, window.InnerRect.Min.y - 1), ImVec2(window.InnerRect.Max.x + 1, window.InnerRect.Max.y + 1)))
-- Take account of changing scroll to handle triggering a new move request on a scrolling frame. (#6171)
-- Otherwise 'inner_rect_rel' would be off on the move result frame.
local next_scroll = CalcNextScrollFromScrollTargetAndClamp(window)
inner_rect_rel:Translate(ImVec2(next_scroll.x - window.Scroll.x, next_scroll.y - window.Scroll.y))
if (clamp_x or clamp_y) and not inner_rect_rel:Contains(window.NavRectRel[g.NavLayer]) then
-- IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel for gamepad move\n")
local pad_x = ImMin(inner_rect_rel:GetWidth(), window.FontRefSize * 0.5)
local pad_y = ImMin(inner_rect_rel:GetHeight(), window.FontRefSize * 0.5) -- Terrible approximation for the intent of starting navigation from first fully visible item
inner_rect_rel.Min.x = clamp_x and (inner_rect_rel.Min.x + pad_x) or -3.4028234663853e+38 --[[FLT_MAX]]
inner_rect_rel.Max.x = clamp_x and (inner_rect_rel.Max.x - pad_x) or 3.4028234663853e+38 --[[FLT_MAX]]
inner_rect_rel.Min.y = clamp_y and (inner_rect_rel.Min.y + pad_y) or -3.4028234663853e+38 --[[FLT_MAX]]
inner_rect_rel.Max.y = clamp_y and (inner_rect_rel.Max.y - pad_y) or 3.4028234663853e+38 --[[FLT_MAX]]
window.NavRectRel[g.NavLayer]:ClipWithFull(inner_rect_rel)
g.NavId = 0
end
end
-- Prepare scoring rectangle.
-- For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items)
local scoring_rect = ImRect()
if window ~= nil then
local nav_rect_rel = ImRect()
if not window.NavRectRel[g.NavLayer]:IsInverted() then
ImRect_Copy(nav_rect_rel, window.NavRectRel[g.NavLayer])
end
scoring_rect = ImGui.WindowRectRelToAbs(window, nav_rect_rel)
if bit32.band(g.NavMoveFlags, 2048 --[[ImGuiNavMoveFlags.IsPageMove]]) ~= 0 then
-- When we start from a visible location, score visible items and prioritize this result.
if window.InnerRect:Contains(scoring_rect) then
g.NavMoveFlags = bit32.bor(g.NavMoveFlags, 32 --[[ImGuiNavMoveFlags.AlsoScoreVisibleSet]])
end
ImRect_Copy(g.NavScoringNoClipRect, scoring_rect)
scoring_rect:TranslateY(scoring_page_offset_y)
g.NavScoringNoClipRect:Add(scoring_rect)
end
-- ImGui.GetForegroundDrawList():AddRectFilled(scoring_rect.Min - ImVec2(1, 1), scoring_rect.Max + ImVec2(1, 1), IM_COL32(255, 100, 0, 80)) -- [DEBUG] Pre-bias
if g.NavMoveSubmitted then
ImGui.NavBiasScoringRect(scoring_rect, window.RootWindowForNav.NavPreferredScoringPosRel[g.NavLayer], g.NavMoveDir, g.NavMoveFlags)
end
assert(not scoring_rect:IsInverted()) -- Ensure we have a non-inverted bounding box here will allow us to remove extraneous ImFabs() calls in NavScoreItem().
-- ImGui.GetForegroundDrawList():AddRectFilled(scoring_rect.Min - ImVec2(1, 1), scoring_rect.Max + ImVec2(1, 1), IM_COL32(255, 100, 0, 80)) -- [DEBUG] Post-bias
-- if not g.NavScoringNoClipRect:IsInverted() then ImGui.GetForegroundDrawList():AddRectFilled(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(100, 255, 0, 80)) end -- [DEBUG]
end
ImRect_Copy(g.NavScoringRect, scoring_rect)
-- g.NavScoringNoClipRect:Add(scoring_rect)
end
function ImGui.NavUpdateCreateTabbingRequest()
local g = GImGui
local window = g.NavWindow
assert(g.NavMoveDir == (-1) --[[ImGuiDir.None]])
if window == nil or g.NavWindowingTarget ~= nil or bit32.band(window.Flags, 65536 --[[ImGuiWindowFlags.NoNavInputs]]) ~= 0 or not g.ConfigNavEnableTabbing then
return
end
local tab_pressed = ImGui.IsKeyPressedEx(512 --[[ImGuiKey.Tab]], 1 --[[ImGuiInputFlags.Repeat]], 4294967295 --[[ImGuiKeyOwner_NoOwner]]) and not g.IO.KeyCtrl and not g.IO.KeyAlt
if not tab_pressed then
return
end
-- Initiate tabbing request
-- (this is ALWAYS ENABLED, regardless of ImGuiConfigFlags.NavEnableKeyboard flag!)
-- See NavProcessItemForTabbingRequest() for a description of the various forward/backward tabbing cases with and without wrapping.
local nav_keyboard_active = bit32.band(g.IO.ConfigFlags, 1 --[[ImGuiConfigFlags.NavEnableKeyboard]]) ~= 0
if nav_keyboard_active then
g.NavTabbingDir = g.IO.KeyShift and -1 or ((g.NavCursorVisible == false and g.ActiveId == 0) and 0 or 1)
else
g.NavTabbingDir = g.IO.KeyShift and -1 or ((g.ActiveId == 0) and 0 or 1)
end
local move_flags = bit32.bor(1024 --[[ImGuiNavMoveFlags.IsTabbing]], 4096 --[[ImGuiNavMoveFlags.Activate]])
local scroll_flags = window.Appearing and bit32.bor(1 --[[ImGuiScrollFlags.KeepVisibleEdgeX]], 32 --[[ImGuiScrollFlags.AlwaysCenterY]]) or bit32.bor(1 --[[ImGuiScrollFlags.KeepVisibleEdgeX]], 2 --[[ImGuiScrollFlags.KeepVisibleEdgeY]])
local clip_dir = (g.NavTabbingDir < 0) and 2 --[[ImGuiDir.Up]] or 3 --[[ImGuiDir.Down]]
ImGui.NavMoveRequestSubmit((-1) --[[ImGuiDir.None]], clip_dir, move_flags, scroll_flags) -- FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
g.NavTabbingCounter = -1
end
function ImGui.NavUpdate()
local g = GImGui
local io = g.IO
io.WantSetMousePos = false
-- if g.NavScoringDebugCount > 0 then IMGUI_DEBUG_LOG_NAV("[nav] NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest) end
-- Set input source based on which keys are last pressed (as some features differs when used with Gamepad vs Keyboard)
-- FIXME-NAV: Now that keys are separated maybe we can get rid of NavInputSource?
local nav_gamepad_active = bit32.band(io.ConfigFlags, 2 --[[ImGuiConfigFlags.NavEnableGamepad]]) ~= 0 and bit32.band(io.BackendFlags, 1 --[[ImGuiBackendFlags.HasGamepad]]) ~= 0
if nav_gamepad_active and g.NavInputSource ~= 3 --[[ImGuiInputSource.Gamepad]] then
for _, key in ipairs(nav_gamepad_keys_to_change_source) do
if ImGui.IsKeyDown(key) then
g.NavInputSource = 3 --[[ImGuiInputSource.Gamepad]]
end
end
end
local nav_keyboard_active = bit32.band(io.ConfigFlags, 1 --[[ImGuiConfigFlags.NavEnableKeyboard]]) ~= 0
if nav_keyboard_active and g.NavInputSource ~= 2 --[[ImGuiInputSource.Keyboard]] then
for _, key in ipairs(nav_keyboard_keys_to_change_source) do
if ImGui.IsKeyDown(key) then
g.NavInputSource = 2 --[[ImGuiInputSource.Keyboard]]
end
end
end
-- Process navigation init request (select first/default focus)
g.NavJustMovedToId = 0
g.NavJustMovedToFocusScopeId = 0
g.NavJustMovedFromFocusScopeId = 0
if g.NavInitResult.ID ~= 0 then
ImGui.NavInitRequestApplyResult()
end
g.NavInitRequest = false
g.NavInitRequestFromMove = false
g.NavInitResult.ID = 0
-- Process navigation move request
if g.NavMoveSubmitted then
ImGui.NavMoveRequestApplyResult()
end
g.NavTabbingCounter = 0
g.NavMoveSubmitted = false
g.NavMoveScoringItems = false
if g.NavCursorHideFrames > 0 then
g.NavCursorHideFrames = g.NavCursorHideFrames - 1
if g.NavCursorHideFrames == 0 then
g.NavCursorVisible = true
end
end
-- Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling)
local set_mouse_pos = false
if g.NavMousePosDirty and g.NavIdIsAlive then
if g.NavCursorVisible and g.NavHighlightItemUnderNav and g.NavWindow then
set_mouse_pos = true
end
end
g.NavMousePosDirty = false
assert(g.NavLayer == 0 --[[ImGuiNavLayer.Main]] or g.NavLayer == 1 --[[ImGuiNavLayer.Menu]])
-- Store our return window (for returning from Menu Layer to Main Layer) and clear it as soon as we step back in our own Layer 0
if g.NavWindow then
ImGui.NavSaveLastChildNavWindowIntoParent(g.NavWindow)
end
if g.NavWindow and g.NavWindow.NavLastChildNavWindow ~= nil and g.NavLayer == 0 --[[ImGuiNavLayer.Main]] then
g.NavWindow.NavLastChildNavWindow = nil
end
-- Update Ctrl+Tab and Windowing features (hold Square to move/resize/etc.)
ImGui.NavUpdateWindowing()
-- Set output flags for user application
io.NavActive = (nav_keyboard_active or nav_gamepad_active) and g.NavWindow and bit32.band(g.NavWindow.Flags, 65536 --[[ImGuiWindowFlags.NoNavInputs]]) == 0
io.NavVisible = (io.NavActive and g.NavId ~= 0 and g.NavCursorVisible) or (g.NavWindowingTarget ~= nil)
-- Process NavCancel input (to close a popup, get back to parent, clear focus)
ImGui.NavUpdateCancelRequest()
ImGui.NavUpdateContextMenuRequest()
-- Process manual activation request
g.NavActivateId = 0
g.NavActivateDownId = 0
g.NavActivatePressedId = 0
g.NavActivateFlags = 0 --[[ImGuiActivateFlags.None]]
if g.NavId ~= 0 and g.NavCursorVisible and not g.NavWindowingTarget and g.NavWindow and bit32.band(g.NavWindow.Flags, 65536 --[[ImGuiWindowFlags.NoNavInputs]]) == 0 then
local activate_down = (nav_keyboard_active and ImGui.IsKeyDown(524 --[[ImGuiKey.Space]], 4294967295 --[[ImGuiKeyOwner_NoOwner]])) or (nav_gamepad_active and ImGui.IsKeyDown(ImGuiKey.NavGamepadActivate, 4294967295 --[[ImGuiKeyOwner_NoOwner]]))
local activate_pressed = activate_down and ((nav_keyboard_active and ImGui.IsKeyPressedEx(524 --[[ImGuiKey.Space]], 0 --[[ImGuiInputFlags.None]], 4294967295 --[[ImGuiKeyOwner_NoOwner]])) or (nav_gamepad_active and ImGui.IsKeyPressedEx(ImGuiKey.NavGamepadActivate, 0 --[[ImGuiInputFlags.None]], 4294967295 --[[ImGuiKeyOwner_NoOwner]])))
local input_pressed_keyboard = nav_keyboard_active and (ImGui.IsKeyPressedEx(525 --[[ImGuiKey.Enter]], 0 --[[ImGuiInputFlags.None]], 4294967295 --[[ImGuiKeyOwner_NoOwner]]) or ImGui.IsKeyPressedEx(627 --[[ImGuiKey.KeypadEnter]], 0 --[[ImGuiInputFlags.None]], 4294967295 --[[ImGuiKeyOwner_NoOwner]]))
local input_pressed_gamepad = false
if activate_down and nav_gamepad_active and ImGui.IsKeyDown(ImGuiKey.NavGamepadActivate, 4294967295 --[[ImGuiKeyOwner_NoOwner]]) and bit32.band(g.NavIdItemFlags, 1048576 --[[ImGuiItemFlags.Inputable]]) ~= 0 then -- requires ImGuiItemFlags.Inputable to avoid retriggering regular buttons.
if ImGui.GetKeyData(g, ImGuiKey.NavGamepadActivate).DownDurationPrev < 0.6 --[[NAV_ACTIVATE_INPUT_WITH_GAMEPAD_DELAY]] and ImGui.GetKeyData(g, ImGuiKey.NavGamepadActivate).DownDuration >= 0.6 --[[NAV_ACTIVATE_INPUT_WITH_GAMEPAD_DELAY]] then
input_pressed_gamepad = true
end
end
if g.ActiveId == 0 and activate_pressed then
g.NavActivateId = g.NavId
g.NavActivateFlags = 2 --[[ImGuiActivateFlags.PreferTweak]]
end
if (g.ActiveId == 0 or g.ActiveId == g.NavId) and (input_pressed_keyboard or input_pressed_gamepad) then
g.NavActivateId = g.NavId
g.NavActivateFlags = 1 --[[ImGuiActivateFlags.PreferInput]]
end
if (g.ActiveId == 0 or g.ActiveId == g.NavId) and (activate_down or input_pressed_keyboard or input_pressed_gamepad) then -- FIXME-NAV: Unsure why input_pressed_xxx (migrated from input_down which was already dubious)
g.NavActivateDownId = g.NavId
end
if (g.ActiveId == 0 or g.ActiveId == g.NavId) and (activate_pressed or input_pressed_keyboard or input_pressed_gamepad) then
g.NavActivatePressedId = g.NavId
ImGui.NavHighlightActivated(g.NavId)
end
end
if g.NavWindow and bit32.band(g.NavWindow.Flags, 65536 --[[ImGuiWindowFlags.NoNavInputs]]) ~= 0 then
g.NavCursorVisible = false
elseif g.IO.ConfigNavCursorVisibleAlways and g.NavCursorHideFrames == 0 then
g.NavCursorVisible = true
end
if g.NavActivateId ~= 0 then
assert(g.NavActivateDownId == g.NavActivateId)
end
-- Highlight
if g.NavHighlightActivatedTimer > 0.0 then
g.NavHighlightActivatedTimer = ImMax(0.0, g.NavHighlightActivatedTimer - io.DeltaTime)
end
if g.NavHighlightActivatedTimer == 0.0 then
g.NavHighlightActivatedId = 0
end
-- Process programmatic activation request
-- FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others)
if g.NavNextActivateId ~= 0 then
g.NavActivateId = g.NavNextActivateId
g.NavActivateDownId = g.NavNextActivateId
g.NavActivatePressedId = g.NavNextActivateId
g.NavActivateFlags = g.NavNextActivateFlags
end
g.NavNextActivateId = 0
-- Process move requests
ImGui.NavUpdateCreateMoveRequest()
if g.NavMoveDir == (-1) --[[ImGuiDir.None]] then
ImGui.NavUpdateCreateTabbingRequest()
end
ImGui.NavUpdateAnyRequestFlag()
g.NavIdIsAlive = false
-- Scrolling
if g.NavWindow and bit32.band(g.NavWindow.Flags, 65536 --[[ImGuiWindowFlags.NoNavInputs]]) == 0 and not g.NavWindowingTarget then
-- *Fallback* manual-scroll with Nav directional keys when window has no navigable item
local window = g.NavWindow
local scroll_speed = IM_ROUND(window.FontRefSize * 100 * io.DeltaTime) -- We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
local move_dir = g.NavMoveDir
if window.DC.NavLayersActiveMask == 0x00 and window.DC.NavWindowHasScrollY and move_dir ~= (-1) --[[ImGuiDir.None]] then
if move_dir == 0 --[[ImGuiDir.Left]] or move_dir == 1 --[[ImGuiDir.Right]] then
local delta = (move_dir == 0 --[[ImGuiDir.Left]]) and -1.0 or 1.0
ImGui.SetScrollX(window, ImTrunc(window.Scroll.x + delta * scroll_speed))
end
if move_dir == 2 --[[ImGuiDir.Up]] or move_dir == 3 --[[ImGuiDir.Down]] then
local delta = (move_dir == 2 --[[ImGuiDir.Up]]) and -1.0 or 1.0
ImGui.SetScrollY(window, ImTrunc(window.Scroll.y + delta * scroll_speed))
end
end
-- *Normal* Manual scroll with LStick
-- Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
if nav_gamepad_active then
local scroll_dir = ImGui.GetKeyMagnitude2d(648 --[[ImGuiKey.GamepadLStickLeft]], 649 --[[ImGuiKey.GamepadLStickRight]], 650 --[[ImGuiKey.GamepadLStickUp]], 651 --[[ImGuiKey.GamepadLStickDown]])
local tweak_factor = 1.0
if ImGui.IsKeyDown(642 --[[ImGuiKey.NavGamepadTweakSlow]]) then
tweak_factor = 1.0 / 10.0
elseif ImGui.IsKeyDown(643 --[[ImGuiKey.NavGamepadTweakFast]]) then
tweak_factor = 10.0
end
if scroll_dir.x ~= 0.0 and window.ScrollbarX then
ImGui.SetScrollX(window, ImTrunc(window.Scroll.x + scroll_dir.x * scroll_speed * tweak_factor))
end
if scroll_dir.y ~= 0.0 then
ImGui.SetScrollY(window, ImTrunc(window.Scroll.y + scroll_dir.y * scroll_speed * tweak_factor))
end
end
end
-- Always prioritize mouse highlight if navigation is disabled
if not nav_keyboard_active and not nav_gamepad_active then
g.NavCursorVisible = false
g.NavHighlightItemUnderNav = false
set_mouse_pos = false
end
-- Update mouse position if requested
-- (This will take into account the possibility that a Scroll was queued in the window to offset our absolute mouse position before scroll has been applied)
if set_mouse_pos and io.ConfigNavMoveSetMousePos and bit32.band(io.BackendFlags, 4 --[[ImGuiBackendFlags.HasSetMousePos]]) ~= 0 then
ImGui.TeleportMousePos(ImGui.NavCalcPreferredRefPos(67108864 --[[ImGuiWindowFlags.Popup]]))
end
-- [DEBUG]
g.NavScoringDebugCount = 0
end
end
-- Process Escape/NavCancel input (to close a popup, get back to parent, clear focus)
-- FIXME: In order to support e.g. Escape to clear a selection we'll need:
-- - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it.
-- - either to move most/all of those tests to the epilogue/end functions of the scope they are dealing with (e.g. exit child window in EndChild()) or in EndFrame(), to allow an earlier intercept
function ImGui.NavUpdateCancelRequest()
local g = GImGui
local nav_gamepad_active = bit32.band(g.IO.ConfigFlags, 2 --[[ImGuiConfigFlags.NavEnableGamepad]]) ~= 0 and bit32.band(g.IO.BackendFlags, 1 --[[ImGuiBackendFlags.HasGamepad]]) ~= 0
local nav_keyboard_active = bit32.band(g.IO.ConfigFlags, 1 --[[ImGuiConfigFlags.NavEnableKeyboard]]) ~= 0
if not (nav_keyboard_active and ImGui.IsKeyPressedEx(526 --[[ImGuiKey.Escape]], 0, 4294967295 --[[ImGuiKeyOwner_NoOwner]])) and not (nav_gamepad_active and ImGui.IsKeyPressedEx(ImGuiKey.NavGamepadCancel, 0, 4294967295 --[[ImGuiKeyOwner_NoOwner]])) then
return
end
-- IMGUI_DEBUG_LOG_NAV("[nav] NavUpdateCancelRequest()")
if g.ActiveId ~= 0 then
ImGui.ClearActiveID()
elseif g.NavLayer ~= 0 --[[ImGuiNavLayer.Main]] then
-- Leave the "menu" layer
ImGui.NavRestoreLayer(0 --[[ImGuiNavLayer.Main]])
ImGui.SetNavCursorVisibleAfterMove()
elseif g.NavWindow and g.NavWindow ~= g.NavWindow.RootWindow and bit32.band(g.NavWindow.RootWindowForNav.Flags, 67108864 --[[ImGuiWindowFlags.Popup]]) == 0 and g.NavWindow.RootWindowForNav.ParentWindow then
-- Exit child window
local child_window = g.NavWindow.RootWindowForNav
local parent_window = child_window.ParentWindow
assert(child_window.ChildId ~= 0)
ImGui.FocusWindow(parent_window)
ImGui.SetNavID(child_window.ChildId, 0 --[[ImGuiNavLayer.Main]], 0, ImGui.WindowRectAbsToRel(parent_window, child_window:Rect()))
ImGui.SetNavCursorVisibleAfterMove()
elseif g.OpenPopupStack.Size > 0 and g.OpenPopupStack:back().Window ~= nil and bit32.band(g.OpenPopupStack:back().Window.Flags, 134217728 --[[ImGuiWindowFlags.Modal]]) == 0 then
-- Close open popup/menu
ImGui.ClosePopupToLevel(g.OpenPopupStack.Size - 1, true)
else
-- Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
-- FIXME-NAV: This should happen on window appearing.
if g.IO.ConfigNavEscapeClearFocusItem or g.IO.ConfigNavEscapeClearFocusWindow then
if g.NavWindow and bit32.band(g.NavWindow.Flags, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0 then -- or not (g.NavWindow.Flags & ImGuiWindowFlags.ChildWindow)
g.NavWindow.NavLastIds[0] = 0
end
end
-- Clear nav focus
if g.IO.ConfigNavEscapeClearFocusItem or g.IO.ConfigNavEscapeClearFocusWindow then
g.NavId = 0
end
if g.IO.ConfigNavEscapeClearFocusWindow then
ImGui.FocusWindow(nil)
end
end
end
function ImGui.NavMoveRequestButNoResultYet()
local g = GImGui
return g.NavMoveScoringItems and g.NavMoveResultLocal.ID == 0 and g.NavMoveResultOther.ID == 0
end
local function ImGetDirQuadrantFromDelta(dx, dy)
if ImFabs(dx) > ImFabs(dy) then
return (dx > 0.0) and 1 --[[ImGuiDir.Right]] or 0 --[[ImGuiDir.Left]]
end
return (dy > 0.0) and 3 --[[ImGuiDir.Down]] or 2 --[[ImGuiDir.Up]]
end
local function NavScoreItemDistInterval(cand_min, cand_max, curr_min, curr_max)
if cand_max < curr_min then
return cand_max - curr_min
end
if curr_max < cand_min then
return cand_min - curr_max
end
return 0.0
end
-- Scoring function for keyboard/gamepad directional navigation. Based on https://gist.github.com/rygorous/6981057
--- @param result ImGuiNavItemData
--- @param nav_bb ImRect
--- @return bool
function ImGui.NavScoreItem(result, nav_bb)
local g = GImGui
local window = g.CurrentWindow
if g.NavLayer ~= window.DC.NavLayerCurrent then
return false
end
-- FIXME: Those are not good variables names
local cand = ImRect()
ImRect_Copy(cand, nav_bb) -- Current item nav rectangle
local curr = g.NavScoringRect -- Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)
g.NavScoringDebugCount = g.NavScoringDebugCount + 1
-- When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
if window.ParentWindow == g.NavWindow then
assert(bit32.band(bit32.bor(window.ChildFlags, g.NavWindow.ChildFlags), 256 --[[ImGuiChildFlags.NavFlattened]]) ~= 0)
if not window.ClipRect:Overlaps(cand) then
return false
end
cand:ClipWithFull(window.ClipRect) -- This allows the scored item to not overlap other candidates in the parent window
end
-- Compute distance between boxes
-- FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
local dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x)
local dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2), ImLerp(cand.Min.y, cand.Max.y, 0.8), ImLerp(curr.Min.y, curr.Max.y, 0.2), ImLerp(curr.Min.y, curr.Max.y, 0.8)) -- Scale down on Y to keep using box-distance for vertically touching items
if dby ~= 0.0 and dbx ~= 0.0 then
dbx = (dbx / 1000.0) + ((dbx > 0.0) and 1.0 or -1.0)
end
local dist_box = ImFabs(dbx) + ImFabs(dby)
-- Compute distance between centers (this is off by a factor of 2, but we only compare center distances with each other so it doesn't matter)
local dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x)
local dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y)
local dist_center = ImFabs(dcx) + ImFabs(dcy) -- L1 metric (need this for our connectedness guarantee)
-- Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
local quadrant
local dax = 0.0
local day = 0.0
local dist_axial = 0.0
if dbx ~= 0.0 or dby ~= 0.0 then
-- For non-overlapping boxes, use distance between boxes
-- FIXME-NAV: Quadrant may be incorrect because of (1) dbx bias and (2) curr.Max.y bias applied by NavBiasScoringRect() where typically curr.Max.y==curr.Min.y
-- One typical case where this happens, with style.WindowMenuButtonPosition == ImGuiDir.Right, pressing Left to navigate from Close to Collapse tends to fail.
-- Also see #6344. Calling ImGetDirQuadrantFromDelta() with unbiased values may be good but side-effects are plenty.
dax = dbx
day = dby
dist_axial = dist_box
quadrant = ImGetDirQuadrantFromDelta(dbx, dby)
elseif dcx ~= 0.0 or dcy ~= 0.0 then
-- For overlapping boxes with different centers, use distance between centers
dax = dcx
day = dcy
dist_axial = dist_center
quadrant = ImGetDirQuadrantFromDelta(dcx, dcy)
else
-- Degenerate case: two overlapping buttons with same center, break ties arbitrarily (note that LastItemId here is really the _previous_ item order, but it doesn't matter)
quadrant = (g.LastItemData.ID < g.NavId) and 0 --[[ImGuiDir.Left]] or 1 --[[ImGuiDir.Right]]
end
local move_dir = g.NavMoveDir
-- Is it in the quadrant we're interested in moving to?
local new_best = false
if quadrant == move_dir then
-- Does it beat the current best candidate?
if dist_box < result.DistBox then
result.DistBox = dist_box
result.DistCenter = dist_center
return true
end
if dist_box == result.DistBox then
-- Try using distance between center points to break ties
if dist_center < result.DistCenter then
result.DistCenter = dist_center
new_best = true
elseif dist_center == result.DistCenter then
-- Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
-- (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index),
-- this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis.
if (((move_dir == 2 --[[ImGuiDir.Up]] or move_dir == 3 --[[ImGuiDir.Down]]) and dby) or dbx) < 0.0 then -- moving bj to the right/down decreases distance
new_best = true
end
end
end
end
-- Axial check: if 'curr' has no link at all in some direction and 'cand' lies roughly in that direction, add a tentative link. This will only be kept if no "real" matches
-- are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
-- This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too.
-- 2017/09/29: FIXME: This now currently only enabled inside menu bars, ideally we'd disable it everywhere. Menus in particular need to catch failure. For general navigation it feels awkward.
-- Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
if result.DistBox == 3.4028234663853e+38 --[[FLT_MAX]] and dist_axial < result.DistAxial then -- Check axial match
if g.NavLayer == 1 --[[ImGuiNavLayer.Menu]] and bit32.band(g.NavWindow.Flags, 268435456 --[[ImGuiWindowFlags.ChildMenu]]) == 0 then
if (move_dir == 0 --[[ImGuiDir.Left]] and dax < 0.0) or (move_dir == 1 --[[ImGuiDir.Right]] and dax > 0.0) or (move_dir == 2 --[[ImGuiDir.Up]] and day < 0.0) or (move_dir == 3 --[[ImGuiDir.Down]] and day > 0.0) then
result.DistAxial = dist_axial
new_best = true
end
end
end
return new_best
end
--- @param result ImGuiNavItemData
function ImGui.NavApplyItemToResult(result)
local g = GImGui
local window = g.CurrentWindow
result.Window = window
result.ID = g.LastItemData.ID
result.FocusScopeId = g.CurrentFocusScopeId
result.ItemFlags = g.LastItemData.ItemFlags
ImRect_Copy(result.RectRel, ImGui.WindowRectAbsToRel(window, g.LastItemData.NavRect))
if bit32.band(result.ItemFlags, 2097152 --[[ImGuiItemFlags.HasSelectionUserData]]) ~= 0 then
assert(g.NextItemData.SelectionUserData ~= (-1) --[[ImGuiSelectionUserData_Invalid]])
result.SelectionUserData = g.NextItemData.SelectionUserData -- INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
end
end
--- @param result ImGuiNavItemData
function ImGui.NavMoveRequestResolveWithLastItem(result)
local g = GImGui
g.NavMoveScoringItems = false -- Ensure request doesn't need more processing
ImGui.NavApplyItemToResult(result)
ImGui.NavUpdateAnyRequestFlag()
end
-- Handle "scoring" of an item for a tabbing/focusing request initiated by NavUpdateCreateTabbingRequest().
-- Note that SetKeyboardFocusHere() API calls are considered tabbing requests!
-- - Case 1: no nav/active id: set result to first eligible item, stop storing.
-- - Case 2: tab forward: on ref id set counter, on counter elapse store result
-- - Case 3: tab forward wrap: set result to first eligible item (preemptively), on ref id set counter, on next frame if counter hasn't elapsed store result. -- FIXME-TABBING: Could be done as a next-frame forwarded request
-- - Case 4: tab backward: store all results, on ref id pick prev, stop storing
-- - Case 5: tab backward wrap: store all results, on ref id if no result keep storing until last -- FIXME-TABBING: Could be done as next-frame forwarded requested
--- @param id ImGuiID
--- @param item_flags ImGuiItemFlags
--- @param move_flags ImGuiNavMoveFlags
function ImGui.NavProcessItemForTabbingRequest(id, item_flags, move_flags)
local g = GImGui
if bit32.band(move_flags, 512 --[[ImGuiNavMoveFlags.FocusApi]]) == 0 then
if g.NavLayer ~= g.CurrentWindow.DC.NavLayerCurrent then
return
end
if g.NavFocusScopeId ~= g.CurrentFocusScopeId then
return
end
end
-- - Can always land on an item when using API call.
-- - Tabbing with _NavEnableKeyboard (space/enter/arrows): goes through every item.
-- - Tabbing without _NavEnableKeyboard: goes through inputable items only.
local can_stop
if bit32.band(move_flags, 512 --[[ImGuiNavMoveFlags.FocusApi]]) ~= 0 then
can_stop = true
else
can_stop = bit32.band(item_flags, 1 --[[ImGuiItemFlags.NoTabStop]]) == 0 and (bit32.band(g.IO.ConfigFlags, 1 --[[ImGuiConfigFlags.NavEnableKeyboard]]) ~= 0 or bit32.band(item_flags, 1048576 --[[ImGuiItemFlags.Inputable]]) ~= 0)
end
-- Always store in NavMoveResultLocal (unlike directional request which uses NavMoveResultOther on sibling/flattened windows)
local result = g.NavMoveResultLocal
if g.NavTabbingDir == 1 then
-- Tab Forward or SetKeyboardFocusHere() with >= 0
if can_stop and g.NavTabbingResultFirst.ID == 0 then
ImGui.NavApplyItemToResult(g.NavTabbingResultFirst)
end
if can_stop and g.NavTabbingCounter > 0 then
g.NavTabbingCounter = g.NavTabbingCounter - 1
if g.NavTabbingCounter == 0 then
ImGui.NavMoveRequestResolveWithLastItem(result)
end
elseif g.NavId == id then
g.NavTabbingCounter = 1
end
elseif g.NavTabbingDir == -1 then
-- Tab Backward
if g.NavId == id then
if result.ID ~= 0 then
g.NavMoveScoringItems = false
ImGui.NavUpdateAnyRequestFlag()
end
elseif can_stop then
-- Keep applying until reaching NavId
ImGui.NavApplyItemToResult(result)
end
elseif g.NavTabbingDir == 0 then
if can_stop and g.NavId == id then
ImGui.NavMoveRequestResolveWithLastItem(result)
end
if can_stop and g.NavTabbingResultFirst.ID == 0 then -- Tab init
ImGui.NavApplyItemToResult(g.NavTabbingResultFirst)
end
end
end
-- We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
-- This is called after LastItemData is set, but NextItemData is also still valid.
function ImGui.NavProcessItem()
local g = GImGui
local window = g.CurrentWindow
local id = g.LastItemData.ID
local item_flags = g.LastItemData.ItemFlags
-- When inside a container that isn't scrollable with Left<>Right, clip NavRect accordingly (#2221, #8816, #7994)
local nav_bb = ImRect()
ImRect_Copy(nav_bb, g.LastItemData.NavRect)
if window.DC.NavIsScrollPushableX == false then
nav_bb.Min.x = ImClamp(nav_bb.Min.x, window.ClipRect.Min.x, window.ClipRect.Max.x)
nav_bb.Max.x = ImClamp(nav_bb.Max.x, window.ClipRect.Min.x, window.ClipRect.Max.x)
end
-- Process Init Request
if g.NavInitRequest and g.NavLayer == window.DC.NavLayerCurrent and bit32.band(item_flags, 64 --[[ImGuiItemFlags.Disabled]]) == 0 then
-- Even if 'ImGuiItemFlags.NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
local candidate_for_nav_default_focus = bit32.band(item_flags, 4 --[[ImGuiItemFlags.NoNavDefaultFocus]]) == 0
if candidate_for_nav_default_focus or g.NavInitResult.ID == 0 then
ImGui.NavApplyItemToResult(g.NavInitResult)
end
if candidate_for_nav_default_focus then
g.NavInitRequest = false -- Found a match, clear request
ImGui.NavUpdateAnyRequestFlag()
end
end
-- Process Move Request (scoring for navigation)
-- FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy)
if g.NavMoveScoringItems and bit32.band(item_flags, 64 --[[ImGuiItemFlags.Disabled]]) == 0 then
if bit32.band(g.NavMoveFlags, 512 --[[ImGuiNavMoveFlags.FocusApi]]) ~= 0 or bit32.band(window.Flags, 65536 --[[ImGuiWindowFlags.NoNavInputs]]) == 0 then
local is_tabbing = bit32.band(g.NavMoveFlags, 1024 --[[ImGuiNavMoveFlags.IsTabbing]]) ~= 0
if is_tabbing then
ImGui.NavProcessItemForTabbingRequest(id, item_flags, g.NavMoveFlags)
elseif g.NavId ~= id or bit32.band(g.NavMoveFlags, 16 --[[ImGuiNavMoveFlags.AllowCurrentNavId]]) ~= 0 then
local result = (window == g.NavWindow) and g.NavMoveResultLocal or g.NavMoveResultOther
if ImGui.NavScoreItem(result, nav_bb) then
ImGui.NavApplyItemToResult(result)
end
-- Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
local VISIBLE_RATIO = 0.70
if bit32.band(g.NavMoveFlags, 32 --[[ImGuiNavMoveFlags.AlsoScoreVisibleSet]]) ~= 0 then
local r = window.InnerRect -- window.ClipRect
if r:Overlaps(nav_bb) then
if ImClamp(nav_bb.Max.y, r.Min.y, r.Max.y) - ImClamp(nav_bb.Min.y, r.Min.y, r.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO then
if ImGui.NavScoreItem(g.NavMoveResultLocalVisible, nav_bb) then
ImGui.NavApplyItemToResult(g.NavMoveResultLocalVisible)
end
end
end
end
end
end
end
-- Update information for currently focused/navigated item
if g.NavId == id then
if g.NavWindow ~= window then
ImGui.SetNavWindow(window) -- Always refresh g.NavWindow, because some operations such as FocusItem() may not have a window.
end
g.NavLayer = window.DC.NavLayerCurrent
ImGui.SetNavFocusScope(g.CurrentFocusScopeId) -- Will set g.NavFocusScopeId AND store g.NavFocusScopePath
g.NavFocusScopeId = g.CurrentFocusScopeId
g.NavIdIsAlive = true
g.NavIdItemFlags = item_flags
if bit32.band(g.LastItemData.ItemFlags, 2097152 --[[ImGuiItemFlags.HasSelectionUserData]]) ~= 0 then
assert(g.NextItemData.SelectionUserData ~= (-1) --[[ImGuiSelectionUserData_Invalid]])
g.NavLastValidSelectionUserData = g.NextItemData.SelectionUserData -- INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
end
window.NavRectRel[window.DC.NavLayerCurrent] = ImGui.WindowRectAbsToRel(window, nav_bb) -- Store item bounding box (relative to window position)
end
end
function ImGui.NavInitRequestApplyResult()
-- In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void)
local g = GImGui
if not g.NavWindow then
return
end
local result = g.NavInitResult
if g.NavId ~= result.ID then
g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId
g.NavJustMovedToId = result.ID
g.NavJustMovedToFocusScopeId = result.FocusScopeId
g.NavJustMovedToKeyMods = 0
g.NavJustMovedToIsTabbing = false
g.NavJustMovedToIsInit = true
g.NavJustMovedToHasSelectionData = bit32.band(result.ItemFlags, 2097152 --[[ImGuiItemFlags.HasSelectionUserData]]) ~= 0
end
-- Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
-- FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently.
-- IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: ApplyResult: NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name)
ImGui.SetNavID(result.ID, g.NavLayer, result.FocusScopeId, result.RectRel)
g.NavIdIsAlive = true -- Mark as alive from previous frame as we got a result
if result.SelectionUserData ~= (-1) --[[ImGuiSelectionUserData_Invalid]] then
g.NavLastValidSelectionUserData = result.SelectionUserData
end
if g.NavInitRequestFromMove then
ImGui.SetNavCursorVisibleAfterMove()
end
end
-- Apply result from previous frame navigation directional move request. Always called from NavUpdate()
function ImGui.NavMoveRequestApplyResult()
local g = GImGui
-- Select which result to use
local result
if g.NavMoveResultLocal.ID ~= 0 then
result = g.NavMoveResultLocal
elseif g.NavMoveResultOther.ID ~= 0 then
result = g.NavMoveResultOther
else
result = nil
end
-- Tabbing forward wrap
if bit32.band(g.NavMoveFlags, 1024 --[[ImGuiNavMoveFlags.IsTabbing]]) ~= 0 and result == nil then
if (g.NavTabbingCounter == 1 or g.NavTabbingDir == 0) and g.NavTabbingResultFirst.ID ~= 0 then
result = g.NavTabbingResultFirst
end
end
-- In a situation when there are no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result)
local axis = (g.NavMoveDir == 2 --[[ImGuiDir.Up]] or g.NavMoveDir == 3 --[[ImGuiDir.Down]]) and 2 --[[ImGuiAxis.Y]] or 1 --[[ImGuiAxis.X]]
if result == nil then
if bit32.band(g.NavMoveFlags, 1024 --[[ImGuiNavMoveFlags.IsTabbing]]) ~= 0 then
g.NavMoveFlags = bit32.bor(g.NavMoveFlags, 16384 --[[ImGuiNavMoveFlags.NoSetNavCursorVisible]])
end
if g.NavId ~= 0 and bit32.band(g.NavMoveFlags, 16384 --[[ImGuiNavMoveFlags.NoSetNavCursorVisible]]) == 0 then
ImGui.SetNavCursorVisibleAfterMove()
end
ImGui.NavClearPreferredPosForAxis(axis) -- On a failed move, clear preferred pos for this axis.
-- IMGUI_DEBUG_LOG_NAV("[nav] NavMoveSubmitted but not led to a result!\n")
return
end
-- PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
if bit32.band(g.NavMoveFlags, 32 --[[ImGuiNavMoveFlags.AlsoScoreVisibleSet]]) ~= 0
and g.NavMoveResultLocalVisible.ID ~= 0 and g.NavMoveResultLocalVisible.ID ~= g.NavId then
result = g.NavMoveResultLocalVisible
end
-- Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
if result ~= g.NavMoveResultOther and g.NavMoveResultOther.ID ~= 0 and g.NavMoveResultOther.Window.ParentWindow == g.NavWindow then
if g.NavMoveResultOther.DistBox < result.DistBox
or (g.NavMoveResultOther.DistBox == result.DistBox and g.NavMoveResultOther.DistCenter < result.DistCenter) then
result = g.NavMoveResultOther
end
end
assert(g.NavWindow and result.Window)
-- Scroll to keep newly navigated item fully into view.
if g.NavLayer == 0 --[[ImGuiNavLayer.Main]] then
local rect_abs = ImGui.WindowRectRelToAbs(result.Window, result.RectRel)
ImGui.ScrollToRectEx(result.Window, rect_abs, g.NavMoveScrollFlags)
if bit32.band(g.NavMoveFlags, 64 --[[ImGuiNavMoveFlags.ScrollToEdgeY]]) ~= 0 then
-- FIXME: Should remove this? Or make more precise: use ScrollToRectEx() with edge?
local scroll_target = (g.NavMoveDir == 2 --[[ImGuiDir.Up]]) and result.Window.ScrollMax.y or 0.0
ImGui.SetScrollY(result.Window, scroll_target)
end
end
if g.NavWindow ~= result.Window then
-- IMGUI_DEBUG_LOG_FOCUS("[focus] NavMoveRequest: SetNavWindow(\"%s\")\n", result->Window->Name)
g.NavWindow = result.Window
g.NavLastValidSelectionUserData = (-1) --[[ImGuiSelectionUserData_Invalid]]
end
-- Clear active id unless requested not to
-- FIXME: ImGuiNavMoveFlags.NoClearActiveId is currently unused as we don't have a clear strategy to preserve active id after interaction,
-- so this is mostly provided as a gateway for further experiments (see #1418, #2890)
if g.ActiveId ~= result.ID and bit32.band(g.NavMoveFlags, 32768 --[[ImGuiNavMoveFlags.NoClearActiveId]]) == 0 then
ImGui.ClearActiveID()
end
-- Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags.AllowCurrentNavId)
-- PageUp/PageDown however sets always set NavJustMovedTo (vs Home/End which doesn't) mimicking Windows behavior.
if (g.NavId ~= result.ID or bit32.band(g.NavMoveFlags, 2048 --[[ImGuiNavMoveFlags.IsPageMove]]) ~= 0)
and bit32.band(g.NavMoveFlags, 8192 --[[ImGuiNavMoveFlags.NoSelect]]) == 0 then
g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId
g.NavJustMovedToId = result.ID
g.NavJustMovedToFocusScopeId = result.FocusScopeId
g.NavJustMovedToKeyMods = g.NavMoveKeyMods
g.NavJustMovedToIsTabbing = bit32.band(g.NavMoveFlags, 1024 --[[ImGuiNavMoveFlags.IsTabbing]]) ~= 0
g.NavJustMovedToIsInit = false
g.NavJustMovedToHasSelectionData = bit32.band(result.ItemFlags, 2097152 --[[ImGuiItemFlags.HasSelectionUserData]]) ~= 0
-- IMGUI_DEBUG_LOG_NAV("[nav] NavJustMovedFromFocusScopeId = 0x%08X, NavJustMovedToFocusScopeId = 0x%08X\n", g.NavJustMovedFromFocusScopeId, g.NavJustMovedToFocusScopeId)
end
-- Apply new NavID/Focus
-- IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name)
local root_for_nav = g.NavWindow.RootWindowForNav
local preferred_scoring_pos_rel = ImVec2()
ImVec2_Copy(preferred_scoring_pos_rel, root_for_nav.NavPreferredScoringPosRel[g.NavLayer])
ImGui.SetNavID(result.ID, g.NavLayer, result.FocusScopeId, result.RectRel)
if result.SelectionUserData ~= (-1) --[[ImGuiSelectionUserData_Invalid]] then
g.NavLastValidSelectionUserData = result.SelectionUserData
end
-- Restore last preferred position for current axis
-- (storing in RootWindowForNav-> as the info is desirable at the beginning of a Move Request. In theory all storage should use RootWindowForNav..)
if bit32.band(g.NavMoveFlags, 1024 --[[ImGuiNavMoveFlags.IsTabbing]]) == 0 then
preferred_scoring_pos_rel[axis] = result.RectRel:GetCenter()[axis]
ImVec2_Copy(root_for_nav.NavPreferredScoringPosRel[g.NavLayer], preferred_scoring_pos_rel)
end
-- Tabbing: Activates Inputable, otherwise only Focus
if bit32.band(g.NavMoveFlags, 1024 --[[ImGuiNavMoveFlags.IsTabbing]]) ~= 0
and bit32.band(result.ItemFlags, 1048576 --[[ImGuiItemFlags.Inputable]]) == 0 then
g.NavMoveFlags = bit32.band(g.NavMoveFlags, bit32.bnot(4096 --[[ImGuiNavMoveFlags.Activate]]))
end
-- Activate
if bit32.band(g.NavMoveFlags, 4096 --[[ImGuiNavMoveFlags.Activate]]) ~= 0 then
g.NavNextActivateId = result.ID
g.NavNextActivateFlags = 0 --[[ImGuiActivateFlags.None]]
if bit32.band(g.NavMoveFlags, 512 --[[ImGuiNavMoveFlags.FocusApi]]) ~= 0 then
g.NavNextActivateFlags = bit32.bor(g.NavNextActivateFlags, 32 --[[ImGuiActivateFlags.FromFocusApi]])
end
if bit32.band(g.NavMoveFlags, 1024 --[[ImGuiNavMoveFlags.IsTabbing]]) ~= 0 then
g.NavNextActivateFlags = bit32.bor(g.NavNextActivateFlags, 1 --[[ImGuiActivateFlags.PreferInput]], 4 --[[ImGuiActivateFlags.TryToPreserveState]], 8 --[[ImGuiActivateFlags.FromTabbing]])
end
end
-- Make nav cursor visible
if bit32.band(g.NavMoveFlags, 16384 --[[ImGuiNavMoveFlags.NoSetNavCursorVisible]]) == 0 then
ImGui.SetNavCursorVisibleAfterMove()
end
end
-- True when current work location may be scrolled horizontally when moving left / right.
-- This is generally always true UNLESS within a column. We don't have a vertical equivalent.
function ImGui.NavUpdateCurrentWindowIsScrollPushableX()
local g = GImGui
local window = g.CurrentWindow
window.DC.NavIsScrollPushableX = (g.CurrentTable == nil and window.DC.CurrentColumns == nil)
end
-- Called by TreePop() to implement ImGuiTreeNodeFlags.NavLeftJumpsToParent
--- @param result ImGuiNavItemData
--- @param tree_node_data ImGuiTreeNodeStackData
function ImGui.NavMoveRequestResolveWithPastTreeNode(result, tree_node_data)
local g = GImGui
g.NavMoveScoringItems = false
g.LastItemData.ID = tree_node_data.ID
g.LastItemData.ItemFlags = bit32.band(tree_node_data.ItemFlags, bit32.bnot(2097152 --[[ImGuiItemFlags.HasSelectionUserData]])) -- Losing SelectionUserData, recovered next-frame (cheaper).
ImRect_Copy(g.LastItemData.NavRect, tree_node_data.NavRect)
ImGui.NavApplyItemToResult(result) -- Result this instead of implementing a NavApplyPastTreeNodeToResult()
ImGui.NavClearPreferredPosForAxis(2 --[[ImGuiAxis.Y]])
ImGui.NavUpdateAnyRequestFlag()
end
function ImGui.NavMoveRequestCancel()
local g = GImGui
g.NavMoveSubmitted = false
g.NavMoveScoringItems = false
ImGui.NavUpdateAnyRequestFlag()
end
-- Forward will reuse the move request again on the next frame (generally with modifications done to it)
--- @param move_dir ImGuiDir
--- @param clip_dir ImGuiDir
--- @param move_flags ImGuiNavMoveFlags
--- @param scroll_flags ImGuiScrollFlags
function ImGui.NavMoveRequestForward(move_dir, clip_dir, move_flags, scroll_flags)
local g = GImGui
assert(g.NavMoveForwardToNextFrame == false)
ImGui.NavMoveRequestCancel()
g.NavMoveForwardToNextFrame = true
g.NavMoveDir = move_dir
g.NavMoveClipDir = clip_dir
g.NavMoveFlags = bit32.bor(move_flags, 128 --[[ImGuiNavMoveFlags.Forwarded]])
g.NavMoveScrollFlags = scroll_flags
end
-- Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
-- popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
--- @param window ImGuiWindow
--- @param wrap_flags ImGuiNavMoveFlags
function ImGui.NavMoveRequestTryWrapping(window, wrap_flags)
local g = GImGui
assert(bit32.band(wrap_flags, 15 --[[ImGuiNavMoveFlags.WrapMask_]]) ~= 0 and bit32.band(wrap_flags, bit32.bnot(15 --[[ImGuiNavMoveFlags.WrapMask_]])) == 0) -- Call with _WrapX, _WrapY, _LoopX, _LoopY
-- In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it:
-- as NavEndFrame() will do the same test. It will end up calling NavUpdateCreateWrappingRequest().
if g.NavWindow == window and g.NavMoveScoringItems and g.NavLayer == window.DC.NavLayerCurrent then
g.NavMoveFlags = bit32.bor(bit32.band(g.NavMoveFlags, bit32.bnot(15 --[[ImGuiNavMoveFlags.WrapMask_]])), wrap_flags)
end
end
-------------------------------------------------------------------------------
-- [SECTION] DRAG AND DROP
-------------------------------------------------------------------------------
function ImGui.IsDragDropPayloadBeingAccepted()
local g = GImGui
return g.DragDropActive and g.DragDropAcceptIdPrev ~= 0
end
-------------------------------------------------------------------------------
-- [SECTION] LOCALIZATION
-------------------------------------------------------------------------------
--- @param entries ImGuiLocEntry[]
--- @param count int
function ImGui.LocalizeRegisterEntries(entries, count)
local g = GImGui
for n = 1, count do
g.LocalizationTable[entries[n].Key] = entries[n].Text
end
end
-------------------------------------------------------------------------------
-- [SECTION] VIEWPORTS, PLATFORM WINDOWS
-------------------------------------------------------------------------------
-- - GetMainViewport()
-- - FindViewportByID()
-- - SetCurrentViewport() [Internal]
-- - SetWindowViewport() [Internal]
-- - GetWindowAlwaysWantOwnViewport() [Internal]
-- - UpdateTryMergeWindowIntoHostViewport() [Internal]
-- - UpdateTryMergeWindowIntoHostViewports() [Internal]
-- - TranslateWindowsInViewport() [Internal]
-- - ScaleWindowsInViewport() [Internal]
-- - FindHoveredViewportFromPlatformWindowStack() [Internal]
-- - UpdateViewportsNewFrame() [Internal]
-- - UpdateViewportsEndFrame() [Internal]
-- - AddUpdateViewport() [Internal]
-- - WindowSelectViewport() [Internal]
-- - WindowSyncOwnedViewport() [Internal]
-- - UpdatePlatformWindows()
-- - RenderPlatformWindowsDefault()
-- - FindPlatformMonitorForPos() [Internal]
-- - FindPlatformMonitorForRect() [Internal]
-- - UpdateViewportPlatformMonitor() [Internal]
-- - DestroyPlatformWindow() [Internal]
-- - DestroyPlatformWindows()
-------------------------------------------------------------------------------
function MT.ImGuiViewport:GetDebugName()
local viewport = self
return viewport.Window and viewport.Window.Name or "n/a"
end
function MT.ImGuiPlatformIO:ClearPlatformHandlers()
self.Platform_GetClipboardTextFn = nil; self.Platform_SetClipboardTextFn = nil
self.Platform_ClipboardUserData = nil
self.Platform_OpenInShellFn = nil; self.Platform_OpenInShellUserData = nil
self.Platform_SetImeDataFn = nil; self.Platform_ImeUserData = nil
self.Platform_CreateWindow = nil; self.Platform_DestroyWindow = nil; self.Platform_ShowWindow = nil
self.Platform_SetWindowPos = nil; self.Platform_SetWindowSize = nil
self.Platform_GetWindowPos = nil; self.Platform_GetWindowSize = nil; self.Platform_GetWindowFramebufferScale = nil
self.Platform_SetWindowFocus = nil; self.Platform_GetWindowFocus = nil; self.Platform_GetWindowMinimized = nil
self.Platform_SetWindowTitle = nil; self.Platform_SetWindowAlpha = nil; self.Platform_UpdateWindow = nil
self.Platform_RenderWindow = nil; self.Platform_SwapBuffers = nil; self.Platform_GetWindowDpiScale = nil
self.Platform_OnChangedViewport = nil; self.Platform_GetWindowWorkAreaInsets = nil; self.Platform_CreateVkSurface = nil
InitializeDefaultPlatformHandlers(self) -- Prevents losing the functionality provided by Platform_*_DefaultImpl on backend re-creation.
end
function MT.ImGuiPlatformIO:ClearRendererHandlers()
self.Renderer_TextureMaxWidth = 0; self.Renderer_TextureMaxHeight = 0
self.Renderer_RenderState = nil
self.Renderer_CreateWindow = nil; self.Renderer_DestroyWindow = nil
self.Renderer_SetWindowSize = nil
self.Renderer_RenderWindow = nil; self.Renderer_SwapBuffers = nil
end
--- @return ImGuiViewport
function ImGui.GetMainViewport()
local g = GImGui
return g.Viewports.Data[1] -- [Luau] .Data[]: no IM_VECTOR.__index
end
-- FIXME: This leaks access to viewports not listed in PlatformIO.Viewports[]. Problematic? (#4236)
--- @param viewport_id ImGuiID
--- @return ImGuiViewport?
function ImGui.FindViewportByID(viewport_id)
local g = GImGui
for _, viewport in g.Viewports:iter() do
if viewport.ID == viewport_id then
return viewport
end
end
return nil
end
--- @param platform_handle any
--- @return ImGuiViewportP?
function ImGui.FindViewportByPlatformHandle(platform_handle)
local g = GImGui
for _, viewport in g.Viewports:iter() do
if viewport.PlatformHandle == platform_handle then
return viewport
end
end
return nil
end
--- @param current_window? ImGuiWindow
--- @param viewport? ImGuiViewportP
function ImGui.SetCurrentViewport(current_window, viewport)
local g = GImGui
if viewport then
viewport.LastFrameActive = g.FrameCount
end
if g.CurrentViewport == viewport then
return
end
g.CurrentDpiScale = viewport and viewport.DpiScale or 1.0
g.CurrentViewport = viewport
assert(g.CurrentDpiScale > 0.0 and g.CurrentDpiScale < 99.0) -- Typical correct values would be between 1.0f and 4.0f
-- IMGUI_DEBUG_LOG_VIEWPORT("[viewport] SetCurrentViewport changed '%s' 0x%08X", current_window and current_window.Name or "NULL", viewport and viewport.ID or 0)
if g.IO.ConfigDpiScaleFonts then
g.Style.FontScaleDpi = g.CurrentDpiScale
end
-- Notify platform layer of viewport changes
-- FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI
if g.CurrentViewport and g.PlatformIO.Platform_OnChangedViewport then
g.PlatformIO.Platform_OnChangedViewport(g.CurrentViewport)
end
end
--- @param window ImGuiWindow
--- @param viewport ImGuiViewportP
function ImGui.SetWindowViewport(window, viewport)
-- Abandon viewport
if window.ViewportOwned and window.Viewport.Window == window then
window.Viewport.Size = ImVec2(0.0, 0.0)
end
window.Viewport = viewport
window.ViewportId = viewport.ID
window.ViewportOwned = (viewport.Window == window)
end
--- @param window ImGuiWindow
--- @return bool
function ImGui.GetWindowAlwaysWantOwnViewport(window)
-- Tooltips and menus are not automatically forced into their own viewport when the NoMerge flag is set, however the multiplication of viewports makes them more likely to protrude and create their own.
local g = GImGui
if g.IO.ConfigViewportsNoAutoMerge or (bit32.band(window.WindowClass.ViewportFlagsOverrideSet, 512 --[[ImGuiViewportFlags.NoAutoMerge]]) ~= 0) then
if bit32.band(g.ConfigFlagsCurrFrame, 1024 --[[ImGuiConfigFlags.ViewportsEnable]]) ~= 0 then
if not window.DockIsActive then
if bit32.band(window.Flags, bit32.bor(16777216 --[[ImGuiWindowFlags.ChildWindow]], 268435456 --[[ImGuiWindowFlags.ChildMenu]], 33554432 --[[ImGuiWindowFlags.Tooltip]])) == 0 then
if bit32.band(window.Flags, 67108864 --[[ImGuiWindowFlags.Popup]]) == 0 or bit32.band(window.Flags, 134217728 --[[ImGuiWindowFlags.Modal]]) ~= 0 then
return true
end
end
end
end
end
return false
end
-- Heuristic, see #8948: depends on how backends handle OS-level parenting.
-- Due to how parent viewport stack is layed out, note that IsViewportAbove(a,b) isn't always the same as !IsViewportAbove(b,a).
--- @param potential_above ImGuiViewportP
--- @param potential_below ImGuiViewportP
--- @return bool
function ImGui.IsViewportAbove(potential_above, potential_below)
-- If ImGuiBackendFlags.HasParentViewport if set, ->ParentViewport chain should be accurate.
local g = GImGui
if bit32.band(g.IO.BackendFlags, 8192 --[[ImGuiBackendFlags.HasParentViewport]]) ~= 0 then
local v = potential_above --[[@as ImGuiViewport]]
while v ~= nil and v.ParentViewport do
if v.ParentViewport == potential_below then
return true
end
v = v.ParentViewport
end
else
if potential_above.ParentViewport == potential_below then
return true
end
end
if potential_above.LastFocusedStampCount > potential_below.LastFocusedStampCount then
return true
end
return false
end
--- @param window ImGuiWindow
--- @param viewport_dst ImGuiViewportP
function ImGui.UpdateTryMergeWindowIntoHostViewport(window, viewport_dst)
local g = GImGui
assert(window == window.RootWindowDockTree)
local viewport_src = window.Viewport -- Current viewport
if viewport_src == viewport_dst then
return false
end
if bit32.band(viewport_dst.Flags, 2048 --[[ImGuiViewportFlags.CanHostOtherWindows]]) == 0 then
return false
end
if bit32.band(viewport_dst.Flags, 4096 --[[ImGuiViewportFlags.IsMinimized]]) ~= 0 then
return false
end
if not viewport_dst:GetMainRect():Contains(window:Rect()) then
return false
end
if ImGui.GetWindowAlwaysWantOwnViewport(window) then
return false
end
for _, viewport_obstructing in g.Viewports:iter() do
repeat
if viewport_obstructing == viewport_src or viewport_obstructing == viewport_dst or not viewport_obstructing.PlatformWindowCreated then
break
end
if viewport_obstructing:GetMainRect():Overlaps(window:Rect()) then
if ImGui.IsViewportAbove(viewport_obstructing, viewport_dst) then
if viewport_src == nil or ImGui.IsViewportAbove(viewport_src, viewport_obstructing) then
return false -- viewport_obstructing is between viewport_src and viewport_dst -> Cannot merge.
end
end
end
until true
end
-- Move to the existing viewport, Move child/hosted windows as well (FIXME-OPT: iterate child)
IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Window '%s' merge into Viewport 0X%08X", window.Name, viewport_dst.ID)
if window.ViewportOwned then
for n = 1, g.Windows.Size do
if g.Windows.Data[n].Viewport == viewport_src then
ImGui.SetWindowViewport(g.Windows.Data[n], viewport_dst)
end
end
end
ImGui.SetWindowViewport(window, viewport_dst)
if bit32.band(window.Flags, 8192 --[[ImGuiWindowFlags.NoBringToFrontOnFocus]]) == 0 then
ImGui.BringWindowToDisplayFront(window)
end
return true
end
-- FIXME: handle 0 to N host viewports
--- @param window ImGuiWindow
--- @return bool
function ImGui.UpdateTryMergeWindowIntoHostViewports(window)
local g = GImGui
return ImGui.UpdateTryMergeWindowIntoHostViewport(window, g.Viewports.Data[1])
end
-- Translate Dear ImGui windows when a Host Viewport has been moved
-- (This additionally keeps windows at the same place when ImGuiConfigFlags.ViewportsEnable is toggled!)
--- @param viewport ImGuiViewportP
--- @param old_pos ImVec2
--- @param new_pos ImVec2
--- @param old_size ImVec2
--- @param new_size ImVec2
function ImGui.TranslateWindowsInViewport(viewport, old_pos, new_pos, old_size, new_size)
local g = GImGui
-- IMGUI_DEBUG_LOG_VIEWPORT("[viewport] TranslateWindowsInViewport 0x%08X", viewport.ID)
assert(viewport.Window == nil and (bit32.band(viewport.Flags, 2048 --[[ImGuiViewportFlags.CanHostOtherWindows]]) ~= 0))
-- 1) We test if ImGuiConfigFlags.ViewportsEnable was just toggled, which allows us to conveniently
-- translate imgui windows from OS-window-local to absolute coordinates or vice-versa.
-- 2) If it's not going to fit into the new size, keep it at same absolute position.
-- One problem with this is that most Win32 applications doesn't update their render while dragging,
-- and so the window will appear to teleport when releasing the mouse.
local translate_all_windows = (bit32.band(g.ConfigFlagsCurrFrame, 1024 --[[ImGuiConfigFlags.ViewportsEnable]]) ~= bit32.band(g.ConfigFlagsLastFrame, 1024 --[[ImGuiConfigFlags.ViewportsEnable]]))
local test_still_fit_rect = ImRect(old_pos, old_pos + old_size)
local delta_pos = new_pos - old_pos
for _, window in g.Windows:iter() do -- FIXME-OPT
if translate_all_windows or (window.Viewport == viewport and (old_size == new_size or test_still_fit_rect:Contains(window:Rect()))) then
ImGui.TranslateWindow(window, delta_pos)
end
end
end
-- If the backend doesn't support ImGuiBackendFlags.HasMouseHoveredViewport or doesn't honor ImGuiViewportFlags.NoInputs for it, we do a search ourselves.
-- A) It won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window.
-- B) It requires Platform_GetWindowFocus to be implemented by backend.
--- @param mouse_platform_pos ImVec2
--- @return ImGuiViewportP?
function ImGui.FindHoveredViewportFromPlatformWindowStack(mouse_platform_pos)
local g = GImGui
local best_candidate = nil
for _, viewport in g.Viewports:iter() do
if bit32.band(viewport.Flags, bit32.bor(128 --[[ImGuiViewportFlags.NoInputs]], 4096 --[[ImGuiViewportFlags.IsMinimized]])) == 0 and viewport:GetMainRect():Contains(mouse_platform_pos) then
if best_candidate == nil or best_candidate.LastFocusedStampCount < viewport.LastFocusedStampCount then
if viewport.PlatformWindowCreated then
best_candidate = viewport
end
end
end
end
return best_candidate
end
-- Update viewports and monitor infos
-- Note that this is running even if 'ImGuiConfigFlags.ViewportsEnable' is not set, in order to clear unused viewports (if any) and update monitor info.
function ImGui.UpdateViewportsNewFrame()
local g = GImGui
assert(g.PlatformIO.Viewports.Size <= g.Viewports.Size)
-- Update Minimized status (we need it first in order to decide if we'll apply Pos/Size of the main viewport)
-- Update Focused status
local viewports_enabled = (bit32.band(g.ConfigFlagsCurrFrame, 1024 --[[ImGuiConfigFlags.ViewportsEnable]]) ~= 0)
if viewports_enabled then
local focused_viewport = nil
for i = 1, g.Viewports.Size do
local viewport = g.Viewports.Data[i]
local platform_funcs_available = viewport.PlatformWindowCreated
if g.PlatformIO.Platform_GetWindowMinimized and platform_funcs_available then
local is_minimized = g.PlatformIO.Platform_GetWindowMinimized(viewport)
if is_minimized then
viewport.Flags = bit32.bor(viewport.Flags, 4096 --[[ImGuiViewportFlags.IsMinimized]])
else
viewport.Flags = bit32.band(viewport.Flags, bit32.bnot(4096 --[[ImGuiViewportFlags.IsMinimized]]))
end
end
-- Update our implicit z-order knowledge of platform windows, which is used when the backend cannot provide io.MouseHoveredViewport.
-- When setting Platform_GetWindowFocus, it is expected that the platform backend can handle calls without crashing if it doesn't have data stored.
if g.PlatformIO.Platform_GetWindowFocus and platform_funcs_available then
local is_focused = g.PlatformIO.Platform_GetWindowFocus(viewport)
if is_focused then
viewport.Flags = bit32.bor(viewport.Flags, 8192 --[[ImGuiViewportFlags.IsFocused]])
else
viewport.Flags = bit32.band(viewport.Flags, bit32.bnot(8192 --[[ImGuiViewportFlags.IsFocused]]))
end
if is_focused then
focused_viewport = viewport
end
end
end
-- Focused viewport has changed?
if focused_viewport and g.PlatformLastFocusedViewportId ~= focused_viewport.ID then
IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Focused viewport changed %08X -> %08X '%s', attempting to apply our focus.", g.PlatformLastFocusedViewportId, focused_viewport.ID, focused_viewport.Window and focused_viewport.Window.Name or "n/a")
local prev_focused_viewport = ImGui.FindViewportByID(g.PlatformLastFocusedViewportId)
local prev_focused_has_been_destroyed = (prev_focused_viewport == nil) or (not prev_focused_viewport.PlatformWindowCreated)
-- Store a tag so we can infer z-order easily from all our windows
-- We compare PlatformLastFocusedViewportId so newly created viewports with _NoFocusOnAppearing flag
-- will keep the front most stamp instead of losing it back to their parent viewport.
if focused_viewport.LastFocusedStampCount ~= g.ViewportFocusedStampCount then
g.ViewportFocusedStampCount = g.ViewportFocusedStampCount + 1
focused_viewport.LastFocusedStampCount = g.ViewportFocusedStampCount
end
g.PlatformLastFocusedViewportId = focused_viewport.ID
-- Focus associated dear imgui window
-- - if focus didn't happen with a click within imgui boundaries, e.g. Clicking platform title bar. (#6299)
-- - if focus didn't happen because we destroyed another window (#6462)
-- FIXME: perhaps 'FocusTopMostWindowUnderOne()' can handle the 'focused_window.Window ~= nil' case as well.
local apply_imgui_focus_on_focused_viewport = not ImGui.IsAnyMouseDown() and not prev_focused_has_been_destroyed
if apply_imgui_focus_on_focused_viewport and g.IO.ConfigViewportsPlatformFocusSetsImGuiFocus then
focused_viewport.LastFocusedHadNavWindow = focused_viewport.LastFocusedHadNavWindow or (g.NavWindow ~= nil and g.NavWindow.Viewport == focused_viewport) -- Update so a window changing viewport won't lose focus.
local focus_request_flags = bit32.bor(2 --[[ImGuiFocusRequestFlags.UnlessBelowModal]], 1 --[[ImGuiFocusRequestFlags.RestoreFocusedChild]])
if focused_viewport.Window ~= nil then
ImGui.FocusWindow(focused_viewport.Window, focus_request_flags)
elseif focused_viewport.LastFocusedHadNavWindow then
ImGui.FocusTopMostWindowUnderOne(nil, nil, focused_viewport, focus_request_flags) -- Focus top most in viewport
else
ImGui.FocusWindow(nil, focus_request_flags) -- No window had focus last time viewport was focused
end
end
end
if focused_viewport then
focused_viewport.LastFocusedHadNavWindow = (g.NavWindow ~= nil) and (g.NavWindow.Viewport == focused_viewport)
end
end
-- Create/update main viewport with current platform position.
-- FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent.
local main_viewport = g.Viewports.Data[1]
assert(main_viewport.ID == IMGUI_VIEWPORT_DEFAULT_ID)
assert(main_viewport.Window == nil)
local main_viewport_pos
if viewports_enabled then
main_viewport_pos = g.PlatformIO.Platform_GetWindowPos(main_viewport)
else
main_viewport_pos = ImVec2(0.0, 0.0)
end
local main_viewport_size = g.IO.DisplaySize
local main_viewport_framebuffer_scale = g.IO.DisplayFramebufferScale
if viewports_enabled and (bit32.band(main_viewport.Flags, 4096 --[[ImGuiViewportFlags.IsMinimized]]) ~= 0) then
ImVec2_Copy(main_viewport_pos, main_viewport.Pos) -- Preserve last pos/size when minimized (FIXME: We don't do the same for Size outside of the viewport path)
ImVec2_Copy(main_viewport_size, main_viewport.Size)
main_viewport_framebuffer_scale = main_viewport.FramebufferScale
end
ImGui.AddUpdateViewport(nil, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_pos, main_viewport_size, bit32.bor(4 --[[ImGuiViewportFlags.OwnedByApp]], 2048 --[[ImGuiViewportFlags.CanHostOtherWindows]]))
local memory_compact_start_time = (g.GcCompactAll or g.IO.ConfigMemoryCompactTimer < 0.0) and 3.4028234663853e+38 --[[FLT_MAX]] or (g.Time - g.IO.ConfigMemoryCompactTimer)
g.CurrentDpiScale = 0.0
g.CurrentViewport = nil
g.MouseViewport = nil
local n = 1
while n <= g.Viewports.Size do
local viewport = g.Viewports.Data[n]
viewport.Idx = n
-- Erase unused viewports
if n > 1 and viewport.LastFrameActive < g.FrameCount - 2 then
ImGui.DestroyViewport(viewport)
-- n stays the same because we removed an element
else
local platform_funcs_available = viewport.PlatformWindowCreated
if viewports_enabled then
-- Update Position and Size (from Platform Window to ImGui) if requested.
-- We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities.
if (bit32.band(viewport.Flags, 4096 --[[ImGuiViewportFlags.IsMinimized]]) == 0) and platform_funcs_available then
-- Viewport->WorkPos and WorkSize will be updated below
if viewport.PlatformRequestMove then
ImVec2_Copy(viewport.Pos, g.PlatformIO.Platform_GetWindowPos(viewport))
ImVec2_Copy(viewport.LastPlatformPos, viewport.Pos)
end
if viewport.PlatformRequestResize then
viewport.Size = g.PlatformIO.Platform_GetWindowSize(viewport)
ImVec2_Copy(viewport.LastPlatformSize, viewport.Size)
end
if g.PlatformIO.Platform_GetWindowFramebufferScale ~= nil then
viewport.FramebufferScale = g.PlatformIO.Platform_GetWindowFramebufferScale(viewport)
end
end
end
-- Update/copy monitor info
ImGui.UpdateViewportPlatformMonitor(viewport)
-- Lock down space taken by menu bars and status bars + query initial insets from backend
-- Setup initial value for functions like BeginMainMenuBar(), DockSpaceOverViewport() etc.
viewport.WorkInsetMin = viewport.BuildWorkInsetMin
viewport.WorkInsetMax = viewport.BuildWorkInsetMax
viewport.BuildWorkInsetMin = ImVec2(0.0, 0.0)
viewport.BuildWorkInsetMax = ImVec2(0.0, 0.0)
if g.PlatformIO.Platform_GetWindowWorkAreaInsets ~= nil and platform_funcs_available then
local insets = g.PlatformIO.Platform_GetWindowWorkAreaInsets(viewport)
assert(insets.x >= 0.0 and insets.y >= 0.0 and insets.z >= 0.0 and insets.w >= 0.0)
viewport.BuildWorkInsetMin = ImVec2(insets.x, insets.y)
viewport.BuildWorkInsetMax = ImVec2(insets.z, insets.w)
end
viewport:UpdateWorkRect()
-- Garbage collect transient buffers of recently BG/FG drawlists
for dl_n = 1, 2 do -- IM_COUNTOF(viewport->BgFgDrawLists)
if viewport.BgFgDrawListsLastTimeActive[dl_n] < memory_compact_start_time and viewport.BgFgDrawLists[dl_n] ~= nil then
IM_DELETE(viewport.BgFgDrawLists, dl_n)
end
end
-- Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back.
viewport.Alpha = 1.0
-- Translate Dear ImGui windows when a Host Viewport has been moved
-- (This additionally keeps windows at the same place when ImGuiConfigFlags.ViewportsEnable is toggled!)
local viewport_delta_pos = viewport.Pos - viewport.LastPos
if (bit32.band(viewport.Flags, 2048 --[[ImGuiViewportFlags.CanHostOtherWindows]]) ~= 0) and (viewport_delta_pos.x ~= 0.0 or viewport_delta_pos.y ~= 0.0) then
ImGui.TranslateWindowsInViewport(viewport, viewport.LastPos, viewport.Pos, viewport.LastSize, viewport.Size)
end
-- Update DPI scale
local new_dpi_scale
if g.PlatformIO.Platform_GetWindowDpiScale and platform_funcs_available then
new_dpi_scale = g.PlatformIO.Platform_GetWindowDpiScale(viewport)
elseif viewport.PlatformMonitor ~= -1 then
new_dpi_scale = g.PlatformIO.Monitors.Data[viewport.PlatformMonitor].DpiScale
else
new_dpi_scale = (viewport.DpiScale ~= 0.0) and viewport.DpiScale or 1.0
end
assert(new_dpi_scale > 0.0 and new_dpi_scale < 99.0) -- Typical correct values would be between 1.0f and 4.0f
if viewport.DpiScale ~= 0.0 and new_dpi_scale ~= viewport.DpiScale then
local scale_factor = new_dpi_scale / viewport.DpiScale
if g.IO.ConfigDpiScaleViewports then
ImGui.ScaleWindowsInViewport(viewport, scale_factor)
end
-- if viewport == ImGui.GetMainViewport() then
-- g.PlatformInterface.SetWindowSize(viewport, viewport.Size * scale_factor)
-- end
-- Scale our window moving pivot so that the window will rescale roughly around the mouse position.
-- FIXME-VIEWPORT: This currently creates a resizing feedback loop when a window is straddling a DPI transition border.
-- (Minor: since our sizes do not perfectly linearly scale, deferring the click offset scale until we know the actual window scale ratio may get us slightly more precise mouse positioning.)
-- if g.MovingWindow ~= nil and g.MovingWindow.Viewport == viewport then
-- g.ActiveIdClickOffset = ImTrunc(g.ActiveIdClickOffset * scale_factor)
-- end
end
viewport.DpiScale = new_dpi_scale
n = n + 1
end
end
-- Update fallback monitor
g.PlatformMonitorsFullWorkRect = ImRect(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]], -3.4028234663853e+38 --[[FLT_MAX]], -3.4028234663853e+38 --[[FLT_MAX]])
if g.PlatformIO.Monitors.Size == 0 then
local monitor = g.FallbackMonitor
ImVec2_Copy(monitor.MainPos, main_viewport.Pos)
ImVec2_Copy(monitor.MainSize, main_viewport.Size)
ImVec2_Copy(monitor.WorkPos, main_viewport.WorkPos)
ImVec2_Copy(monitor.WorkSize, main_viewport.WorkSize)
monitor.DpiScale = main_viewport.DpiScale
g.PlatformMonitorsFullWorkRect:Add(monitor.WorkPos)
g.PlatformMonitorsFullWorkRect:Add(monitor.WorkPos + monitor.WorkSize)
else
g.FallbackMonitor = g.PlatformIO.Monitors.Data[1]
end
for i = 1, g.PlatformIO.Monitors.Size do
local monitor = g.PlatformIO.Monitors.Data[i]
g.PlatformMonitorsFullWorkRect:Add(monitor.WorkPos)
g.PlatformMonitorsFullWorkRect:Add(monitor.WorkPos + monitor.WorkSize)
end
if not viewports_enabled then
g.MouseViewport = main_viewport
return
end
-- Mouse handling: decide on the actual mouse viewport for this frame between the active/focused viewport and the hovered viewport.
-- Note that 'viewport_hovered' should skip over any viewport that has the ImGuiViewportFlags.NoInputs flags set.
local viewport_hovered = nil
if bit32.band(g.IO.BackendFlags, 4096 --[[ImGuiBackendFlags.HasMouseHoveredViewport]]) ~= 0 then
viewport_hovered = g.IO.MouseHoveredViewport and ImGui.FindViewportByID(g.IO.MouseHoveredViewport) or nil
if viewport_hovered and (bit32.band(viewport_hovered.Flags, 128 --[[ImGuiViewportFlags.NoInputs]]) ~= 0) then
viewport_hovered = ImGui.FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos) -- Backend failed to handle _NoInputs viewport: revert to our fallback.
end
else
-- If the backend doesn't know how to honor ImGuiViewportFlags.NoInputs, we do a search ourselves. Note that this search:
-- A) won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window.
-- B) won't take account of how the backend apply parent<>child relationship to secondary viewports, which affects their Z order.
-- C) uses LastFocusedStampCount as a flawed replacement for the last time a window was focused (we could/should fix that by introducing Focus functions in PlatformIO)
viewport_hovered = ImGui.FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos)
end
if viewport_hovered ~= nil then
g.MouseLastHoveredViewport = viewport_hovered
elseif g.MouseLastHoveredViewport == nil then
g.MouseLastHoveredViewport = g.Viewports.Data[1]
end
-- Update mouse reference viewport
-- (when moving a window we aim at its viewport, but this will be overwritten below if we go in drag and drop mode)
-- (MovingViewport->Viewport will be nil in the rare situation where the window disappared while moving, set UpdateMouseMovingWindowNewFrame() for details)
if g.MovingWindow and g.MovingWindow.Viewport then
g.MouseViewport = g.MovingWindow.Viewport
else
g.MouseViewport = g.MouseLastHoveredViewport
end
-- When dragging something, always refer to the last hovered viewport.
-- - when releasing a moving window we will revert to aiming behind (at viewport_hovered)
-- - when we are between viewports, our dragged preview will tend to show in the last viewport _even_ if we don't have tooltips in their viewports (when lacking monitor info)
-- - consider the case of holding on a menu item to browse child menus: even thou a mouse button is held, there's no active id because menu items only react on mouse release.
-- FIXME-VIEWPORT: This is essentially broken, when ImGuiBackendFlags.HasMouseHoveredViewport is set we want to trust when viewport_hovered==nil and use that.
local is_mouse_dragging_with_an_expected_destination = g.DragDropActive
if is_mouse_dragging_with_an_expected_destination and viewport_hovered == nil then
viewport_hovered = g.MouseLastHoveredViewport
end
if is_mouse_dragging_with_an_expected_destination or g.ActiveId == 0 or not ImGui.IsAnyMouseDown() then
if viewport_hovered ~= nil and viewport_hovered ~= g.MouseViewport and (bit32.band(viewport_hovered.Flags, 128 --[[ImGuiViewportFlags.NoInputs]]) == 0) then
g.MouseViewport = viewport_hovered
end
end
assert(g.MouseViewport ~= nil)
end
-- Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some)
function ImGui.UpdateViewportsEndFrame()
local g = GImGui
g.PlatformIO.Viewports:resize(0)
for i = 1, g.Viewports.Size do
repeat
local viewport = g.Viewports.Data[i]
ImVec2_Copy(viewport.LastPos, ImVec2(viewport.Pos.x, viewport.Pos.y))
ImVec2_Copy(viewport.LastSize, ImVec2(viewport.Size.x, viewport.Size.y))
if (viewport.LastFrameActive < g.FrameCount or viewport.Size.x <= 0.0 or viewport.Size.y <= 0.0) and (i > 1) then -- Always include main viewport in the list
break
end
if viewport.Window and not ImGui.IsWindowActiveAndVisible(viewport.Window) then
break
end
if i > 1 then
assert(viewport.Window ~= nil)
end
g.PlatformIO.Viewports:push_back(viewport)
until true
end
g.Viewports.Data[1]:ClearRequestFlags() -- Clear main viewport flags because UpdatePlatformWindows() won't do it and may not even be called
end
-- FIXME: We should ideally refactor the system to call this every frame (we currently don't)
--- @param window? ImGuiWindow
--- @param id ImGuiID
--- @param pos ImVec2
--- @param size ImVec2
--- @param flags ImGuiViewportFlags
--- @return ImGuiViewportP
function ImGui.AddUpdateViewport(window, id, pos, size, flags)
local g = GImGui
assert(id ~= 0)
flags = bit32.bor(flags, 1 --[[ImGuiViewportFlags.IsPlatformWindow]])
if window ~= nil then
local window_can_use_inputs = (bit32.band(window.Flags, bit32.bor(512 --[[ImGuiWindowFlags.NoMouseInputs]], 65536 --[[ImGuiWindowFlags.NoNavInputs]])) == 0)
if g.MovingWindow and g.MovingWindow.RootWindowDockTree == window then
flags = bit32.bor(flags, 128 --[[ImGuiViewportFlags.NoInputs]], 32 --[[ImGuiViewportFlags.NoFocusOnAppearing]])
end
if not window_can_use_inputs then
flags = bit32.bor(flags, 128 --[[ImGuiViewportFlags.NoInputs]])
end
if bit32.band(window.Flags, 4096 --[[ImGuiWindowFlags.NoFocusOnAppearing]]) ~= 0 then
flags = bit32.bor(flags, 32 --[[ImGuiViewportFlags.NoFocusOnAppearing]])
end
end
local viewport = ImGui.FindViewportByID(id)
if viewport then
--- @cast viewport ImGuiViewportP
-- Always update for main viewport as we are already pulling correct platform pos/size (see #4900)
local prev_pos = ImVec2(viewport.Pos.x, viewport.Pos.y)
local prev_size = ImVec2(viewport.Size.x, viewport.Size.y)
if not viewport.PlatformRequestMove or viewport.ID == IMGUI_VIEWPORT_DEFAULT_ID then
ImVec2_Copy(viewport.Pos, pos)
end
if not viewport.PlatformRequestResize or viewport.ID == IMGUI_VIEWPORT_DEFAULT_ID then
ImVec2_Copy(viewport.Size, size)
end
viewport.Flags = bit32.bor(flags, bit32.band(viewport.Flags, bit32.bor(4096 --[[ImGuiViewportFlags.IsMinimized]], 8192 --[[ImGuiViewportFlags.IsFocused]]))) -- Preserve existing flags
if prev_pos.x ~= viewport.Pos.x or prev_pos.y ~= viewport.Pos.y or prev_size.x ~= viewport.Size.x or prev_size.y ~= viewport.Size.y then
ImGui.UpdateViewportPlatformMonitor(viewport)
end
else
-- New viewport
viewport = ImGuiViewportP()
viewport.ID = id
viewport.Idx = g.Viewports.Size
ImVec2_Copy(viewport.Pos, pos)
ImVec2_Copy(viewport.LastPos, pos)
ImVec2_Copy(viewport.Size, size)
ImVec2_Copy(viewport.LastSize, size)
viewport.Flags = flags
ImGui.UpdateViewportPlatformMonitor(viewport)
g.Viewports:push_back(viewport)
g.ViewportCreatedCount = g.ViewportCreatedCount + 1
IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Add Viewport %08X '%s'", id, window and window.Name or "<NULL>")
-- We assume the window becomes front-most (even when ImGuiViewportFlags.NoFocusOnAppearing is used).
-- This is useful for our platform z-order heuristic when io.MouseHoveredViewport is not available.
g.ViewportFocusedStampCount = g.ViewportFocusedStampCount + 1
viewport.LastFocusedStampCount = g.ViewportFocusedStampCount
-- We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport.
-- We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame
g.DrawListSharedData.ClipRectFullscreen.x = ImMin(g.DrawListSharedData.ClipRectFullscreen.x, viewport.Pos.x)
g.DrawListSharedData.ClipRectFullscreen.y = ImMin(g.DrawListSharedData.ClipRectFullscreen.y, viewport.Pos.y)
g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport.Pos.x + viewport.Size.x)
g.DrawListSharedData.ClipRectFullscreen.w = ImMax(g.DrawListSharedData.ClipRectFullscreen.w, viewport.Pos.y + viewport.Size.y)
-- Store initial DpiScale before the OS platform window creation, based on expected monitor data.
-- This is so we can select an appropriate font size on the first frame of our window lifetime
viewport.DpiScale = ImGui.GetViewportPlatformMonitor(viewport).DpiScale
end
viewport.Window = window
viewport.LastFrameActive = g.FrameCount
viewport:UpdateWorkRect()
assert(window == nil or viewport.ID == window.ID)
-- Initialize FramebufferScale for main viewport and new viewports, before UpdatePlatformWindows() -> Platform_GetWindowFramebufferScale() has a chance to run.
-- FIXME: New viewport on monitors with a different scale will run for a frame with wrong scale? (#9502)
-- A workaround could be to cache last known FramebufferScale per monitor, so the heuristic can pull it from monitor.
if viewport.FramebufferScale.x <= 0.0 or viewport.FramebufferScale.y <= 0.0 or viewport.ID == IMGUI_VIEWPORT_DEFAULT_ID then
ImVec2_Copy(viewport.FramebufferScale, g.IO.DisplayFramebufferScale)
end
if window ~= nil then
window.ViewportOwned = true
end
return viewport
end
--- @param viewport ImGuiViewportP
function ImGui.DestroyViewport(viewport)
-- Clear references to this viewport in windows (window->ViewportId becomes the master data)
local g = GImGui
for i = 1, g.Windows.Size do
local window = g.Windows.Data[i]
if window.Viewport == viewport then
window.Viewport = nil
window.ViewportOwned = false
end
end
if viewport == g.MouseLastHoveredViewport then
g.MouseLastHoveredViewport = nil
end
-- Destroy
IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Delete Viewport %08X '%s'", viewport.ID, viewport.Window and viewport.Window.Name or "n/a")
ImGui.DestroyPlatformWindow(viewport) -- In most circumstances the platform window will already be destroyed here.
assert(not g.PlatformIO.Viewports:contains(viewport))
assert(g.Viewports.Data[viewport.Idx] == viewport)
g.Viewports:erase(viewport.Idx)
-- IM_DELETE(viewport)
end
-- FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten.
--- @param window ImGuiWindow
function ImGui.WindowSelectViewport(window)
local g = GImGui
local flags = window.Flags
window.ViewportAllowPlatformMonitorExtend = -1
-- Restore main viewport if multi-viewport is not supported by the backend
local main_viewport = ImGui.GetMainViewport() --[[@as ImGuiViewportP]]
if bit32.band(g.ConfigFlagsCurrFrame, 1024 --[[ImGuiConfigFlags.ViewportsEnable]]) == 0 then
ImGui.SetWindowViewport(window, main_viewport)
return
end
window.ViewportOwned = false
-- Appearing popups reset their viewport so they can inherit again
if bit32.band(flags, bit32.bor(67108864 --[[ImGuiWindowFlags.Popup]], 33554432 --[[ImGuiWindowFlags.Tooltip]])) ~= 0 and window.Appearing then
window.Viewport = nil
window.ViewportId = 0
end
if bit32.band(g.NextWindowData.HasFlags, 2048 --[[ImGuiNextWindowDataFlags.HasViewport]]) == 0 then
-- By default inherit from parent window
if window.Viewport == nil and window.ParentWindow and (not window.ParentWindow.IsFallbackWindow or window.ParentWindow.WasActive) then
window.Viewport = window.ParentWindow.Viewport
end
-- Attempt to restore saved viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPos' restored from .ini file
if window.Viewport == nil and window.ViewportId ~= 0 then
window.Viewport = ImGui.FindViewportByID(window.ViewportId) --[[@as ImGuiViewportP]]
if window.Viewport == nil and window.ViewportPos.x ~= 3.4028234663853e+38 --[[FLT_MAX]] and window.ViewportPos.y ~= 3.4028234663853e+38 --[[FLT_MAX]] then
window.Viewport = ImGui.AddUpdateViewport(window, window.ID, window.ViewportPos, window.Size, 0 --[[ImGuiViewportFlags.None]])
end
end
end
local lock_viewport = false
if bit32.band(g.NextWindowData.HasFlags, 2048 --[[ImGuiNextWindowDataFlags.HasViewport]]) ~= 0 then
-- Code explicitly request a viewport
window.Viewport = ImGui.FindViewportByID(g.NextWindowData.ViewportId) --[[@as ImGuiViewportP]]
window.ViewportId = g.NextWindowData.ViewportId -- Store ID even if Viewport isn't resolved yet.
if window.Viewport and bit32.band(window.Flags, 8388608 --[[ImGuiWindowFlags.DockNodeHost]]) ~= 0 and window.Viewport.Window ~= nil then
window.Viewport.Window = window
window.Viewport.ID = window.ID
window.ViewportId = window.ID -- Overwrite ID (always owned by node)
end
lock_viewport = true
elseif bit32.band(flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0 or bit32.band(flags, 268435456 --[[ImGuiWindowFlags.ChildMenu]]) ~= 0 then
-- Always inherit viewport from parent window
if window.DockNode and window.DockNode.HostWindow then
assert(window.DockNode.HostWindow.Viewport == window.ParentWindow.Viewport)
end
window.Viewport = window.ParentWindow.Viewport
elseif window.DockNode and window.DockNode.HostWindow then
-- This covers the "always inherit viewport from parent window" case for when a window reattach to a node that was just created mid-frame
window.Viewport = window.DockNode.HostWindow.Viewport
elseif bit32.band(flags, 33554432 --[[ImGuiWindowFlags.Tooltip]]) ~= 0 then
window.Viewport = g.MouseViewport
elseif ImGui.GetWindowAlwaysWantOwnViewport(window) then
window.Viewport = ImGui.AddUpdateViewport(window, window.ID, window.Pos, window.Size, 0 --[[ImGuiViewportFlags.None]])
elseif g.MovingWindow and g.MovingWindow.RootWindowDockTree == window and ImGui.IsMousePosValid() then
if window.Viewport ~= nil and window.Viewport.Window == window then
window.Viewport = ImGui.AddUpdateViewport(window, window.ID, window.Pos, window.Size, 0 --[[ImGuiViewportFlags.None]])
end
else
-- Merge into host viewport?
-- We cannot test window->ViewportOwned as it set lower in the function.
-- Testing (g.ActiveId == 0 || g.ActiveIdAllowOverlap) to avoid merging during a short-term widget interaction. Main intent was to avoid during resize (see #4212)
local try_to_merge_into_host_viewport = (window.Viewport and window == window.Viewport.Window and (g.ActiveId == 0 or g.ActiveIdAllowOverlap))
if try_to_merge_into_host_viewport then
ImGui.UpdateTryMergeWindowIntoHostViewports(window)
end
end
-- Fallback: merge in default viewport if z-order matches, otherwise create a new viewport
if window.Viewport == nil then
--- @cast main_viewport ImGuiViewportP
if not ImGui.UpdateTryMergeWindowIntoHostViewport(window, main_viewport) then
window.Viewport = ImGui.AddUpdateViewport(window, window.ID, window.Pos, window.Size, 0 --[[ImGuiViewportFlags.None]])
end
end
-- Mark window as allowed to protrude outside of its viewport and into the current monitor
if not lock_viewport then
if bit32.band(flags, bit32.bor(33554432 --[[ImGuiWindowFlags.Tooltip]], 67108864 --[[ImGuiWindowFlags.Popup]])) ~= 0 then
-- We need to take account of the possibility that mouse may become invalid.
-- Popups/Tooltip always set ViewportAllowPlatformMonitorExtend so GetWindowAllowedExtentRect() will return full monitor bounds.
local mouse_ref = bit32.band(flags, 33554432 --[[ImGuiWindowFlags.Tooltip]]) ~= 0 and g.IO.MousePos or g.BeginPopupStack.Data[g.BeginPopupStack.Size].OpenMousePos
local use_mouse_ref = not g.NavCursorVisible or not g.NavHighlightItemUnderNav or not g.NavWindow
local mouse_valid = ImGui.IsMousePosValid(mouse_ref)
if (window.Appearing or bit32.band(flags, bit32.bor(33554432 --[[ImGuiWindowFlags.Tooltip]], 268435456 --[[ImGuiWindowFlags.ChildMenu]])) ~= 0) and (not use_mouse_ref or mouse_valid) then
local pos_to_use = (use_mouse_ref and mouse_valid) and mouse_ref or ImGui.NavCalcPreferredRefPos(window.Flags)
window.ViewportAllowPlatformMonitorExtend = ImGui.FindPlatformMonitorForPos(pos_to_use)
else
window.ViewportAllowPlatformMonitorExtend = window.Viewport.PlatformMonitor
end
elseif window.Viewport and window ~= window.Viewport.Window and window.Viewport.Window and bit32.band(flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) == 0 and window.DockNode == nil then
-- When called from Begin() we don't have access to a proper version of the Hidden flag yet, so we replicate this code.
local will_be_visible = (window.DockIsActive and not window.DockTabIsVisible) and false or true
if bit32.band(window.Flags, 8388608 --[[ImGuiWindowFlags.DockNodeHost]]) ~= 0 and window.Viewport.LastFrameActive < g.FrameCount and will_be_visible then
-- Steal/transfer ownership
IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Window '%s' steal Viewport %08X from Window '%s'", window.Name, window.Viewport.ID, window.Viewport.Window.Name)
window.Viewport.Window = window
window.Viewport.ID = window.ID
window.Viewport.LastNameHash = 0
elseif not ImGui.UpdateTryMergeWindowIntoHostViewports(window) then -- Merge?
-- New viewport
window.Viewport = ImGui.AddUpdateViewport(window, window.ID, window.Pos, window.Size, 32 --[[ImGuiViewportFlags.NoFocusOnAppearing]])
end
elseif window.ViewportAllowPlatformMonitorExtend < 0 and bit32.band(flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) == 0 then
-- Regular (non-child, non-popup) windows by default are also allowed to protrude
-- Child windows are kept contained within their parent.
window.ViewportAllowPlatformMonitorExtend = window.Viewport.PlatformMonitor
end
end
-- Update flags
window.ViewportOwned = (window == window.Viewport.Window)
window.ViewportId = window.Viewport.ID
-- If the OS window has a title bar, hide our imgui title bar
-- if window.ViewportOwned and bit32.band(window.Viewport.Flags, ImGuiViewportFlags.NoDecoration) == 0 then
-- window.Flags = bit32.bor(window.Flags, ImGuiWindowFlags.NoTitleBar)
-- end
end
--- @param window ImGuiWindow
--- @param parent_window_in_stack? ImGuiWindow
function ImGui.WindowSyncOwnedViewport(window, parent_window_in_stack)
local g = GImGui
local viewport_rect_changed = false
-- Synchronize window --> viewport in most situations
-- Synchronize viewport -> window in case the platform window has been moved or resized from the OS/WM
if window.Viewport.PlatformRequestMove then
ImVec2_Copy(window.Pos, window.Viewport.Pos)
-- ImGui.MarkIniSettingsDirty(window)
elseif window.Viewport.Pos.x ~= window.Pos.x or window.Viewport.Pos.y ~= window.Pos.y then
viewport_rect_changed = true
ImVec2_Copy(window.Viewport.Pos, window.Pos)
end
if window.Viewport.PlatformRequestResize then
ImVec2_Copy(window.Size, window.Viewport.Size)
ImVec2_Copy(window.SizeFull, window.Viewport.Size)
-- ImGui.MarkIniSettingsDirty(window)
elseif window.Viewport.Size.x ~= window.Size.x or window.Viewport.Size.y ~= window.Size.y then
viewport_rect_changed = true
ImVec2_Copy(window.Viewport.Size, window.Size)
end
window.Viewport:UpdateWorkRect()
-- The viewport may have changed monitor since the global update in UpdateViewportsNewFrame()
-- Either a SetNextWindowPos() call in the current frame or a SetWindowPos() call in the previous frame may have this effect.
if viewport_rect_changed then
ImGui.UpdateViewportPlatformMonitor(window.Viewport)
end
-- Update common viewport flags
local viewport_flags_to_clear = bit32.bor(1024 --[[ImGuiViewportFlags.TopMost]], 16 --[[ImGuiViewportFlags.NoTaskBarIcon]], 8 --[[ImGuiViewportFlags.NoDecoration]], 256 --[[ImGuiViewportFlags.NoRendererClear]])
local viewport_flags = bit32.band(window.Viewport.Flags, bit32.bnot(viewport_flags_to_clear))
local window_flags = window.Flags
local is_modal = bit32.band(window_flags, 134217728 --[[ImGuiWindowFlags.Modal]]) ~= 0
local is_short_lived_floating_window = bit32.band(window_flags, bit32.bor(268435456 --[[ImGuiWindowFlags.ChildMenu]], 33554432 --[[ImGuiWindowFlags.Tooltip]], 67108864 --[[ImGuiWindowFlags.Popup]])) ~= 0
if bit32.band(window_flags, 33554432 --[[ImGuiWindowFlags.Tooltip]]) ~= 0 then
viewport_flags = bit32.bor(viewport_flags, 1024 --[[ImGuiViewportFlags.TopMost]])
end
if (g.IO.ConfigViewportsNoTaskBarIcon or is_short_lived_floating_window) and not is_modal then
viewport_flags = bit32.bor(viewport_flags, 16 --[[ImGuiViewportFlags.NoTaskBarIcon]])
end
if g.IO.ConfigViewportsNoDecoration or is_short_lived_floating_window then
viewport_flags = bit32.bor(viewport_flags, 8 --[[ImGuiViewportFlags.NoDecoration]])
end
-- Not correct to set modal as topmost because:
-- - Because other popups can be stacked above a modal (e.g. combo box in a modal)
-- - ImGuiViewportFlags.TopMost is currently handled different in backends: in Win32 it is "appear top most" whereas in GLFW and SDL it is "stay topmost"
-- if bit32.band(window_flags, ImGuiWindowFlags.Modal) ~= 0 then
-- viewport_flags = bit32.bor(viewport_flags, ImGuiViewportFlags.TopMost)
-- end
-- For popups and menus that may be protruding out of their parent viewport, we enable _NoFocusOnClick so that clicking on them
-- won't steal the OS focus away from their parent window (which may be reflected in OS the title bar decoration).
-- Setting _NoFocusOnClick would technically prevent us from bringing back to front in case they are being covered by an OS window from a different app,
-- but it shouldn't be much of a problem considering those are already popups that are closed when clicking elsewhere.
if is_short_lived_floating_window and not is_modal then
viewport_flags = bit32.bor(viewport_flags, 32 --[[ImGuiViewportFlags.NoFocusOnAppearing]], 64 --[[ImGuiViewportFlags.NoFocusOnClick]])
end
-- We can overwrite viewport flags using ImGuiWindowClass (advanced users)
if window.WindowClass.ViewportFlagsOverrideSet ~= 0 then
viewport_flags = bit32.bor(viewport_flags, window.WindowClass.ViewportFlagsOverrideSet)
end
if window.WindowClass.ViewportFlagsOverrideClear ~= 0 then
viewport_flags = bit32.band(viewport_flags, bit32.bnot(window.WindowClass.ViewportFlagsOverrideClear))
end
-- We can also tell the backend that clearing the platform window won't be necessary,
-- as our window background is filling the viewport and we have disabled BgAlpha.
-- FIXME: Work on support for per-viewport transparency (#2766)
if bit32.band(window_flags, 128 --[[ImGuiWindowFlags.NoBackground]]) == 0 then
viewport_flags = bit32.bor(viewport_flags, 256 --[[ImGuiViewportFlags.NoRendererClear]])
end
window.Viewport.Flags = viewport_flags
window.Viewport.PlatformIconData = window.WindowClass.PlatformIconData
-- Update parent viewport ID
-- (the !IsFallbackWindow test mimic the one done in WindowSelectViewport())
if window.WindowClass.ParentViewportId ~= 0xFFFFFFFF then
local old_parent_viewport_id = window.Viewport.ParentViewportId
window.Viewport.ParentViewportId = window.WindowClass.ParentViewportId
if window.Viewport.ParentViewportId ~= old_parent_viewport_id then
window.Viewport.ParentViewport = ImGui.FindViewportByID(window.Viewport.ParentViewportId) --[[@as ImGuiViewport]]
end
elseif bit32.band(window_flags, bit32.bor(67108864 --[[ImGuiWindowFlags.Popup]], 33554432 --[[ImGuiWindowFlags.Tooltip]])) ~= 0 and parent_window_in_stack and (not parent_window_in_stack.IsFallbackWindow or parent_window_in_stack.WasActive) then
window.Viewport.ParentViewport = parent_window_in_stack.Viewport
window.Viewport.ParentViewportId = parent_window_in_stack.Viewport.ID
else
if g.IO.ConfigViewportsNoDefaultParent then
window.Viewport.ParentViewport = nil
else
window.Viewport.ParentViewport = ImGui.GetMainViewport()
end
window.Viewport.ParentViewportId = g.IO.ConfigViewportsNoDefaultParent and 0 or IMGUI_VIEWPORT_DEFAULT_ID
end
end
-- Called by user at the end of the main loop, after EndFrame()
-- This will handle the creation/update of all OS windows via function defined in the ImGuiPlatformIO api.
function ImGui.UpdatePlatformWindows()
local g = GImGui
assert(g.FrameCountEnded == g.FrameCount, "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?")
assert(g.FrameCountPlatformEnded < g.FrameCount)
g.FrameCountPlatformEnded = g.FrameCount
if bit32.band(g.ConfigFlagsCurrFrame, 1024 --[[ImGuiConfigFlags.ViewportsEnable]]) == 0 then
return
end
-- Create/resize/destroy platform windows to match each active viewport.
-- Skip the main viewport (index 0), which is always fully handled by the application!
for i = 2, g.Viewports.Size do
repeat
local viewport = g.Viewports.Data[i]
-- Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window
-- (the implicit/fallback Debug##Default window will be registering its viewport then be disabled, causing a dummy DestroyPlatformWindow to be made each frame)
local destroy_platform_window = false
destroy_platform_window = destroy_platform_window or (viewport.LastFrameActive < g.FrameCount - 1)
destroy_platform_window = destroy_platform_window or (viewport.Window and not ImGui.IsWindowActiveAndVisible(viewport.Window)) --[[@as bool]]
if destroy_platform_window then
ImGui.DestroyPlatformWindow(viewport)
break
end
-- New windows that appears directly in a new viewport won't always have a size on their first frame
if viewport.LastFrameActive < g.FrameCount or viewport.Size.x <= 0 or viewport.Size.y <= 0 then
break
end
-- Create window
local is_new_platform_window = not viewport.PlatformWindowCreated
if is_new_platform_window then
IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Create Platform Window %08X '%s'", viewport.ID, viewport.Window and viewport.Window.Name or "n/a")
g.PlatformIO.Platform_CreateWindow(viewport)
if g.PlatformIO.Renderer_CreateWindow ~= nil then
g.PlatformIO.Renderer_CreateWindow(viewport)
end
g.PlatformWindowsCreatedCount = g.PlatformWindowsCreatedCount + 1
viewport.LastNameHash = 0
ImVec2_CopyV(viewport.LastPlatformPos, 3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]])
ImVec2_CopyV(viewport.LastPlatformSize, 3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]]) -- By clearing those we'll enforce a call to Platform_SetWindowPos/Size below, before Platform_ShowWindow (FIXME: Is that necessary?)
ImVec2_Copy(viewport.LastRendererSize, viewport.Size) -- We don't need to call Renderer_SetWindowSize() as it is expected Renderer_CreateWindow() already did it.
viewport.PlatformWindowCreated = true
end
-- Apply Position and Size (from ImGui to Platform/Renderer backends)
if (viewport.LastPlatformPos.x ~= viewport.Pos.x or viewport.LastPlatformPos.y ~= viewport.Pos.y) and not viewport.PlatformRequestMove then
g.PlatformIO.Platform_SetWindowPos(viewport, viewport.Pos)
end
if (viewport.LastPlatformSize.x ~= viewport.Size.x or viewport.LastPlatformSize.y ~= viewport.Size.y) and not viewport.PlatformRequestResize then
g.PlatformIO.Platform_SetWindowSize(viewport, viewport.Size)
end
if (viewport.LastRendererSize.x ~= viewport.Size.x or viewport.LastRendererSize.y ~= viewport.Size.y) and g.PlatformIO.Renderer_SetWindowSize ~= nil then
g.PlatformIO.Renderer_SetWindowSize(viewport, viewport.Size)
end
ImVec2_Copy(viewport.LastPlatformPos, viewport.Pos)
ImVec2_Copy(viewport.LastPlatformSize, viewport.Size)
ImVec2_Copy(viewport.LastRendererSize, viewport.Size)
-- Update title bar (if it changed)
local window_for_title = GetWindowForTitleDisplay(viewport.Window)
if window_for_title ~= nil then
local title = window_for_title.Name
local title_begin = 1
local title_end = ImGui.FindRenderedTextEnd(title)
local title_hash = ImHashStr(title, title_end - title_begin)
if viewport.LastNameHash ~= title_hash then
-- This still creates new strings
g.PlatformIO.Platform_SetWindowTitle(viewport, string.sub(title, title_begin, title_end - 1))
viewport.LastNameHash = title_hash
end
end
-- Update alpha (if it changed)
if viewport.LastAlpha ~= viewport.Alpha and g.PlatformIO.Platform_SetWindowAlpha ~= nil then
g.PlatformIO.Platform_SetWindowAlpha(viewport, viewport.Alpha)
end
viewport.LastAlpha = viewport.Alpha
-- Optional, general purpose call to allow the backend to perform general book-keeping even if things haven't changed.
if g.PlatformIO.Platform_UpdateWindow ~= nil then
g.PlatformIO.Platform_UpdateWindow(viewport)
end
if is_new_platform_window then
-- On startup ensure new platform window don't steal focus (give it a few frames, as nested contents may lead to viewport being created a few frames late)
if g.FrameCount < 3 then
viewport.Flags = bit32.bor(viewport.Flags, 32 --[[ImGuiViewportFlags.NoFocusOnAppearing]])
end
-- Show window
g.PlatformIO.Platform_ShowWindow(viewport)
end
-- Clear request flags
viewport:ClearRequestFlags()
until true
end
end
-- This is a default/basic function for performing the rendering/swap of multiple Platform Windows.
-- Custom renderers may prefer to not call this function at all, and instead iterate the publicly exposed platform data and handle rendering/sync themselves.
-- The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself:
--
-- local platform_io = ImGui.GetPlatformIO()
-- for i = 2, platform_io.Viewports.Size do
-- if bit32.band(platform_io.Viewports.Data[i].Flags, ImGuiViewportFlags.IsMinimized) == 0 then
-- MyRenderFunction(platform_io.Viewports.Data[i], my_args)
-- end
-- end
-- for i = 2, platform_io.Viewports.Size do
-- if bit32.band(platform_io.Viewports.Data[i].Flags, ImGuiViewportFlags.IsMinimized) == 0 then
-- MySwapBufferFunction(platform_io.Viewports.Data[i], my_args)
-- end
-- end
--
function ImGui.RenderPlatformWindowsDefault(platform_render_arg, renderer_render_arg)
-- Skip the main viewport (index 1 in Lua), which is always fully handled by the application!
local platform_io = ImGui.GetPlatformIO()
-- First pass: render all windows
for i = 2, platform_io.Viewports.Size do
local viewport = platform_io.Viewports.Data[i]
if bit32.band(viewport.Flags, 4096 --[[ImGuiViewportFlags.IsMinimized]]) == 0 then
if platform_io.Platform_RenderWindow then
platform_io.Platform_RenderWindow(viewport, platform_render_arg)
end
if platform_io.Renderer_RenderWindow then
platform_io.Renderer_RenderWindow(viewport, renderer_render_arg)
end
end
end
-- Second pass: swap buffers for all windows
for i = 2, platform_io.Viewports.Size do
local viewport = platform_io.Viewports.Data[i]
if bit32.band(viewport.Flags, 4096 --[[ImGuiViewportFlags.IsMinimized]]) == 0 then
if platform_io.Platform_SwapBuffers then
platform_io.Platform_SwapBuffers(viewport, platform_render_arg)
end
if platform_io.Renderer_SwapBuffers then
platform_io.Renderer_SwapBuffers(viewport, renderer_render_arg)
end
end
end
end
--- @param pos ImVec2
function ImGui.FindPlatformMonitorForPos(pos)
local g = GImGui
for monitor_n = 1, g.PlatformIO.Monitors.Size do
local monitor = g.PlatformIO.Monitors.Data[monitor_n]
if ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize):Contains(pos) then
return monitor_n
end
end
return -1
end
-- Search for the monitor with the largest intersection area with the given rectangle
-- We generally try to avoid searching loops but the monitor count should be very small here
-- FIXME-OPT: We could test the last monitor used for that viewport first, and early
--- @param rect ImRect
--- @return int
function ImGui.FindPlatformMonitorForRect(rect)
local g = GImGui
local monitor_count = g.PlatformIO.Monitors.Size
if monitor_count <= 1 then
return monitor_count
end
-- Use a minimum threshold of 1.0f so a zero-sized rect won't false positive, and will still find the correct monitor given its position.
-- This is necessary for tooltips which always resize down to zero at first.
local surface_threshold = math.max(rect:GetWidth() * rect:GetHeight() * 0.5, 1.0)
local best_monitor_n = 1 -- Default to the first monitor as fallback
local best_monitor_surface = 0.001
for monitor_n = 1, g.PlatformIO.Monitors.Size do
if best_monitor_surface >= surface_threshold then
break
end
local monitor = g.PlatformIO.Monitors.Data[monitor_n]
local monitor_rect = ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize)
if monitor_rect:Contains(rect) then
return monitor_n
end
local overlapping_rect = ImRect(rect.Min.x, rect.Min.y, rect.Max.x, rect.Max.y)
overlapping_rect:ClipWithFull(monitor_rect)
local overlapping_surface = overlapping_rect:GetWidth() * overlapping_rect:GetHeight()
if overlapping_surface > best_monitor_surface then
best_monitor_surface = overlapping_surface
best_monitor_n = monitor_n
end
end
return best_monitor_n
end
-- Update monitor from viewport rectangle (we'll use this info to clamp windows and save windows lost in a removed monitor)
--- @param viewport ImGuiViewportP
function ImGui.UpdateViewportPlatformMonitor(viewport)
viewport.PlatformMonitor = ImGui.FindPlatformMonitorForRect(viewport:GetMainRect())
end
-- Return value is always ~= nil, but don't hold on it across frames.
--- @param viewport_p ImGuiViewport
--- @return ImGuiPlatformMonitor
function ImGui.GetViewportPlatformMonitor(viewport_p)
local g = GImGui
local viewport = viewport_p --- @cast viewport ImGuiViewportP
local monitor_idx = viewport.PlatformMonitor
if monitor_idx >= 1 and monitor_idx <= g.PlatformIO.Monitors.Size then
return g.PlatformIO.Monitors.Data[monitor_idx]
end
return g.FallbackMonitor
end
--- @param viewport ImGuiViewportP
function ImGui.DestroyPlatformWindow(viewport)
local g = GImGui
if viewport.PlatformWindowCreated then
IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Destroy Platform Window %08X '%s'", viewport.ID, viewport.Window and viewport.Window.Name or "n/a")
if g.PlatformIO.Renderer_DestroyWindow then
g.PlatformIO.Renderer_DestroyWindow(viewport)
end
if g.PlatformIO.Platform_DestroyWindow then
g.PlatformIO.Platform_DestroyWindow(viewport)
end
assert(viewport.RendererUserData == nil and viewport.PlatformUserData == nil)
-- Don't clear PlatformWindowCreated for the main viewport, as we initially set that up to true in Initialize()
-- The righter way may be to leave it to the backend to set this flag all-together, and made the flag public.
if viewport.ID ~= IMGUI_VIEWPORT_DEFAULT_ID then
viewport.PlatformWindowCreated = false
end
else
assert(viewport.RendererUserData == nil and viewport.PlatformUserData == nil and viewport.PlatformHandle == nil)
end
viewport.RendererUserData = nil
viewport.PlatformUserData = nil
viewport.PlatformHandle = nil
viewport:ClearRequestFlags()
end
function ImGui.DestroyPlatformWindows()
-- We call the destroy window on every viewport (including the main viewport, index 1) to give a chance to the backend
-- to clear any data they may have stored in e.g. PlatformUserData, RendererUserData.
-- It is convenient for the platform backend code to store something in the main viewport, in order for e.g. the mouse handling
-- code to operator a consistent manner.
-- It is expected that the backend can handle calls to Renderer_DestroyWindow/Platform_DestroyWindow without
-- crashing if it doesn't have data stored.
local g = GImGui
for _, viewport in g.Viewports:iter() do
ImGui.DestroyPlatformWindow(viewport)
end
end
-------------------------------------------------------------------------------
-- [SECTION] METRICS/DEBUGGER WINDOW
-------------------------------------------------------------------------------
-- - DebugRenderViewportThumbnail() [Internal]
-- - RenderViewportsThumbnails() [Internal]
-------------------------------------------------------------------------------
--- @param draw_list ImDrawList
--- @param viewport ImGuiViewport
--- @param bb ImRect
function ImGui.DebugRenderViewportThumbnail(draw_list, viewport, bb)
local g = GImGui
local window = g.CurrentWindow
local scale = bb:GetSize() / viewport.Size
local off = bb.Min - viewport.Pos * scale
local alpha_mul = (bit32.band(viewport.Flags, 4096 --[[ImGuiViewportFlags.IsMinimized]]) ~= 0) and 0.30 or 1.00
window.DrawList:AddRectFilled(bb.Min, bb.Max, ImGui.GetColorU32(5 --[[ImGuiCol.Border]], alpha_mul * 0.40))
for _, thumb_window in g.Windows:iter() do
repeat
if not thumb_window.WasActive or bit32.band(thumb_window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0 then
break
end
if thumb_window.Viewport ~= viewport then
break
end
local thumb_r = thumb_window:Rect()
local title_r = thumb_window:TitleBarRect()
thumb_r = ImRect(ImTrunc(off + thumb_r.Min * scale), ImTrunc(off + thumb_r.Max * scale))
title_r = ImRect(ImTrunc(off + title_r.Min * scale), ImTrunc(off + ImVec2(title_r.Max.x, title_r.Min.y + title_r:GetHeight() * 3.0) * scale)) -- Exaggerate title bar height
thumb_r:ClipWithFull(bb)
title_r:ClipWithFull(bb)
local window_is_focused = g.NavWindow and thumb_window.RootWindowForTitleBarHighlight == g.NavWindow.RootWindowForTitleBarHighlight
window.DrawList:AddRectFilled(thumb_r.Min, thumb_r.Max, ImGui.GetColorU32(2 --[[ImGuiCol.WindowBg]], alpha_mul))
window.DrawList:AddRectFilled(title_r.Min, title_r.Max, ImGui.GetColorU32(window_is_focused and 11 --[[ImGuiCol.TitleBgActive]] or 10 --[[ImGuiCol.TitleBg]], alpha_mul))
window.DrawList:AddRect(thumb_r.Min, thumb_r.Max, ImGui.GetColorU32(5 --[[ImGuiCol.Border]], alpha_mul))
window.DrawList:AddText(g.Font, g.FontSize * 1.0, title_r.Min, ImGui.GetColorU32(0 --[[ImGuiCol.Text]], alpha_mul), thumb_window.Name, 1, ImGui.FindRenderedTextEnd(thumb_window.Name))
until true
end
draw_list:AddRect(bb.Min, bb.Max, ImGui.GetColorU32(5 --[[ImGuiCol.Border]], alpha_mul))
if viewport.ID == g.DebugMetricsConfig.HighlightViewportID then
window.DrawList:AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255))
end
end
function ImGui.RenderViewportsThumbnails()
local g = GImGui
local window = g.CurrentWindow
-- Draw monitor and calculate their boundaries
local SCALE = 1.0 / 8.0
local bb_full = ImRect(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]], -3.4028234663853e+38 --[[FLT_MAX]], -3.4028234663853e+38 --[[FLT_MAX]])
for _, monitor in g.PlatformIO.Monitors:iter() do
bb_full:Add(ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize))
end
local p = window.DC.CursorPos
local off = p - bb_full.Min * SCALE
for _, monitor in g.PlatformIO.Monitors:iter() do
local monitor_draw_bb = ImRect(off + monitor.MainPos * SCALE, off + (monitor.MainPos + monitor.MainSize) * SCALE)
window.DrawList:AddRect(monitor_draw_bb.Min, monitor_draw_bb.Max, (g.DebugMetricsConfig.HighlightMonitorIdx == g.PlatformIO.Monitors:index_from_ptr(monitor)) and IM_COL32(255, 255, 0, 255) or ImGui.GetColorU32(5 --[[ImGuiCol.Border]]), 4.0)
window.DrawList:AddRectFilled(monitor_draw_bb.Min, monitor_draw_bb.Max, ImGui.GetColorU32(5 --[[ImGuiCol.Border]], 0.10), 4.0)
end
-- Draw viewports
for _, viewport in g.Viewports:iter() do
local viewport_draw_bb = ImRect(off + viewport.Pos * SCALE, off + (viewport.Pos + viewport.Size) * SCALE)
ImGui.DebugRenderViewportThumbnail(window.DrawList, viewport, viewport_draw_bb)
end
ImGui.Dummy(bb_full:GetSize() * SCALE)
end
--- @param idx ImGuiCol
--- @return ImVec4 # live reference to the style color (like upstream's const&)
function ImGui.GetStyleColorVec4(idx)
return GImGui.Style.Colors[idx]
end
-- Until 1.89 (August 2022, IMGUI_VERSION_NUM < 18814) it was legal to use SetCursorPos()/SetCursorScreenPos()
-- to extend contents size of our parent container (e.g. window contents size, which is used for auto-resizing
-- windows, table column contents size used for auto-resizing columns, group size).
-- This was causing issues and ambiguities and we needed to retire that.
--
-- Advanced:
-- - For reference, old logic was causing issues because it meant that SetCursorScreenPos(GetCursorScreenPos())
-- had a side-effect on layout! In particular this caused problem to compute group boundaries.
-- e.g. BeginGroup() + SomeItem() + SetCursorScreenPos(GetCursorScreenPos()) + EndGroup() would cause the
-- group to be taller because auto-sizing generally adds padding on bottom and right side.
-- - While this code is a little twisted, no-one would expect SetXXX(GetXXX()) to have a side-effect.
-- Using vertical alignment patterns would frequently trigger this sorts of issue.
-- - See https://github.com/ocornut/imgui/issues/5548 for more details.
function ImGui.ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
local g = GImGui
local window = g.CurrentWindow
assert(window.DC.IsSetPos)
window.DC.IsSetPos = false
if window.DC.CursorPos.x <= window.DC.CursorMaxPos.x and window.DC.CursorPos.y <= window.DC.CursorMaxPos.y then return end
if window.SkipItems then return end
-- Upstream asserts (user error): "Code uses SetCursorPos()/SetCursorScreenPos() to extend window/parent boundaries.
-- Please submit an item e.g. Dummy() afterwards". We keep the old, tolerant behavior instead of erroring.
window.DC.CursorMaxPos = ImMax(window.DC.CursorMaxPos, window.DC.CursorPos)
end
-------------------------------------------------------------------------------
-- [SECTION] POPUPS: modals, context popups, dimming
-------------------------------------------------------------------------------
local function GetWindowDisplayLayer(window)
return (bit32.band(window.Flags, 33554432 --[[ImGuiWindowFlags.Tooltip]]) ~= 0) and 1 or 0
end
--- @return int # 0-based display index (like upstream), -1 if not found
function ImGui.FindWindowDisplayIndex(window)
local g = GImGui
local i = g.Windows:find_index(window)
return i and (i - 1) or -1
end
function ImGui.BringWindowToDisplayBack(window)
local g = GImGui
if g.Windows.Data[1] == window then return end
local i = g.Windows:find_index(window)
if i then
table.remove(g.Windows.Data, i)
table.insert(g.Windows.Data, 1, window)
end
end
function ImGui.FindBottomMostVisibleWindowWithinBeginStack(parent_window)
local g = GImGui
local bottom_most_visible_window = parent_window
for i = ImGui.FindWindowDisplayIndex(parent_window), 0, -1 do
local window = g.Windows.Data[i + 1]
if bit32.band(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) == 0 then
if not ImGui.IsWindowWithinBeginStackOf(window, parent_window) then break end
if ImGui.IsWindowActiveAndVisible(window) and GetWindowDisplayLayer(window) <= GetWindowDisplayLayer(parent_window) then
bottom_most_visible_window = window
end
end
end
return bottom_most_visible_window
end
-- See Demo->Stacked Modal to confirm what this is for.
function ImGui.GetTopMostAndVisiblePopupModal()
local g = GImGui
for n = g.OpenPopupStack.Size, 1, -1 do
local popup = g.OpenPopupStack.Data[n].Window
if popup and bit32.band(popup.Flags, 134217728 --[[ImGuiWindowFlags.Modal]]) ~= 0 and ImGui.IsWindowActiveAndVisible(popup) then
return popup
end
end
return nil
end
function ImGui.RenderDimmedBackgroundBehindWindow(window, col)
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then return end
local viewport = window.Viewport
local viewport_rect = viewport:GetMainRect()
-- Draw behind window by moving the draw command at the FRONT of the draw list
-- Draw list have been trimmed already, hence the explicit recreation of a draw command if missing.
-- FIXME: This is a little bit complicated, solely to avoid creating/injecting an extra drawlist in drawdata.
local draw_list = (window.RootWindowDockTree or window.RootWindow).DrawList
draw_list:ChannelsMerge()
if draw_list.CmdBuffer.Size == 0 then draw_list:AddDrawCmd() end
draw_list:PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false) -- FIXME: Need to strictly ensure ImDrawCmd are not merged
draw_list:AddRectFilled(viewport_rect.Min, viewport_rect.Max, col)
local cmd = draw_list.CmdBuffer.Data[draw_list.CmdBuffer.Size]
draw_list.CmdBuffer:pop_back()
draw_list.CmdBuffer:push_front(cmd)
draw_list:AddDrawCmd() -- We need to create a command as CmdBuffer.back().IdxOffset won't be correct if we append to same command.
draw_list:PopClipRect()
end
-- Important: AddWindowToDrawData() has not been called yet, meaning DockNodeHost windows needs a DrawList:ChannelsMerge() before usage.
-- We call ChannelsMerge() lazily here at it is faster that doing a full iteration of g.Windows[] prior to calling RenderDimmedBackgrounds().
function ImGui.RenderDimmedBackgrounds()
local g = GImGui
local modal_window = ImGui.GetTopMostAndVisiblePopupModal()
if g.DimBgRatio <= 0.0 and g.NavWindowingHighlightAlpha <= 0.0 then return end
local dim_bg_for_modal = (modal_window ~= nil)
local dim_bg_for_window_list = (g.NavWindowingTargetAnim ~= nil and g.NavWindowingTargetAnim.Active)
if not dim_bg_for_modal and not dim_bg_for_window_list then return end
if dim_bg_for_modal then
-- Draw dimming behind modal or a begin stack child, whichever comes first in draw order.
local dim_behind_window = ImGui.FindBottomMostVisibleWindowWithinBeginStack(modal_window)
ImGui.RenderDimmedBackgroundBehindWindow(dim_behind_window, ImGui.GetColorU32(modal_window.DC.ModalDimBgColor, g.DimBgRatio, true))
elseif dim_bg_for_window_list then
-- Draw dimming behind Ctrl+Tab target window and behind Ctrl+Tab UI window
ImGui.RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, ImGui.GetColorU32(61 --[[ImGuiCol.NavWindowingDimBg]], g.DimBgRatio))
if g.NavWindowingListWindow ~= nil and g.NavWindowingListWindow.Active and g.NavWindowingListWindow.Viewport and g.NavWindowingListWindow.Viewport ~= g.NavWindowingTargetAnim.Viewport then
ImGui.RenderDimmedBackgroundBehindWindow(g.NavWindowingListWindow, ImGui.GetColorU32(61 --[[ImGuiCol.NavWindowingDimBg]], g.DimBgRatio))
end
-- Draw border around Ctrl+Tab target window
local window = g.NavWindowingTargetAnim
local viewport = window.Viewport
local distance = g.FontSize
local bb = window:Rect()
bb:Expand(distance)
if bb:GetWidth() >= viewport.Size.x and bb:GetHeight() >= viewport.Size.y then bb:Expand(-distance - 1.0) end -- If a window fits the entire viewport, adjust its highlight inward
window.DrawList:ChannelsMerge()
if window.DrawList.CmdBuffer.Size == 0 then window.DrawList:AddDrawCmd() end
window.DrawList:PushClipRect(viewport.Pos, viewport.Pos + viewport.Size)
window.DrawList:AddRect(bb.Min, bb.Max, ImGui.GetColorU32(60 --[[ImGuiCol.NavWindowingHighlight]], g.NavWindowingHighlightAlpha), window.WindowRounding, 3.0) -- FIXME-DPI
window.DrawList:PopClipRect()
end
end
function ImGui.ClosePopupsExceptModals()
local g = GImGui
local popup_count_to_keep = g.OpenPopupStack.Size
while popup_count_to_keep > 0 do
local window = g.OpenPopupStack.Data[popup_count_to_keep].Window
if not window or bit32.band(window.Flags, 134217728 --[[ImGuiWindowFlags.Modal]]) ~= 0 then break end
popup_count_to_keep = popup_count_to_keep - 1
end
if popup_count_to_keep < g.OpenPopupStack.Size then -- This test is not required but it allows to set a convenient breakpoint on the statement below
ImGui.ClosePopupToLevel(popup_count_to_keep, true)
end
end
-- If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
-- Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup).
-- - p_open is returned set back to false by BeginPopupModal() when popup is not open.
-- - if you set p_open to false before calling BeginPopupModal(), it will close the popup.
--- @return bool visible, bool? p_open # p_open is the updated value of the optional close button flag
function ImGui.BeginPopupModal(name, p_open, flags)
if flags == nil then flags = 0 end
local g = GImGui
local window = g.CurrentWindow
local id = window:GetID(name)
if not ImGui.IsPopupOpen(id, 0 --[[ImGuiPopupFlags.None]]) then
g.NextWindowData:ClearFlags() -- We behave like Begin() and need to consume those values
if p_open then p_open = false end
return false, p_open
end
-- Center modal windows by default for increased visibility
-- (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)
-- FIXME: Should test for (PosCond & window.SetWindowPosAllowFlags) with the upcoming window.
if bit32.band(g.NextWindowData.HasFlags, 1 --[[ImGuiNextWindowDataFlags.HasPos]]) == 0 then
local viewport = window.WasActive and window.Viewport or ImGui.GetMainViewport() -- FIXME-VIEWPORT: What may be our reference viewport?
ImGui.SetNextWindowPos(viewport:GetCenter(), 4 --[[ImGuiCond.FirstUseEver]], ImVec2(0.5, 0.5))
end
flags = bit32.bor(flags, 67108864 --[[ImGuiWindowFlags.Popup]], 134217728 --[[ImGuiWindowFlags.Modal]], 32 --[[ImGuiWindowFlags.NoCollapse]], 524288 --[[ImGuiWindowFlags.NoDocking]])
local is_open
p_open, is_open = ImGui.Begin(name, p_open, flags)
if not is_open or (p_open ~= nil and not p_open) then -- NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
ImGui.EndPopup()
if is_open then ImGui.ClosePopupToLevel(g.BeginPopupStack.Size, true) end
return false, p_open
end
return is_open, p_open
end
function ImGui.IsPopupOpenRequestForWindow(popup_flags)
local g = GImGui
local mouse_button = ImGui.GetMouseButtonFromPopupFlags(popup_flags)
if ImGui.IsMouseReleased(mouse_button, 4294967295 --[[ImGuiKeyOwner_NoOwner]]) and ImGui.IsWindowHovered(32 --[[ImGuiHoveredFlags.AllowWhenBlockedByPopup]]) then
if bit32.band(popup_flags, 256 --[[ImGuiPopupFlags.NoOpenOverItems]]) == 0 or not ImGui.IsAnyItemHovered() then
return true
end
end
if g.NavOpenContextMenuWindowId and g.NavOpenContextMenuWindowId ~= 0 and g.CurrentWindow.ID ~= 0 then
if ImGui.IsWindowChildOf(g.NavWindow, g.CurrentWindow, false, false) then return true end -- This enable ordering to be used to disambiguate item vs window (#8803)
end
return false
end
function ImGui.BeginPopupContextWindow(str_id, popup_flags)
if popup_flags == nil then popup_flags = 0 end
local g = GImGui
local window = g.CurrentWindow
if not str_id then str_id = "window_context" end
local id = window:GetID(str_id)
if ImGui.IsPopupOpenRequestForWindow(popup_flags) then ImGui.OpenPopupEx(id, popup_flags) end
return ImGui.BeginPopupEx(id, bit32.bor(64 --[[ImGuiWindowFlags.AlwaysAutoResize]], 1 --[[ImGuiWindowFlags.NoTitleBar]], 256 --[[ImGuiWindowFlags.NoSavedSettings]]))
end
function ImGui.BeginPopupContextVoid(str_id, popup_flags)
if popup_flags == nil then popup_flags = 0 end
local g = GImGui
local window = g.CurrentWindow
if not str_id then str_id = "void_context" end
local id = window:GetID(str_id) -- FIXME: Use a global ID?
local mouse_button = ImGui.GetMouseButtonFromPopupFlags(popup_flags)
if ImGui.IsMouseReleased(mouse_button, id) and not ImGui.IsWindowHovered(4 --[[ImGuiHoveredFlags.AnyWindow]]) then
if ImGui.GetTopMostPopupModal() == nil then ImGui.OpenPopupEx(id, popup_flags) end
end
return ImGui.BeginPopupEx(id, bit32.bor(64 --[[ImGuiWindowFlags.AlwaysAutoResize]], 1 --[[ImGuiWindowFlags.NoTitleBar]], 256 --[[ImGuiWindowFlags.NoSavedSettings]]))
end
function ImGui.BeginTooltipHidden()
local g = GImGui
local _, ret = ImGui.Begin("##Tooltip_Hidden", nil, bit32.bor(33554432 --[[ImGuiWindowFlags.Tooltip]], 197120 --[[ImGuiWindowFlags.NoInputs]], 1 --[[ImGuiWindowFlags.NoTitleBar]],
4 --[[ImGuiWindowFlags.NoMove]], 2 --[[ImGuiWindowFlags.NoResize]], 256 --[[ImGuiWindowFlags.NoSavedSettings]], 64 --[[ImGuiWindowFlags.AlwaysAutoResize]]))
ImGui.SetWindowHiddenAndSkipItemsForCurrentFrame(g.CurrentWindow)
return ret
end
-------------------------------------------------------------------------------
-- [SECTION] MISC GETTERS/SETTERS, FOCUS
-------------------------------------------------------------------------------
function ImGui.NavMoveRequestResolveWithLastItem(result)
local g = GImGui
g.NavMoveScoringItems = false -- Ensure request doesn't need more processing
ImGui.NavApplyItemToResult(result)
ImGui.NavUpdateAnyRequestFlag()
end
-- Note: this will likely be called ActivateItem() once we rework our Focus/Activation system!
-- But ActivateItem() should function without altering scroll/focus?
function ImGui.SetKeyboardFocusHere(offset)
if offset == nil then offset = 0 end
local g = GImGui
local window = g.CurrentWindow
assert(offset >= -1) -- -1 is allowed but not below
-- It makes sense in the vast majority of cases to never interrupt a drag and drop.
-- When we refactor this function into ActivateItem() we may want to make this an option.
-- MovingWindow is protected from most user inputs using SetActiveIdUsingNavAndKeys(), but
-- is also automatically dropped in the event g.ActiveId is stolen.
if g.DragDropActive or g.MovingWindow ~= nil then
return
end
ImGui.SetNavWindow(window)
local move_flags = bit32.bor(1024 --[[ImGuiNavMoveFlags.IsTabbing]], 4096 --[[ImGuiNavMoveFlags.Activate]], 512 --[[ImGuiNavMoveFlags.FocusApi]], 16384 --[[ImGuiNavMoveFlags.NoSetNavCursorVisible]])
local scroll_flags = window.Appearing and bit32.bor(1 --[[ImGuiScrollFlags.KeepVisibleEdgeX]], 32 --[[ImGuiScrollFlags.AlwaysCenterY]]) or bit32.bor(1 --[[ImGuiScrollFlags.KeepVisibleEdgeX]], 2 --[[ImGuiScrollFlags.KeepVisibleEdgeY]])
ImGui.NavMoveRequestSubmit((-1) --[[ImGuiDir.None]], (offset < 0) and 2 --[[ImGuiDir.Up]] or 3 --[[ImGuiDir.Down]], move_flags, scroll_flags) -- FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
if offset == -1 then
ImGui.NavMoveRequestResolveWithLastItem(g.NavMoveResultLocal)
else
g.NavTabbingDir = 1
g.NavTabbingCounter = offset + 1
end
end
-- Focus = move navigation cursor, set scrolling, set focus window.
function ImGui.FocusItem()
local g = GImGui
local window = g.CurrentWindow
if g.DragDropActive or g.MovingWindow ~= nil then -- FIXME: Opt-in flags for this?
return
end
local move_flags = bit32.bor(1024 --[[ImGuiNavMoveFlags.IsTabbing]], 512 --[[ImGuiNavMoveFlags.FocusApi]], 16384 --[[ImGuiNavMoveFlags.NoSetNavCursorVisible]], 8192 --[[ImGuiNavMoveFlags.NoSelect]])
local scroll_flags = window.Appearing and bit32.bor(1 --[[ImGuiScrollFlags.KeepVisibleEdgeX]], 32 --[[ImGuiScrollFlags.AlwaysCenterY]]) or bit32.bor(1 --[[ImGuiScrollFlags.KeepVisibleEdgeX]], 2 --[[ImGuiScrollFlags.KeepVisibleEdgeY]])
ImGui.SetNavWindow(window)
ImGui.NavMoveRequestSubmit((-1) --[[ImGuiDir.None]], 2 --[[ImGuiDir.Up]], move_flags, scroll_flags)
ImGui.NavMoveRequestResolveWithLastItem(g.NavMoveResultLocal)
end
function ImGui.ActivateItemByID(id)
local g = GImGui
g.NavNextActivateId = id
g.NavNextActivateFlags = 0 --[[ImGuiActivateFlags.None]]
end
function ImGui.SetNavCursorVisible(visible)
local g = GImGui
if g.NavWindow and bit32.band(g.NavWindow.Flags, 65536 --[[ImGuiWindowFlags.NoNavInputs]]) ~= 0 then
visible = false
elseif g.IO.ConfigNavCursorVisibleAlways then
visible = true
end
g.NavCursorVisible = visible
end
function ImGui.GetFrameCount() return GImGui.FrameCount end
function ImGui.GetStateStorage() return GImGui.CurrentWindow.DC.StateStorage end
function ImGui.SetStateStorage(tree)
local window = GImGui.CurrentWindow
window.DC.StateStorage = tree or window.StateStorage
end
function ImGui.SetNextFrameWantCaptureKeyboard(want) GImGui.WantCaptureKeyboardNextFrame = want and 1 or 0 end
function ImGui.SetNextFrameWantCaptureMouse(want) GImGui.WantCaptureMouseNextFrame = want and 1 or 0 end
function ImGui.GetVersion() return IMGUI_VERSION end
local StyleColorNames = nil
function ImGui.GetStyleColorName(idx)
if StyleColorNames == nil then
StyleColorNames = {}
for k, v in pairs(ImGuiCol) do if k ~= "COUNT" then StyleColorNames[v] = k end end
end
return StyleColorNames[idx] or "Unknown"
end
-------------------------------------------------------------------------------
-- [SECTION] NAV END FRAME
-------------------------------------------------------------------------------
local NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15
-- Window has already passed the IsWindowNavFocusable()
local function GetFallbackWindowNameForWindowingList(window)
if bit32.band(window.Flags, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0 then
return "(Popup)"
end
if bit32.band(window.Flags, 1024 --[[ImGuiWindowFlags.MenuBar]]) ~= 0 and window.Name == "##MainMenuBar" then
return "(Main menu bar)"
end
if window.DockNodeAsHost then
return "(Dock node)" -- Not normally shown to user.
end
return "(Untitled)"
end
-- Overlay displayed when using Ctrl+Tab. Called by EndFrame().
function ImGui.NavUpdateWindowingOverlay()
local g = GImGui
assert(g.NavWindowingTarget ~= nil)
if g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY then
return
end
local viewport = ImGui.GetMainViewport()
ImGui.SetNextWindowSizeConstraints(ImVec2(viewport.Size.x * 0.20, viewport.Size.y * 0.20), ImVec2(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]]))
ImGui.SetNextWindowPos(viewport:GetCenter(), 1 --[[ImGuiCond.Always]], ImVec2(0.5, 0.5))
ImGui.PushStyleVar(2 --[[ImGuiStyleVar.WindowPadding]], g.Style.WindowPadding * 2.0)
ImGui.Begin("##NavWindowingOverlay", nil, bit32.bor(1 --[[ImGuiWindowFlags.NoTitleBar]], 4096 --[[ImGuiWindowFlags.NoFocusOnAppearing]], 2 --[[ImGuiWindowFlags.NoResize]], 4 --[[ImGuiWindowFlags.NoMove]], 197120 --[[ImGuiWindowFlags.NoInputs]], 64 --[[ImGuiWindowFlags.AlwaysAutoResize]], 256 --[[ImGuiWindowFlags.NoSavedSettings]]))
g.NavWindowingListWindow = g.CurrentWindow
for n = g.WindowsFocusOrder.Size, 1, -1 do
local window = g.WindowsFocusOrder.Data[n]
assert(window ~= nil) -- Fix static analyzers
if ImGui.IsWindowNavFocusable(window) then
local label = window.Name
if ImGui.FindRenderedTextEnd(label) == 1 then
label = GetFallbackWindowNameForWindowingList(window)
end
ImGui.Selectable(label, g.NavWindowingTarget == window)
end
end
ImGui.End()
ImGui.PopStyleVar()
end
local function NavUpdateCreateWrappingRequest()
local g = GImGui
local window = g.NavWindow
local do_forward = false
local src = window.NavRectRel[g.NavLayer]
local bb_rel = ImRect(src.Min.x, src.Min.y, src.Max.x, src.Max.y)
local clip_dir = g.NavMoveDir
local move_flags = g.NavMoveFlags
-- local move_axis = (g.NavMoveDir == ImGuiDir.Up or g.NavMoveDir == ImGuiDir.Down) and ImGuiAxis.Y or ImGuiAxis.X
-- Menu layer does not maintain scrolling / content size (#9178)
local wrap_size = (g.NavLayer == 1 --[[ImGuiNavLayer.Menu]]) and window.Size or (window.ContentSize + window.WindowPadding)
if g.NavMoveDir == 0 --[[ImGuiDir.Left]] and bit32.band(move_flags, bit32.bor(4 --[[ImGuiNavMoveFlags.WrapX]], 1 --[[ImGuiNavMoveFlags.LoopX]])) ~= 0 then
bb_rel.Min.x = wrap_size.x; bb_rel.Max.x = wrap_size.x
if bit32.band(move_flags, 4 --[[ImGuiNavMoveFlags.WrapX]]) ~= 0 then
bb_rel:TranslateY(-bb_rel:GetHeight()) -- Previous row
clip_dir = 2 --[[ImGuiDir.Up]]
end
do_forward = true
end
if g.NavMoveDir == 1 --[[ImGuiDir.Right]] and bit32.band(move_flags, bit32.bor(4 --[[ImGuiNavMoveFlags.WrapX]], 1 --[[ImGuiNavMoveFlags.LoopX]])) ~= 0 then
bb_rel.Min.x = -window.WindowPadding.x; bb_rel.Max.x = -window.WindowPadding.x
if bit32.band(move_flags, 4 --[[ImGuiNavMoveFlags.WrapX]]) ~= 0 then
bb_rel:TranslateY(bb_rel:GetHeight()) -- Next row
clip_dir = 3 --[[ImGuiDir.Down]]
end
do_forward = true
end
if g.NavMoveDir == 2 --[[ImGuiDir.Up]] and bit32.band(move_flags, bit32.bor(8 --[[ImGuiNavMoveFlags.WrapY]], 2 --[[ImGuiNavMoveFlags.LoopY]])) ~= 0 then
bb_rel.Min.y = wrap_size.y; bb_rel.Max.y = wrap_size.y
if bit32.band(move_flags, 8 --[[ImGuiNavMoveFlags.WrapY]]) ~= 0 then
bb_rel:TranslateX(-bb_rel:GetWidth()) -- Previous column
clip_dir = 0 --[[ImGuiDir.Left]]
end
do_forward = true
end
if g.NavMoveDir == 3 --[[ImGuiDir.Down]] and bit32.band(move_flags, bit32.bor(8 --[[ImGuiNavMoveFlags.WrapY]], 2 --[[ImGuiNavMoveFlags.LoopY]])) ~= 0 then
bb_rel.Min.y = -window.WindowPadding.y; bb_rel.Max.y = -window.WindowPadding.y
if bit32.band(move_flags, 8 --[[ImGuiNavMoveFlags.WrapY]]) ~= 0 then
bb_rel:TranslateX(bb_rel:GetWidth()) -- Next column
clip_dir = 1 --[[ImGuiDir.Right]]
end
do_forward = true
end
if not do_forward then
return
end
window.NavRectRel[g.NavLayer] = bb_rel
ImGui.NavClearPreferredPosForAxis(1 --[[ImGuiAxis.X]])
ImGui.NavClearPreferredPosForAxis(2 --[[ImGuiAxis.Y]])
ImGui.NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags)
end
function ImGui.NavEndFrame()
local g = GImGui
-- Show Ctrl+Tab list window
if g.NavWindowingTarget ~= nil then
ImGui.NavUpdateWindowingOverlay()
end
-- Perform wrap-around in menus
-- FIXME-NAV: Wrap may need to apply a weight bias on the other axis. e.g. 4x4 grid with 2 last items missing on last item won't handle LoopY/WrapY correctly.
-- FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame.
if g.NavWindow and ImGui.NavMoveRequestButNoResultYet() and bit32.band(g.NavMoveFlags, 15 --[[ImGuiNavMoveFlags.WrapMask_]]) ~= 0 and bit32.band(g.NavMoveFlags, 128 --[[ImGuiNavMoveFlags.Forwarded]]) == 0 then
NavUpdateCreateWrappingRequest()
end
end
-------------------------------------------------------------------------------
-- [SECTION] ImGuiTextFilter
-------------------------------------------------------------------------------
--- @class ImGuiTextFilter
MT.ImGuiTextFilter = {}
MT.ImGuiTextFilter.__index = MT.ImGuiTextFilter
-- Helper: Parse and apply text filters e.g. 'aaa bbb -ccc'.
--- @param default_filter? string
function ImGuiTextFilter(default_filter)
local this = setmetatable({ InputBuf = {}, _CountExclude = 0, _Items = ImVector() }, MT.ImGuiTextFilter)
for i = 1, 256 do this.InputBuf[i] = 0 end
if default_filter and default_filter ~= "" then
for i = 1, ImMin(#default_filter, 255) do this.InputBuf[i] = string.byte(default_filter, i) end
this:Build()
end
return this
end
function MT.ImGuiTextFilter:Draw(label, width)
if label == nil then label = "Filter (inc,-exc)" end
if width ~= nil and width ~= 0.0 then ImGui.SetNextItemWidth(width) end
return self:DrawWithHint(label, "incl -excl")
end
-- Use ImGui.SetNextItemWidth() manually if you want to use this.
function MT.ImGuiTextFilter:DrawWithHint(label, hint)
if label == nil then label = "Filter" end
if hint == nil then hint = "incl -excl" end
local value_changed = ImGui.InputTextWithHint(label, hint, self.InputBuf, 256)
if value_changed then
self:Build()
end
return value_changed
end
function MT.ImGuiTextFilter:Clear() self.InputBuf[1] = 0; self:Build() end
function MT.ImGuiTextFilter:IsActive() return self._Items.Size ~= 0 end
-- Parse filter and split into a format optimal for PassFilter()
-- Items are { Text = string (lowercased), CountInclude = int }
function MT.ImGuiTextFilter:Build()
local items = self._Items
items:resize(0)
self._CountExclude = 0
local n = 0
while self.InputBuf[n + 1] ~= nil and self.InputBuf[n + 1] ~= 0 do n = n + 1 end
local buf = ImGui._ByteArrayToString(self.InputBuf, 1, n + 1)
local buf_e = n + 1 -- exclusive, 1-based
local word_b = 1
local seq_start_idx = -1 -- 1-based index into items, -1 = none
while word_b < buf_e do
-- Trim blanks
while word_b < buf_e and ImCharIsBlankA(string.byte(buf, word_b)) do
word_b = word_b + 1
end
local is_excl = word_b < buf_e and string.byte(buf, word_b) == 45 -- '-'
if is_excl then word_b = word_b + 1 end
local is_quote = word_b < buf_e and string.byte(buf, word_b) == 34 -- '"'
local word_e
if is_quote then
-- Parsing quotes. Omit storing leading/trailing quotes.
word_b = word_b + 1
word_e = string.find(buf, '"', word_b, true) or buf_e
if word_e > buf_e then word_e = buf_e end
else
-- Handle both ' ' and ',' separators.
word_e = word_b
while word_e < buf_e do
local c = string.byte(buf, word_e)
if c == 32 or c == 44 then break end
word_e = word_e + 1
end
end
-- Min length
if word_e - word_b > 0 then
-- Add to list. The '-' is not stored in items but implicitly inferred using (n < CountExclude).
local item = { Text = string.lower(string.sub(buf, word_b, word_e - 1)), CountInclude = 0 }
if is_excl then
items:insert(self._CountExclude + 1, item)
self._CountExclude = self._CountExclude + 1
if seq_start_idx ~= -1 then seq_start_idx = seq_start_idx + 1 end
else
if seq_start_idx == -1 then seq_start_idx = items.Size + 1 end
items:push_back(item)
items.Data[seq_start_idx].CountInclude = items.Data[seq_start_idx].CountInclude + 1
end
end
-- Next sequence
if word_e < buf_e and string.byte(buf, word_e) == 44 then -- ','
seq_start_idx = -1
end
word_b = word_e + 1
end
end
-- FIXME: Could use a specialized ImStristr() + pre-convert our filter to uppercase.
--- @param text string?
--- @param text_end? int # exclusive, 1-based
function MT.ImGuiTextFilter:PassFilter(text, text_end)
local items = self._Items
if items.Size == 0 then return true end
if text == nil then text = "" end
if text_end ~= nil then text = string.sub(text, 1, text_end - 1) end
text = string.lower(text)
-- Filters are sorted so that '-' ones are always leading.
local seq = 1
local seq_excl_end = self._CountExclude + 1
while seq < seq_excl_end do
if string.find(text, items.Data[seq].Text, 1, true) then return false end
seq = seq + 1
end
-- Process includes
local seq_end = items.Size + 1
if seq == seq_end then return true end -- When no inclusion are specified (only exclusions) we implicitly pass
while seq < seq_end do
local seq_incl_end = seq + items.Data[seq].CountInclude
while seq < seq_incl_end do
if not string.find(text, items.Data[seq].Text, 1, true) then break end
seq = seq + 1
end
if seq == seq_incl_end then return true end -- All matched
seq = seq_incl_end -- Try next
end
return false
end
-------------------------------------------------------------------------------
-- [SECTION] ImGuiListClipper
-------------------------------------------------------------------------------
-- FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell.
-- The problem we have is that without a Begin/End scheme for rows using the clipper is ambiguous.
local function GetSkipItemForListClipping()
local g = GImGui
if g.CurrentTable then return g.CurrentTable.HostSkipItems end
return g.CurrentWindow.SkipItems
end
-- `offset` is a 0-based count of leading ranges to leave alone
local function ImGuiListClipper_SortAndFuseRanges(ranges, offset)
if offset == nil then offset = 0 end
if ranges.Size - offset <= 1 then return end
local d = ranges.Data
-- Helper to order ranges and fuse them together if possible (bubble sort is fine as we are only sorting 2-3 entries)
for sort_end = ranges.Size - offset - 1, 1, -1 do
for i = offset + 1, sort_end + offset do
if d[i].Min > d[i + 1].Min then d[i], d[i + 1] = d[i + 1], d[i] end
end
end
-- Now fuse ranges together as much as possible.
local i = 2 + offset
while i <= ranges.Size do
assert(not d[i].PosToIndexConvert and not d[i - 1].PosToIndexConvert)
if d[i - 1].Max < d[i].Min then
i = i + 1
else
d[i - 1].Min = ImMin(d[i - 1].Min, d[i].Min)
d[i - 1].Max = ImMax(d[i - 1].Max, d[i].Max)
ranges:erase(i)
end
end
end
local function ImGuiListClipper_SeekCursorAndSetupPrevLine(clipper, pos_y, line_height)
-- Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
-- FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
-- The clipper should probably have a final step to display the last item in a regular manner, maybe with an opt-out flag for data sets which may have costly seek?
local g = GImGui
local window = g.CurrentWindow
local off_y = pos_y - window.DC.CursorPos.y
window.DC.CursorPos.y = pos_y
window.DC.CursorMaxPos.y = ImMax(window.DC.CursorMaxPos.y, pos_y - g.Style.ItemSpacing.y)
window.DC.CursorPosPrevLine.y = window.DC.CursorPos.y - line_height -- Setting those fields so that SetScrollHereY() can properly function after the end of our clipper usage.
window.DC.PrevLineSize.y = (line_height - g.Style.ItemSpacing.y) -- If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list.
local columns = window.DC.CurrentColumns
if columns then
columns.LineMinY = window.DC.CursorPos.y -- Setting this so that cell Y position are set properly
end
local tbl = g.CurrentTable
if tbl then
if tbl.IsInsideRow then
ImGui.TableEndRow(tbl)
end
local row_increase = math.floor((off_y / line_height) + 0.5)
if row_increase > 0 and bit32.band(clipper.Flags, 1 --[[ImGuiListClipperFlags.NoSetTableRowCounters]]) == 0 then -- If your clipper item height is ~= from actual table row height, consider using ImGuiListClipperFlags.NoSetTableRowCounters. See #8886.
tbl.CurrentRow = tbl.CurrentRow + row_increase
tbl.RowBgColorCounter = tbl.RowBgColorCounter + row_increase
end
tbl.RowPosY2 = window.DC.CursorPos.y
end
end
--- @class ImGuiListClipper
MT.ImGuiListClipper = {}
MT.ImGuiListClipper.__index = MT.ImGuiListClipper
function ImGuiListClipper()
return setmetatable({ DisplayStart = 0, DisplayEnd = 0, UserIndex = 0, ItemsCount = 0, ItemsHeight = 0.0, Flags = 0,
StartPosY = 0.0, StartSeekOffsetY = 0.0, Ctx = nil, TempData = nil }, MT.ImGuiListClipper)
end
--- @param items_count int
--- @param items_height? float
function MT.ImGuiListClipper:Begin(items_count, items_height)
if items_height == nil then items_height = -1.0 end
self.Ctx = ImGui.GetCurrentContext()
local g = self.Ctx
local window = g.CurrentWindow
local tbl = g.CurrentTable
if tbl and tbl.IsInsideRow then
ImGui.TableEndRow(tbl)
end
self.StartPosY = window.DC.CursorPos.y
self.ItemsHeight = items_height
self.ItemsCount = items_count
self.DisplayStart = -1
self.DisplayEnd = 0
-- Acquire temporary buffer
g.ClipperTempDataStacked = g.ClipperTempDataStacked + 1
while g.ClipperTempDataStacked > g.ClipperTempData.Size do
g.ClipperTempData:push_back(ImGuiListClipperData())
end
local data = g.ClipperTempData.Data[g.ClipperTempDataStacked]
data:Reset(self)
data.LossynessOffset = window.DC.CursorStartPosLossyness.y
self.TempData = data
self.StartSeekOffsetY = data.LossynessOffset
end
function MT.ImGuiListClipper:End()
local data = self.TempData
if data then
-- In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user.
local g = self.Ctx
if self.ItemsCount >= 0 and self.ItemsCount < 2147483647 --[[INT_MAX]] and self.DisplayStart >= 0 then
self:SeekCursorForItem(self.ItemsCount)
end
-- Restore temporary buffer and fix back pointers which may be invalidated when nesting
assert(data.ListClipper == self)
data.StepNo = data.Ranges.Size
g.ClipperTempDataStacked = g.ClipperTempDataStacked - 1
if g.ClipperTempDataStacked > 0 then
data = g.ClipperTempData.Data[g.ClipperTempDataStacked]
data.ListClipper.TempData = data
end
self.TempData = nil
end
self.DisplayStart = self.ItemsCount -- Clear this so code which may be reused past last Step() won't trip on a non-empty range.
self.DisplayEnd = self.ItemsCount
self.ItemsCount = -1
end
function MT.ImGuiListClipper:IncludeItemByIndex(item_index) self:IncludeItemsByIndex(item_index, item_index + 1) end
function MT.ImGuiListClipper:IncludeItemsByIndex(item_begin, item_end)
local data = self.TempData
assert(self.DisplayStart < 0) -- Only allowed after Begin() and if there has not been a specified range yet.
assert(item_begin <= item_end)
if item_begin < item_end then
data.Ranges:push_back(MT.ImGuiListClipperRange.FromIndices(item_begin, item_end))
end
end
-- This is already called while stepping.
-- The ONLY reason you may want to call this is if you passed INT_MAX to ImGuiListClipper:Begin() because you couldn't step item count beforehand.
function MT.ImGuiListClipper:SeekCursorForItem(item_n)
-- - StartPosY starts from ItemsFrozen, by adding SeekOffsetY we generally cancel that out (SeekOffsetY == LossynessOffset - ItemsFrozen * ItemsHeight).
-- - The reason we store SeekOffsetY instead of inferring it, is because we want to allow user to perform Seek after the last step, where ImGuiListClipperData is already done.
local pos_y = self.StartPosY + self.StartSeekOffsetY + item_n * self.ItemsHeight
ImGuiListClipper_SeekCursorAndSetupPrevLine(self, pos_y, self.ItemsHeight)
end
local function ImGuiListClipper_StepInternal(clipper)
local g = clipper.Ctx
local window = g.CurrentWindow
local data = clipper.TempData
assert(data ~= nil, "Called ImGuiListClipper::Step() too many times, or before ImGuiListClipper::Begin() ?")
local tbl = g.CurrentTable
if tbl and tbl.IsInsideRow then
ImGui.TableEndRow(tbl)
end
-- No items
if clipper.ItemsCount == 0 or GetSkipItemForListClipping() then
return false
end
-- While we are in frozen row state, keep displaying items one by one, unclipped
-- FIXME: Could be stored as a table-agnostic state.
if data.StepNo == 0 and tbl ~= nil and not tbl.IsUnfrozenRows then
clipper.DisplayStart = data.ItemsFrozen
clipper.DisplayEnd = ImMin(data.ItemsFrozen + 1, clipper.ItemsCount)
if clipper.DisplayStart < clipper.DisplayEnd then
data.ItemsFrozen = data.ItemsFrozen + 1
end
return true
end
-- Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height)
local calc_clipping = false
if data.StepNo == 0 then
clipper.StartPosY = window.DC.CursorPos.y
if clipper.ItemsHeight <= 0.0 then
-- Submit the first item (or range) so we can measure its height (generally the first range is 0..1)
data.Ranges:push_front(MT.ImGuiListClipperRange.FromIndices(data.ItemsFrozen, data.ItemsFrozen + 1))
clipper.DisplayStart = ImMax(data.Ranges.Data[1].Min, data.ItemsFrozen)
clipper.DisplayEnd = ImMin(data.Ranges.Data[1].Max, clipper.ItemsCount)
data.StepNo = 1
return true
end
calc_clipping = true -- If on the first step with known item height, calculate clipping.
end
-- Step 1: Let the clipper infer height from first range
if clipper.ItemsHeight <= 0.0 then
assert(data.StepNo == 1)
-- (no float precision mitigation needed: Luau numbers are doubles)
clipper.ItemsHeight = (window.DC.CursorPos.y - clipper.StartPosY) / (clipper.DisplayEnd - clipper.DisplayStart)
if clipper.ItemsHeight == 0.0 and clipper.ItemsCount == 2147483647 --[[INT_MAX]] then -- Accept that no item have been submitted if in indeterminate mode.
return false
end
if clipper.ItemsHeight <= 0.0 then
IM_ASSERT_USER_ERROR(clipper.ItemsHeight > 0.0, "ImGuiListClipper: Failed to calculate item height! First item hasn't been submitted by user code, or has not moved the cursor vertically!")
return false
end
calc_clipping = true -- If item height had to be calculated, calculate clipping afterwards.
end
-- Step 0 or 1: Calculate the actual ranges of visible elements.
local already_submitted = clipper.DisplayEnd
if calc_clipping then
-- Record seek offset, this is so ImGuiListClipper:Seek() can be called after ImGuiListClipperData is done
clipper.StartSeekOffsetY = data.LossynessOffset - data.ItemsFrozen * clipper.ItemsHeight
if g.LogEnabled then
-- If logging is active, do not perform any clipping
data.Ranges:push_back(MT.ImGuiListClipperRange.FromIndices(0, clipper.ItemsCount))
else
-- Add range selected to be included for navigation
local is_nav_request = (g.NavMoveScoringItems and g.NavWindow and g.NavWindow.RootWindowForNav == window.RootWindowForNav) and true or false
local nav_off_min = (is_nav_request and g.NavMoveClipDir == 2 --[[ImGuiDir.Up]]) and -1 or 0
local nav_off_max = (is_nav_request and g.NavMoveClipDir == 3 --[[ImGuiDir.Down]]) and 1 or 0
if is_nav_request then
data.Ranges:push_back(MT.ImGuiListClipperRange.FromPositions(g.NavScoringRect.Min.y, g.NavScoringRect.Max.y, nav_off_min, nav_off_max))
if not g.NavScoringNoClipRect:IsInverted() then
data.Ranges:push_back(MT.ImGuiListClipperRange.FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, nav_off_min, nav_off_max))
end
end
if is_nav_request and bit32.band(g.NavMoveFlags, 1024 --[[ImGuiNavMoveFlags.IsTabbing]]) ~= 0 and g.NavTabbingDir == -1 then
data.Ranges:push_back(MT.ImGuiListClipperRange.FromIndices(clipper.ItemsCount - 1, clipper.ItemsCount))
end
-- Add focused/active item
local nav_rect_abs = ImGui.WindowRectRelToAbs(window, window.NavRectRel[0])
if g.NavId ~= 0 and window.NavLastIds[0] == g.NavId then
data.Ranges:push_back(MT.ImGuiListClipperRange.FromPositions(nav_rect_abs.Min.y, nav_rect_abs.Max.y, 0, 0))
end
local min_y = window.ClipRect.Min.y
local max_y = window.ClipRect.Max.y
-- Add box selection range
local bs = g.BoxSelectState
if bs.IsActive and bs.Window == window then
-- FIXME: Selectable() use of half-ItemSpacing isn't consistent in matter of layout, as ItemAdd(bb) stray above ItemSize()'s CursorPos.
-- RangeSelect's BoxSelect relies on comparing overlap of previous and current rectangle and is sensitive to that.
-- As a workaround we currently half ItemSpacing worth on each side.
local pad_y = g.Style.ItemSpacing.y
min_y = min_y - pad_y
max_y = max_y + pad_y
-- Box-select on 2D area requires different clipping.
-- (best adding pad_y here than in BeginBoxSelect() as we are closer to current state)
if bs.UnclipMode then
data.Ranges:push_back(MT.ImGuiListClipperRange.FromPositions(bs.UnclipRect.Min.y - pad_y, bs.UnclipRect.Max.y + pad_y, 0, 0))
end
end
-- Add main visible range
data.Ranges:push_back(MT.ImGuiListClipperRange.FromPositions(min_y, max_y, nav_off_min, nav_off_max))
end
-- Convert position ranges to item index ranges
-- - Very important: when a starting position is after our maximum item, we set Min to (ItemsCount - 1). This allows us to handle most forms of wrapping.
-- - Due to how Selectable extra padding they tend to be "unaligned" with exact unit in the item list,
-- which with the flooring/ceiling tend to lead to 2 items instead of one being submitted.
for _, range in data.Ranges:iter() do
if range.PosToIndexConvert then
local m1 = ImTrunc((range.Min - window.DC.CursorPos.y - data.LossynessOffset) / clipper.ItemsHeight)
local m2 = ImTrunc(((range.Max - window.DC.CursorPos.y - data.LossynessOffset) / clipper.ItemsHeight) + 0.999999)
range.Min = ImClamp(already_submitted + m1 + range.PosToIndexOffsetMin, already_submitted, clipper.ItemsCount - 1)
range.Max = ImClamp(already_submitted + m2 + range.PosToIndexOffsetMax, range.Min + 1, clipper.ItemsCount)
range.PosToIndexConvert = false
end
end
ImGuiListClipper_SortAndFuseRanges(data.Ranges, data.StepNo)
end
-- Step 0+ (if item height is given in advance) or 1+: Display the next range in line.
while data.StepNo < data.Ranges.Size do
local r = data.Ranges.Data[data.StepNo + 1]
clipper.DisplayStart = ImMax(r.Min, already_submitted)
clipper.DisplayEnd = ImMin(r.Max, clipper.ItemsCount)
data.StepNo = data.StepNo + 1
if clipper.DisplayStart < clipper.DisplayEnd then
if clipper.DisplayStart > already_submitted then
clipper:SeekCursorForItem(clipper.DisplayStart)
end
return true
end
end
-- After the last step: Let the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd),
-- Advance the cursor to the end of the list and then returns 'false' to end the loop.
if clipper.ItemsCount < 2147483647 --[[INT_MAX]] then
clipper:SeekCursorForItem(clipper.ItemsCount)
end
return false
end
function MT.ImGuiListClipper:Step()
local ret = ImGuiListClipper_StepInternal(self)
if ret and (self.DisplayStart >= self.DisplayEnd) then
ret = false
end
if not ret then
self:End()
end
return ret
end
-------------------------------------------------------------------------------
-- [SECTION] ERROR CHECKING, STATE RECOVERY
-------------------------------------------------------------------------------
-- - ErrorRecoveryStoreState()
-- - ErrorRecoveryTryToRecoverState()
-- - ErrorRecoveryTryToRecoverWindowState()
-- - ErrorLog()
-------------------------------------------------------------------------------
function ImGuiErrorRecoveryState()
return { SizeOfWindowStack = 0, SizeOfIDStack = 0, SizeOfTreeStack = 0, SizeOfColorStack = 0, SizeOfStyleVarStack = 0,
SizeOfFontStack = 0, SizeOfFocusScopeStack = 0, SizeOfGroupStack = 0, SizeOfItemFlagsStack = 0,
SizeOfBeginPopupStack = 0, SizeOfDisabledStack = 0 }
end
function ImGui.ErrorCheckNewFrameSanityChecks()
local g = GImGui
-- [Luau] C++-only check of the user's IM_ASSERT macro definition dropped.
-- Check user data
-- (We pass an error message in the assert expression to make it visible to programmers who are not using a debugger, as most assert handlers display their argument)
assert(g.Initialized)
assert(g.IO.DeltaTime > 0.0 or g.FrameCount == 0, "Need a positive DeltaTime!")
assert(g.FrameCount == 0 or g.FrameCountEnded == g.FrameCount, "Forgot to call Render() or EndFrame() at the end of the previous frame?")
assert(g.IO.DisplaySize.x >= 0.0 and g.IO.DisplaySize.y >= 0.0, "Invalid DisplaySize value!")
assert(g.Style.CurveTessellationMaxError > 0.0, "Invalid style setting!")
assert(g.Style.CircleTessellationMaxError > 0.0, "Invalid style setting!")
assert(g.Style.Alpha >= 0.0 and g.Style.Alpha <= 1.0, "Invalid style setting!") -- Allows us to avoid a few clamps in color computations
assert(g.Style.WindowMinSize.x >= 1.0 and g.Style.WindowMinSize.y >= 1.0, "Invalid style setting!")
assert(g.Style.WindowBorderHoverPadding > 0.0, "Invalid style setting!") -- Required otherwise cannot resize from borders.
assert(g.Style.WindowMenuButtonPosition == (-1) --[[ImGuiDir.None]] or g.Style.WindowMenuButtonPosition == 0 --[[ImGuiDir.Left]] or g.Style.WindowMenuButtonPosition == 1 --[[ImGuiDir.Right]])
assert(g.Style.ColorButtonPosition == 0 --[[ImGuiDir.Left]] or g.Style.ColorButtonPosition == 1 --[[ImGuiDir.Right]])
assert(ImIsPowerOfTwo(bit32.band(g.IO.ConfigColorEditFlags, 7340032 --[[ImGuiColorEditFlags.DisplayMask_]]))) -- Check only 1 option is selected
assert(ImIsPowerOfTwo(bit32.band(g.IO.ConfigColorEditFlags, 25165824 --[[ImGuiColorEditFlags.DataTypeMask_]]))) -- "
assert(ImIsPowerOfTwo(bit32.band(g.IO.ConfigColorEditFlags, 100663296 --[[ImGuiColorEditFlags.PickerMask_]]))) -- "
assert(ImIsPowerOfTwo(bit32.band(g.IO.ConfigColorEditFlags, 805306368 --[[ImGuiColorEditFlags.InputMask_]]))) -- "
assert(g.Style.TreeLinesFlags == 262144 --[[ImGuiTreeNodeFlags.DrawLinesNone]] or g.Style.TreeLinesFlags == 524288 --[[ImGuiTreeNodeFlags.DrawLinesFull]] or g.Style.TreeLinesFlags == 1048576 --[[ImGuiTreeNodeFlags.DrawLinesToNodes]])
assert(g.IO.MouseSingleClickDelay > g.IO.MouseDoubleClickTime)
-- Error handling: we do not accept 100% silent recovery! Please contact me if you feel this is getting in your way.
if g.IO.ConfigErrorRecovery then
assert(g.IO.ConfigErrorRecoveryEnableAssert or g.IO.ConfigErrorRecoveryEnableDebugLog or g.IO.ConfigErrorRecoveryEnableTooltip or g.ErrorCallback ~= nil)
end
-- [Luau] Obsolete io.FontGlobalScale, ImGuiConfigFlags_NavEnableSetMousePos/NavNoCaptureKeyboard/DpiEnableScaleXXX and
-- io.Get/SetClipboardTextFn don't exist in this port: their remaps are dropped.
-- Remap legacy CurveTessellationTol into CurveTessellationMaxError (OBSOLETED in 1.93.0, August 2026)
if g.Style.CurveTessellationTol ~= 0.0 then
g.Style.CurveTessellationMaxError = ImSqrt(g.Style.CurveTessellationTol)
end
g.Style.CurveTessellationTol = 0.0
-- Perform simple check: error if Docking or Viewport are enabled _exactly_ on frame 1 (instead of frame 0 or later), which is a common error leading to loss of .ini data.
if g.FrameCount == 1 and bit32.band(g.IO.ConfigFlags, 128 --[[ImGuiConfigFlags.DockingEnable]]) ~= 0 and bit32.band(g.ConfigFlagsLastFrame, 128 --[[ImGuiConfigFlags.DockingEnable]]) == 0 then
assert(false, "Please set DockingEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!")
end
if g.FrameCount == 1 and bit32.band(g.IO.ConfigFlags, 1024 --[[ImGuiConfigFlags.ViewportsEnable]]) ~= 0 and bit32.band(g.ConfigFlagsLastFrame, 1024 --[[ImGuiConfigFlags.ViewportsEnable]]) == 0 then
assert(false, "Please set ViewportsEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!")
end
-- Perform simple checks: multi-viewport and platform windows support
if bit32.band(g.IO.ConfigFlags, 1024 --[[ImGuiConfigFlags.ViewportsEnable]]) ~= 0 then
if bit32.band(g.IO.BackendFlags, 2048 --[[ImGuiBackendFlags.PlatformHasViewports]]) ~= 0 and bit32.band(g.IO.BackendFlags, 1024 --[[ImGuiBackendFlags.RendererHasViewports]]) ~= 0 then
local pio = g.PlatformIO
assert(g.FrameCount == 0 or g.FrameCount == g.FrameCountPlatformEnded, "Forgot to call UpdatePlatformWindows() in main loop after EndFrame()? Check examples/ applications for reference.")
assert(pio.Platform_CreateWindow ~= nil, "Platform init didn't install handlers?")
assert(pio.Platform_DestroyWindow ~= nil, "Platform init didn't install handlers?")
assert(pio.Platform_GetWindowPos ~= nil, "Platform init didn't install handlers?")
assert(pio.Platform_SetWindowPos ~= nil, "Platform init didn't install handlers?")
assert(pio.Platform_GetWindowSize ~= nil, "Platform init didn't install handlers?")
assert(pio.Platform_SetWindowSize ~= nil, "Platform init didn't install handlers?")
assert(pio.Monitors.Size > 0, "Platform init didn't setup Monitors list?")
assert(g.Viewports.Data[1].PlatformUserData ~= nil or g.Viewports.Data[1].PlatformHandle ~= nil, "Platform init didn't setup main viewport.")
if g.IO.ConfigDockingTransparentPayload and bit32.band(g.IO.ConfigFlags, 128 --[[ImGuiConfigFlags.DockingEnable]]) ~= 0 then
assert(pio.Platform_SetWindowAlpha ~= nil, "Platform_SetWindowAlpha handler is required to use io.ConfigDockingTransparent!")
end
else
-- Disable feature, our backends do not support it
g.IO.ConfigFlags = bit32.band(g.IO.ConfigFlags, bit32.bnot(1024 --[[ImGuiConfigFlags.ViewportsEnable]]))
end
-- Perform simple checks on platform monitor data + compute a total bounding box for quick early outs
for _, mon in g.PlatformIO.Monitors:iter() do
assert(mon.MainSize.x > 0.0 and mon.MainSize.y > 0.0, "Monitor main bounds not setup properly.")
assert(ImRect(mon.MainPos, mon.MainPos + mon.MainSize):Contains(ImRect(mon.WorkPos, mon.WorkPos + mon.WorkSize)), "Monitor work bounds not setup properly. If you don't have work area information, just copy MainPos/MainSize into them.")
assert(mon.DpiScale > 0.0 and mon.DpiScale < 99.0, "Monitor DpiScale is invalid.") -- Typical correct values would be between 1.0f and 4.0f
end
end
end
function ImGui.ErrorCheckEndFrameSanityChecks()
-- Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()
-- One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame().
-- It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will
-- send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.
-- We silently accommodate for this case by ignoring the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
-- while still correctly asserting on mid-frame key press events.
local g = GImGui
local key_mods = ImGui.GetMergedModsFromKeys()
assert(key_mods == 0 or g.IO.KeyMods == key_mods, "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods")
assert(g.CurrentWindowStack.Size == 1)
assert(g.CurrentWindowStack.Data[1].Window.IsFallbackWindow)
end
-- Save current stack sizes. Called e.g. by NewFrame() and by Begin() but may be called for manual recovery.
function ImGui.ErrorRecoveryStoreState(state_out)
local g = GImGui
state_out.SizeOfWindowStack = g.CurrentWindowStack.Size
state_out.SizeOfIDStack = g.CurrentWindow.IDStack.Size
state_out.SizeOfTreeStack = g.CurrentWindow.DC.TreeDepth -- NOT g.TreeNodeStack.Size which is a partial stack!
state_out.SizeOfColorStack = g.ColorStack.Size
state_out.SizeOfStyleVarStack = g.StyleVarStack.Size
state_out.SizeOfFontStack = g.FontStack.Size
state_out.SizeOfFocusScopeStack = g.FocusScopeStack.Size
state_out.SizeOfGroupStack = g.GroupStack.Size
state_out.SizeOfItemFlagsStack = g.ItemFlagsStack.Size
state_out.SizeOfBeginPopupStack = g.BeginPopupStack.Size
state_out.SizeOfDisabledStack = g.DisabledStackSize
end
-- Chosen name "Try to recover" over e.g. "Restore" to suggest this is not a 100% guaranteed recovery.
-- Called by e.g. EndFrame() but may be called for manual recovery.
-- Attempt to recover full window stack.
function ImGui.ErrorRecoveryTryToRecoverState(state_in)
local g = GImGui
while g.CurrentWindowStack.Size > state_in.SizeOfWindowStack do
-- Recap:
-- - Begin()/BeginChild() return false to indicate the window is collapsed or fully clipped.
-- - Always call a matching End() for each Begin() call, regardless of its return value!
-- - Begin/End and BeginChild/EndChild logic is KNOWN TO BE INCONSISTENT WITH ALL OTHER BEGIN/END FUNCTIONS.
-- - We will fix that in a future major update.
local window = g.CurrentWindow
if bit32.band(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0 then
if g.CurrentTable ~= nil and g.CurrentTable.InnerWindow == g.CurrentWindow then
IM_ASSERT_USER_ERROR(false, "Missing EndTable()")
ImGui.EndTable()
else
IM_ASSERT_USER_ERROR(false, "Missing EndChild()")
ImGui.EndChild()
end
else
IM_ASSERT_USER_ERROR(false, "Missing End()")
ImGui.End()
end
end
if g.CurrentWindowStack.Size == state_in.SizeOfWindowStack then
ImGui.ErrorRecoveryTryToRecoverWindowState(state_in)
end
end
-- Called by e.g. End() but may be called for manual recovery.
-- Attempt to recover from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
function ImGui.ErrorRecoveryTryToRecoverWindowState(state_in)
local g = GImGui
while g.CurrentTable ~= nil and g.CurrentTable.InnerWindow == g.CurrentWindow do
IM_ASSERT_USER_ERROR(false, "Missing EndTable()")
ImGui.EndTable()
end
local window = g.CurrentWindow
-- FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
while g.CurrentTabBar ~= nil and g.CurrentTabBar.Window == window do
IM_ASSERT_USER_ERROR(false, "Missing EndTabBar()")
ImGui.EndTabBar()
end
while g.CurrentMultiSelect ~= nil and g.CurrentMultiSelect.Storage.Window == window do --V1044
IM_ASSERT_USER_ERROR(false, "Missing EndMultiSelect()")
ImGui.EndMultiSelect()
end
if window.DC.MenuBarAppending then
IM_ASSERT_USER_ERROR(false, "Missing EndMenuBar()")
ImGui.EndMenuBar()
end
while window.DC.TreeDepth > state_in.SizeOfTreeStack do
IM_ASSERT_USER_ERROR(false, "Missing TreePop()")
ImGui.TreePop()
end
while g.GroupStack.Size > state_in.SizeOfGroupStack do
IM_ASSERT_USER_ERROR(false, "Missing EndGroup()")
ImGui.EndGroup()
end
assert(g.GroupStack.Size == state_in.SizeOfGroupStack)
while window.IDStack.Size > state_in.SizeOfIDStack do
IM_ASSERT_USER_ERROR(false, "Missing PopID()")
ImGui.PopID()
end
while g.DisabledStackSize > state_in.SizeOfDisabledStack do
IM_ASSERT_USER_ERROR(false, "Missing EndDisabled()")
if bit32.band(g.CurrentItemFlags, 64 --[[ImGuiItemFlags.Disabled]]) ~= 0 then
ImGui.EndDisabled()
else
ImGui.EndDisabledOverrideReenable()
g.CurrentWindowStack:back().DisabledOverrideReenable = false
end
end
assert(g.DisabledStackSize == state_in.SizeOfDisabledStack)
while g.ColorStack.Size > state_in.SizeOfColorStack do
IM_ASSERT_USER_ERROR(false, "Missing PopStyleColor()")
ImGui.PopStyleColor()
end
while g.ItemFlagsStack.Size > state_in.SizeOfItemFlagsStack do
IM_ASSERT_USER_ERROR(false, "Missing PopItemFlag()")
ImGui.PopItemFlag()
end
while g.StyleVarStack.Size > state_in.SizeOfStyleVarStack do
IM_ASSERT_USER_ERROR(false, "Missing PopStyleVar()")
ImGui.PopStyleVar()
end
while g.FontStack.Size > state_in.SizeOfFontStack do
IM_ASSERT_USER_ERROR(false, "Missing PopFont()")
ImGui.PopFont()
end
while g.FocusScopeStack.Size > state_in.SizeOfFocusScopeStack do
IM_ASSERT_USER_ERROR(false, "Missing PopFocusScope()")
ImGui.PopFocusScope()
end
-- IM_ASSERT(g.FocusScopeStack.Size == state_in.SizeOfFocusScopeStack)
end
-- Lines are stored in g.DebugLogBuf (one chunk per call)
function ImGui.DebugLog(fmt, ...)
ImGui.DebugLogV(fmt, ...)
end
-- [Luau] va_list -> varargs (same convention as TextV()). Without arguments `fmt` is used verbatim.
function ImGui.DebugLogV(fmt, ...)
local g = GImGui
local prefix
if g.ContextName ~= nil and g.ContextName ~= "" then
prefix = string.format("[%s] [%05d] ", g.ContextName, g.FrameCount)
else
prefix = string.format("[%05d] ", g.FrameCount)
end
local str = prefix .. ((select("#", ...) > 0) and string.format(fmt, ...) or fmt)
g.DebugLogBuf:append(str)
-- Line index (upstream: g.DebugLogIndex). Lines are complete strings without '\n'.
local lines = g.DebugLogLines
for part, nl in string.gmatch(str, "([^\n]*)(\n?)") do
lines[#lines] = lines[#lines] .. part
if nl ~= "" then lines[#lines + 1] = "" end
if part == "" and nl == "" then break end
end
if bit32.band(g.DebugLogFlags, 1048576 --[[ImGuiDebugLogFlags.OutputToTTY]]) ~= 0 then
print((string.gsub(str, "\n$", "")))
end
end
--- @return bool should_assert
function ImGui.ErrorLog(msg)
local g = GImGui
if g == nil then return true end
-- Output to debug log
local window = g.CurrentWindow
if g.IO.ConfigErrorRecoveryEnableDebugLog then
if g.ErrorFirst then
ImGui.DebugLog(string.format("[imgui-error] (current settings: Assert=%d, Log=%d, Tooltip=%d)\n",
g.IO.ConfigErrorRecoveryEnableAssert and 1 or 0, g.IO.ConfigErrorRecoveryEnableDebugLog and 1 or 0, g.IO.ConfigErrorRecoveryEnableTooltip and 1 or 0))
end
ImGui.DebugLog(string.format("[imgui-error] In window '%s': %s\n", window and window.Name or "NULL", tostring(msg)))
end
g.ErrorFirst = false
-- Output to tooltip
if g.IO.ConfigErrorRecoveryEnableTooltip then
if g.WithinFrameScope and ImGui.BeginErrorTooltip() then
if g.ErrorCountCurrentFrame < 20 then
ImGui.Text("In window '%s': %s", window and window.Name or "NULL", tostring(msg))
if window and (not window.IsFallbackWindow or window.WasActive) then
ImGui.GetForegroundDrawList(window.Viewport):AddRect(window.Pos, window.Pos + window.Size, IM_COL32(255, 0, 0, 255))
end
end
if g.ErrorCountCurrentFrame == 20 then
ImGui.Text("(and more errors)")
end
-- EndFrame() will amend debug buttons to this window, after all errors have been submitted.
ImGui.EndErrorTooltip()
end
g.ErrorCountCurrentFrame = g.ErrorCountCurrentFrame + 1
end
-- Output to callback
if g.ErrorCallback ~= nil then
g.ErrorCallback(g, g.ErrorCallbackUserData, msg)
end
-- Return whether we should assert
return g.IO.ConfigErrorRecoveryEnableAssert
end
-- Display an error tooltip when same ID as HoveredId was submitted multiple times.
-- See code in ItemHoverable() for an explanation of why we associate this error to HoveredId + code drawing of rectangles over individual items instances.
function ImGui.ErrorCheckEndFrameFinalizeErrorTooltip()
local g = GImGui
if g.DebugDrawIdConflictsId ~= 0 and g.IO.KeyCtrl == false then
g.DebugDrawIdConflictsCount = g.HoveredIdPreviousFrameItemCount
end
if g.DebugDrawIdConflictsId ~= 0 and not g.DebugItemPickerActive and ImGui.BeginErrorTooltip() then
ImGui.Text("Programmer error: %d visible items with conflicting ID!", g.DebugDrawIdConflictsCount)
ImGui.BulletText("Code should use PushID()/PopID() in loops, or append \"##xx\" to same-label identifiers!")
ImGui.BulletText("Empty label e.g. Button(\"\") == same ID as parent widget/node. Use Button(\"##xx\") instead!")
-- ImGui.BulletText("Code intending to use duplicate ID may use e.g. PushItemFlag(ImGuiItemFlags.AllowDuplicateId, true); ... PopItemFlag()") -- Not making this too visible for fear of it being abused.
ImGui.BulletText("Set io.ConfigDebugHighlightIdConflicts=false to disable this warning in non-programmers builds.")
ImGui.Separator()
if g.IO.ConfigDebugHighlightIdConflictsShowItemPicker then
ImGui.Text("(Hold Ctrl to: use ")
ImGui.SameLine(0.0, 0.0)
if ImGui.SmallButton("Item Picker") then
ImGui.DebugStartItemPicker()
end
ImGui.SameLine(0.0, 0.0)
ImGui.Text(" to break in item call-stack, or ")
else
ImGui.Text("(Hold Ctrl to: ")
end
ImGui.SameLine(0.0, 0.0)
ImGui.TextLinkOpenURL("read FAQ \"About ID Stack System\"", "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#qa-usage")
ImGui.SameLine(0.0, 0.0)
ImGui.Text(")")
ImGui.EndErrorTooltip()
end
if g.ErrorCountCurrentFrame > 0 and ImGui.BeginErrorTooltip() then -- Amend at end of frame
ImGui.Separator()
ImGui.Text("(Hold Ctrl to: ")
ImGui.SameLine(0.0, 0.0)
if ImGui.SmallButton("Enable Asserts") then
g.IO.ConfigErrorRecoveryEnableAssert = true
end
-- ImGui.SameLine()
-- if ImGui.SmallButton("Hide Error Tooltips") then
-- g.IO.ConfigErrorRecoveryEnableTooltip = false -- Too dangerous
-- end
ImGui.SameLine(0, 0)
ImGui.Text(")")
ImGui.EndErrorTooltip()
end
end
-- Pseudo-tooltip. Follow mouse until Ctrl is held. When Ctrl is held we lock position, allowing to click it.
function ImGui.BeginErrorTooltip()
local g = GImGui
local window = ImGui.FindWindowByName("##Tooltip_Error")
local use_locked_pos = g.IO.KeyCtrl and window ~= nil and window.WasActive
local c = g.Style.Colors[4 --[[ImGuiCol.PopupBg]]]
ImGui.PushStyleColor(4 --[[ImGuiCol.PopupBg]], ImVec4(ImLerp(c.x, 1.0, 0.15), ImLerp(c.y, 0.0, 0.15), ImLerp(c.z, 0.0, 0.15), ImLerp(c.w, 1.0, 0.15)))
if use_locked_pos then
ImGui.SetNextWindowPos(g.ErrorTooltipLockedPos)
end
local _, is_visible = ImGui.Begin("##Tooltip_Error", nil, bit32.bor(33554432 --[[ImGuiWindowFlags.Tooltip]], 43 --[[ImGuiWindowFlags.NoDecoration]], 4 --[[ImGuiWindowFlags.NoMove]], 2 --[[ImGuiWindowFlags.NoResize]], 256 --[[ImGuiWindowFlags.NoSavedSettings]], 64 --[[ImGuiWindowFlags.AlwaysAutoResize]]))
ImGui.PopStyleColor()
if is_visible and g.CurrentWindow.BeginCount == 1 then
ImGui.SeparatorText("MESSAGE FROM DEAR IMGUI")
ImGui.BringWindowToDisplayFront(g.CurrentWindow)
ImGui.BringWindowToFocusFront(g.CurrentWindow)
g.ErrorTooltipLockedPos = ImGui.GetWindowPos()
elseif not is_visible then
ImGui.End()
end
return is_visible
end
function ImGui.EndErrorTooltip()
ImGui.End()
end
do --[[ [SECTION] METRICS/DEBUGGER WINDOW, DEBUG LOG, ID STACK TOOL, FONT DEBUG ]]
-------------------------------------------------------------------------------
-- [SECTION] Font atlas / font debugging
-------------------------------------------------------------------------------
-- byte buffers for InputText (same as the demo's DemoBuf/DemoBufStr; kept here so the library doesn't need the demo)
local function DemoBuf(str, size)
local t = {}
for i = 1, #str do t[i] = string.byte(str, i) end
t[#str + 1] = 0
if size then for i = #str + 2, size do t[i] = 0 end end
return t
end
local function DemoBufStr(buf)
local n = 0
while buf[n + 1] ~= nil and buf[n + 1] ~= 0 do n = n + 1 end
if n == 0 then return "" end
return ImGui._ByteArrayToString(buf, 1, n + 1)
end
-- Avoid naming collision with imgui_demo's HelpMarker() for unity builds.
local function MetricsHelpMarker(desc)
ImGui.TextDisabled("(?)")
if ImGui.BeginItemTooltip() then
ImGui.PushTextWrapPos(ImGui.GetFontSize() * 35.0)
ImGui.TextUnformatted(desc)
ImGui.PopTextWrapPos()
ImGui.EndTooltip()
end
end
ImGui.MetricsHelpMarker = MetricsHelpMarker
local function U8(c) return (c and c > 0) and utf8.char(c) or "" end
-- [Luau] table.sort() "less than" predicate (upstream: ImQsort() comparer returning b - a)
--- @param a ImGuiViewportP
--- @param b ImGuiViewportP
local function ViewportComparerByLastFocusedStampCount(a, b)
return b.LastFocusedStampCount < a.LastFocusedStampCount
end
-- Draw an arbitrary US keyboard layout to visualize translated keys
--- @param draw_list ImDrawList
function ImGui.DebugRenderKeyboardPreview(draw_list)
local scale = ImGui.GetFontSize() / 13.0
local key_size = ImVec2(35.0, 35.0) * scale
local key_rounding = 3.0 * scale
local key_face_size = ImVec2(25.0, 25.0) * scale
local key_face_pos = ImVec2(5.0, 3.0) * scale
local key_face_rounding = 2.0 * scale
local key_label_pos = ImVec2(7.0, 4.0) * scale
local key_step = ImVec2(key_size.x - 1.0, key_size.y - 1.0)
local key_row_offset = 9.0 * scale
local board_min = ImGui.GetCursorScreenPos()
local board_max = ImVec2(board_min.x + 3 * key_step.x + 2 * key_row_offset + 10.0, board_min.y + 3 * key_step.y + 10.0)
local start_pos = ImVec2(board_min.x + 5.0 - key_step.x, board_min.y)
local K = ImGuiKey
local keys_to_display = { -- { Row, Col, Label, Key }
{ 0, 0, "", K.Tab }, { 0, 1, "Q", K.Q }, { 0, 2, "W", K.W }, { 0, 3, "E", K.E }, { 0, 4, "R", K.R },
{ 1, 0, "", K.CapsLock }, { 1, 1, "A", K.A }, { 1, 2, "S", K.S }, { 1, 3, "D", K.D }, { 1, 4, "F", K.F },
{ 2, 0, "", K.LeftShift }, { 2, 1, "Z", K.Z }, { 2, 2, "X", K.X }, { 2, 3, "C", K.C }, { 2, 4, "V", K.V },
}
-- Elements rendered manually via ImDrawList API are not clipped automatically.
-- While not strictly necessary, here IsItemVisible() is used to avoid rendering these shapes when they are out of view.
ImGui.Dummy(board_max - board_min)
if not ImGui.IsItemVisible() then
return
end
draw_list:PushClipRect(board_min, board_max, true)
for n = 1, #keys_to_display do
local key_data = keys_to_display[n]
local key_min = ImVec2(start_pos.x + key_data[2] * key_step.x + key_data[1] * key_row_offset, start_pos.y + key_data[1] * key_step.y)
local key_max = key_min + key_size
draw_list:AddRectFilled(key_min, key_max, IM_COL32(204, 204, 204, 255), key_rounding)
draw_list:AddRect(key_min, key_max, IM_COL32(24, 24, 24, 255), key_rounding)
local face_min = ImVec2(key_min.x + key_face_pos.x, key_min.y + key_face_pos.y)
local face_max = ImVec2(face_min.x + key_face_size.x, face_min.y + key_face_size.y)
draw_list:AddRect(face_min, face_max, IM_COL32(193, 193, 193, 255), key_face_rounding, 2.0)
draw_list:AddRectFilled(face_min, face_max, IM_COL32(252, 252, 252, 255), key_face_rounding)
local label_min = ImVec2(key_min.x + key_label_pos.x, key_min.y + key_label_pos.y)
draw_list:AddText(label_min, IM_COL32(64, 64, 64, 255), key_data[3])
if ImGui.IsKeyDown(key_data[4]) then
draw_list:AddRectFilled(key_min, key_max, IM_COL32(255, 0, 0, 128), key_rounding)
end
end
draw_list:PopClipRect()
end
local function DebugFlashStyleColorStop()
local g = GImGui
if g.DebugFlashStyleColorIdx ~= 63 --[[ImGuiCol.COUNT]] then
ImVec4_Copy(g.Style.Colors[g.DebugFlashStyleColorIdx], g.DebugFlashStyleColorBackup)
end
g.DebugFlashStyleColorIdx = 63 --[[ImGuiCol.COUNT]]
end
-- Flash a given style color for some + inhibit modifications of this color via PushStyleColor() calls.
--- @param idx ImGuiCol
function ImGui.DebugFlashStyleColor(idx)
local g = GImGui
DebugFlashStyleColorStop()
g.DebugFlashStyleColorTime = 0.5
g.DebugFlashStyleColorIdx = idx
ImVec4_Copy(g.DebugFlashStyleColorBackup, g.Style.Colors[idx])
end
function ImGui.UpdateDebugToolFlashStyleColor()
local g = GImGui
if g.DebugFlashStyleColorTime <= 0.0 then
return
end
local col = g.Style.Colors[g.DebugFlashStyleColorIdx]
col.x, col.y, col.z = ImGui.ColorConvertHSVtoRGB(math.cos(g.DebugFlashStyleColorTime * 6.0) * 0.5 + 0.5, 0.5, 0.5)
col.w = 1.0
g.DebugFlashStyleColorTime = g.DebugFlashStyleColorTime - g.IO.DeltaTime
if g.DebugFlashStyleColorTime <= 0.0 then
DebugFlashStyleColorStop()
end
end
-- [Luau] ImTextureID can be any Lua value here: numbers are returned as is, anything else is returned unchanged too
-- (there is no memcpy into an ImU64); FormatTextureRefForDebugDisplay() formats non-numbers with tostring().
--- @param tex_id ImTextureID
function ImGui.DebugTextureIDToU64(tex_id)
return tex_id
end
--- @param tex_ref ImTextureRef
--- @return string
local function FormatTextureRefForDebugDisplay(tex_ref)
local buf = ""
if tex_ref._TexData ~= nil then
buf = string.format("#%03d: ", tex_ref._TexData.UniqueID)
end
local v = ImGui.DebugTextureIDToU64(tex_ref:GetTexID())
if type(v) == "number" and v >= 0 and v == math.floor(v) then
return buf .. string.format("0x%X", v)
end
return buf .. tostring(v)
end
-- Demo helper function to select among loaded fonts.
-- Here we use the regular BeginCombo()/EndCombo() api which is the more flexible one.
function ImGui.ShowFontSelector(label)
local io = ImGui.GetIO()
local font_current = ImGui.GetFont()
if ImGui.BeginCombo(label, font_current:GetDebugName()) then
for i, font in io.Fonts.Fonts:iter() do
ImGui.PushID(i)
if ImGui.Selectable(font:GetDebugName(), font == font_current, 64 --[[ImGuiSelectableFlags.SelectOnNav]]) then io.FontDefault = font end
if font == font_current then ImGui.SetItemDefaultFocus() end
ImGui.PopID()
end
ImGui.EndCombo()
end
ImGui.SameLine()
MetricsHelpMarker("- Load additional fonts with io.Fonts->AddFontXXX() functions.\n- On Roblox: register the .ttf bytes with ImGui_ImplRoblox.AddFile() first.\n- Read FAQ and docs/FONTS.md for more details.")
end
function ImGui.DebugNodeFontGlyph(font, glyph)
ImGui.Text("Codepoint: U+%04X", glyph.Codepoint)
ImGui.Separator()
ImGui.Text("Visible: %d", glyph.Visible and 1 or 0)
ImGui.Text("AdvanceX: %.1f", glyph.AdvanceX)
ImGui.Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph.X0, glyph.Y0, glyph.X1, glyph.Y1)
ImGui.Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph.U0, glyph.V0, glyph.U1, glyph.V1)
if glyph.PackId >= 0 then
local r = { uv0 = ImVec2(), uv1 = ImVec2() }
if font.OwnerAtlas:GetCustomRect(glyph.PackId, r) then ImGui.Text("PackId: 0x%X (%dx%d rect at %d,%d)", glyph.PackId, r.w, r.h, r.x, r.y) end
end
ImGui.Text("SourceIdx: %d", glyph.SourceIdx)
end
function ImGui.DebugNodeFontGlyphsForSrcMask(font, baked, src_mask)
local draw_list = ImGui.GetWindowDrawList()
local glyph_col = ImGui.GetColorU32(0 --[[ImGuiCol.Text]])
local cell_size = baked.Size * 1
local cell_spacing = ImGui.GetStyle().ItemSpacing.y
-- [port] only scans loaded codepoints (IndexLookup range) instead of the whole 0..0xFFFF range
local max_c = baked.IndexLookup.Size - 1
local base = 0
while base <= max_c do
local count = 0
-- [Luau] inline IsGlyphLoaded() + FindGlyphNoFallback() (a loaded glyph never needs loading): 0xFFFF = IM_FONTGLYPH_INDEX_UNUSED, 0xFFFE = IM_FONTGLYPH_INDEX_NOT_FOUND
local lookup, lookup_n, glyphs = baked.IndexLookup.Data, baked.IndexLookup.Size, baked.Glyphs.Data
for n = 0, 255 do
local c = base + n
if c < lookup_n then
local i = lookup[c + 1]
if i ~= 0xFFFF and i ~= 0xFFFE then
local glyph = glyphs[i]
if glyph and bit32.band(src_mask, bit32.lshift(1, glyph.SourceIdx)) ~= 0 then count = count + 1 end
end
end
end
if count > 0 and ImGui.TreeNode(base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, (count > 1) and "glyphs" or "glyph") then
-- Draw a 16x16 grid of glyphs
local base_pos = ImGui.GetCursorScreenPos()
local cell_p1, cell_p2 = ImVec2(), ImVec2()
for n = 0, 255 do
local c = base + n
-- We use ImFont:RenderChar as a shortcut because we don't have UTF-8 conversion functions
-- available here and thus cannot easily generate a zero-terminated UTF-8 encoded string.
-- [Luau] reused per cell: AddRect()/RenderChar()/IsMouseHoveringRect() only read them
cell_p1.x = base_pos.x + (n % 16) * (cell_size + cell_spacing); cell_p1.y = base_pos.y + math.floor(n / 16) * (cell_size + cell_spacing)
cell_p2.x = cell_p1.x + cell_size; cell_p2.y = cell_p1.y + cell_size
local glyph = nil -- [Luau] inline IsGlyphLoaded() + FindGlyphNoFallback(), see above
if c < baked.IndexLookup.Size then
local i = baked.IndexLookup.Data[c + 1]
if i ~= 0xFFFF and i ~= 0xFFFE then glyph = baked.Glyphs.Data[i] end
end
draw_list:AddRect(cell_p1, cell_p2, glyph and IM_COL32(255, 255, 255, 100) or IM_COL32(255, 255, 255, 50))
if glyph and bit32.band(src_mask, bit32.lshift(1, glyph.SourceIdx)) ~= 0 then
font:RenderChar(draw_list, cell_size, cell_p1, glyph_col, c)
if ImGui.IsMouseHoveringRect(cell_p1, cell_p2) and ImGui.BeginTooltip() then
ImGui.DebugNodeFontGlyph(font, glyph)
ImGui.EndTooltip()
end
end
end
ImGui.Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16))
ImGui.TreePop()
end
base = base + 256
end
end
-- [DEBUG] Display details for a single font, called by ShowStyleEditor().
-- [DEBUG] Compute mask of inputs with the same codepoint.
-- [Luau] the port's font loaders expose FontSrcContainsGlyph() (pre-1.93 loader API) instead of FontSrcGetGlyphIndexFromCodepoint() != 0.
local function CalcFontGlyphSrcOverlapMask(atlas, font, codepoint)
local mask, count = 0, 0
for src_n = 0, font.Sources.Size - 1 do repeat
local src = font.Sources.Data[src_n + 1]
local loader = src.FontLoader or atlas.FontLoader
if not (loader.FontSrcContainsGlyph ~= nil and loader.FontSrcContainsGlyph(atlas, src, codepoint)) then
break -- continue
end
mask = bit32.bor(mask, bit32.lshift(1, src_n))
count = count + 1
until true end
return (count > 1) and mask or 0
end
function ImGui.DebugNodeFont(font)
local g = ImGui.GetCurrentContext()
local cfg = g.DebugMetricsConfig
local atlas = font.OwnerAtlas
local opened = ImGui.TreeNode(tostring(font), "Font: \"%s\": %d sources(s)", font:GetDebugName(), font.Sources.Size)
-- Display preview text
if not opened then ImGui.Indent() end
ImGui.Indent()
if cfg.ShowFontPreview then
ImGui.PushFont(font, 0.0)
ImGui.Text("The quick brown fox jumps over the lazy dog")
ImGui.PopFont()
end
if not opened then
ImGui.Unindent()
ImGui.Unindent()
return
end
if ImGui.SmallButton("Set as default") then ImGui.GetIO().FontDefault = font end
ImGui.SameLine()
ImGui.BeginDisabled(atlas.Fonts.Size <= 1 or atlas.Locked == true or atlas.RemoveFont == nil)
if ImGui.SmallButton("Remove") and atlas.RemoveFont then atlas:RemoveFont(font) end
ImGui.EndDisabled()
ImGui.SameLine()
if ImGui.SmallButton("Clear bakes") then ImFontAtlasFontDiscardBakes(atlas, font, 0) end
ImGui.SameLine()
if ImGui.SmallButton("Clear unused") then ImFontAtlasFontDiscardBakes(atlas, font, 2) end
-- Display details
ImGui.Text("Fallback character: '%s' (U+%04X)", U8(font.FallbackChar), font.FallbackChar or 0)
ImGui.Text("Ellipsis character: '%s' (U+%04X)", U8(font.EllipsisChar), font.EllipsisChar or 0)
for src_n, src in font.Sources:iter() do
if ImGui.TreeNode(tostring(src), "Input %d: '%s' [%d], Oversample: %d,%d, PixelSnapH: %d, Offset: (%.1f,%.1f)",
src_n - 1, src.Name or "", src.FontNo or 0, src.OversampleH or 0, src.OversampleV or 0, src.PixelSnapH and 1 or 0, src.GlyphOffset.x, src.GlyphOffset.y) then
local loader = src.FontLoader or atlas.FontLoader
ImGui.Text("Loader: '%s'", (loader and loader.Name) or "N/A")
-- if ImGui.DragFloat("ExtraSizeScale", src.ExtraSizeScale, 0.01, 0.10, 2.0) then
-- ImFontAtlasFontRebuildOutput(atlas, font)
-- end
ImGui.TreePop()
end
end
if font.Sources.Size > 1 and ImGui.TreeNode("Input Glyphs Overlap Detection Tool") then
ImGui.TextWrapped("- First Input that contains the glyph is used.\n" ..
"- Use ImFontConfig::GlyphExcludeRanges[] to specify ranges to ignore glyph in given Input.\n- Prefer using a small number of ranges as the list is scanned every time a new glyph is loaded,\n - e.g. GlyphExcludeRanges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };\n- This tool doesn't cache results and is slow, don't keep it open!")
if ImGui.BeginTable("table", 2) then
local c = 0
while c < 0x10000 do
local overlap_mask = CalcFontGlyphSrcOverlapMask(atlas, font, c)
if overlap_mask ~= 0 then
local c_end = c + 1
while c_end < 0x10000 and CalcFontGlyphSrcOverlapMask(atlas, font, c_end) == overlap_mask do
c_end = c_end + 1
end
if ImGui.TableNextColumn() and ImGui.TreeNode(c, "U+%04X-U+%04X: %d codepoints in %d inputs", c, c_end - 1, c_end - c, ImCountSetBits(overlap_mask)) then
for n = c, c_end - 1 do
ImGui.BulletText("Codepoint U+%04X (%s)", n, U8(n))
end
ImGui.TreePop()
end
ImGui.TableNextColumn()
for src_n = 0, font.Sources.Size - 1 do
if bit32.band(overlap_mask, bit32.lshift(1, src_n)) ~= 0 then
ImGui.Text("%d ", src_n)
ImGui.SameLine()
end
end
c = c_end - 1
end
c = c + 1
end
ImGui.EndTable()
end
ImGui.TreePop()
end
-- Display all glyphs of the fonts in separate pages of 256 characters
local builder = atlas.Builder
if builder then
for _, baked in builder.BakedPool:iter() do
if baked.OwnerFont == font then
ImGui.PushID(baked.BakedId)
if ImGui.TreeNode("Glyphs", "Baked at { %.2fpx, d.%.2f }: %d glyphs%s", baked.Size, baked.RasterizerDensity, baked.Glyphs.Size,
(baked.LastUsedFrame < (builder.FrameCount or 0) - 1) and " *Unused*" or "") then
if ImGui.SmallButton("Load all") then
for c = 0, 0x7F do baked:FindGlyph(c) end -- [port] ASCII only: loading the whole BMP in Luau stalls for seconds
end
local surface_sqrt = math.floor(math.sqrt(baked.MetricsTotalSurface))
ImGui.Text("Ascent: %f, Descent: %f, Ascent-Descent: %f", baked.Ascent, baked.Descent, baked.Ascent - baked.Descent)
ImGui.Text("Texture Area: about %d px ~%dx%d px", baked.MetricsTotalSurface, surface_sqrt, surface_sqrt)
for src_n, src in font.Sources:iter() do
ImGui.BulletText("Input %d: '%s', Oversample: (%d,%d), PixelSnapH: %d, Offset: (%.1f,%.1f)",
src_n - 1, src.Name or "", src.OversampleH or 0, src.OversampleV or 0, src.PixelSnapH and 1 or 0, src.GlyphOffset.x, src.GlyphOffset.y)
end
ImGui.DebugNodeFontGlyphsForSrcMask(font, baked, bit32.bnot(0))
ImGui.TreePop()
end
ImGui.PopID()
end
end
end
ImGui.TreePop()
ImGui.Unindent()
end
local StatusNames = { [0] = "OK", "Destroyed", "WantCreate", "WantUpdates", "WantDestroy" }
local FormatNames = { [0] = "RGBA32", "Alpha8" }
function ImGui.DebugNodeTexture(tex, int_id, highlight_rect)
local g = ImGui.GetCurrentContext()
ImGui.PushID(int_id)
if ImGui.TreeNode("", "Texture #%03d (%dx%d pixels)", tex.UniqueID or 0, tex.Width, tex.Height) then
local cfg = g.DebugMetricsConfig
_, cfg.ShowTextureUsedRect = ImGui.Checkbox("Show used rect", cfg.ShowTextureUsedRect)
ImGui.PushStyleVar(24 --[[ImGuiStyleVar.ImageBorderSize]], math.max(1.0, g.Style.ImageBorderSize))
local p = ImGui.GetCursorScreenPos()
if tex.Status == 4 --[[ImTextureStatus.WantDestroy]] or tex.Status == 1 --[[ImTextureStatus.Destroyed]] then
ImGui.Dummy(ImVec2(tex.Width, tex.Height))
else
local ref = ImTextureRef(); ref._TexData = tex
ImGui.ImageWithBg(ref, ImVec2(tex.Width, tex.Height), ImVec2(0.0, 0.0), ImVec2(1.0, 1.0), ImVec4(0.0, 0.0, 0.0, 1.0))
end
if cfg.ShowTextureUsedRect and tex.UsedRect then
local u = tex.UsedRect
ImGui.GetWindowDrawList():AddRect(ImVec2(p.x + u.x, p.y + u.y), ImVec2(p.x + u.x + u.w, p.y + u.y + u.h), IM_COL32(255, 0, 255, 255))
end
if highlight_rect ~= nil then
local r_outer = ImRect(p.x, p.y, p.x + tex.Width, p.y + tex.Height)
local r_inner = ImRect(p.x + highlight_rect.x, p.y + highlight_rect.y, p.x + highlight_rect.x + highlight_rect.w, p.y + highlight_rect.y + highlight_rect.h)
ImGui.RenderRectFilledWithHole(ImGui.GetWindowDrawList(), r_outer, r_inner, IM_COL32(0, 0, 0, 100), 0.0)
ImGui.GetWindowDrawList():AddRect(r_inner.Min - ImVec2(1, 1), r_inner.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255))
end
ImGui.PopStyleVar()
ImGui.Text("Status = %s (%d), Format = %s (%d), UseColors = %d", StatusNames[tex.Status] or "?", tex.Status, FormatNames[tex.Format] or "?", tex.Format, tex.UseColors and 1 or 0)
ImGui.Text("TexRef = %s, BackendUserData = %s", FormatTextureRefForDebugDisplay(tex:GetTexRef()), tostring(tex.BackendUserData))
ImGui.TreePop()
end
ImGui.PopID()
end
-- [DEBUG] List fonts in a font atlas and display its texture
function ImGui.ShowFontAtlas(atlas)
local g = ImGui.GetCurrentContext()
local io = g.IO
local style = g.Style
ImGui.BeginDisabled()
ImGui.CheckboxFlags("io.BackendFlags: RendererHasTextures", io.BackendFlags, 16 --[[ImGuiBackendFlags.RendererHasTextures]])
ImGui.EndDisabled()
ImGui.ShowFontSelector("Font")
local changed
style.FontSizeBase, changed = ImGui.DragFloat("FontSizeBase", style.FontSizeBase, 0.20, 5.0, 100.0, "%.0f")
if changed then style._NextFrameFontSizeBase = style.FontSizeBase end -- FIXME: Temporary hack until we finish remaining work.
ImGui.SameLine(0.0, 0.0); ImGui.Text(" (out %.2f)", ImGui.GetFontSize())
ImGui.SameLine(); MetricsHelpMarker("- This is scaling font only. General scaling will come later.")
style.FontScaleMain = ImGui.DragFloat("FontScaleMain", style.FontScaleMain, 0.02, 0.5, 4.0)
style.FontScaleDpi = ImGui.DragFloat("FontScaleDpi", style.FontScaleDpi, 0.02, 0.5, 4.0)
ImGui.BulletText("Load a nice font for better results!")
ImGui.BulletText("Please submit feedback:")
ImGui.SameLine(); ImGui.TextLinkOpenURL("#8465", "https://github.com/ocornut/imgui/issues/8465")
ImGui.BulletText("Read FAQ for more details:")
ImGui.SameLine(); ImGui.TextLinkOpenURL("dearimgui.com/faq", "https://www.dearimgui.com/faq/")
ImGui.SeparatorText("Font List")
local cfg = g.DebugMetricsConfig
_, cfg.ShowFontPreview = ImGui.Checkbox("Show font preview", cfg.ShowFontPreview)
-- Font loaders
if ImGui.TreeNode("Loader", "Loader: '%s'", atlas.FontLoaderName or "NULL") then
ImGui.BeginDisabled()
ImGui.RadioButtonEx("stb_truetype", true)
ImGui.SameLine()
ImGui.RadioButtonEx("FreeType", false)
ImGui.SetItemTooltip("Not available on Roblox.")
ImGui.EndDisabled()
ImGui.TreePop()
end
-- Font list
for i, font in atlas.Fonts:iter() do
ImGui.PushID(i)
ImGui.DebugNodeFont(font)
ImGui.PopID()
end
ImGui.SeparatorText("Font Atlas")
if ImGui.Button("Compact") then atlas:CompactCache() end
ImGui.SameLine()
if ImGui.Button("Grow") then ImFontAtlasTextureGrow(atlas) end
ImGui.SameLine()
if ImGui.Button("Clear All") then ImFontAtlasBuildClear(atlas) end
ImGui.SetItemTooltip("Destroy cache and custom rectangles.")
for tex_n, tex in atlas.TexList:iter() do
if tex_n > 1 then ImGui.SameLine() end
ImGui.Text("Tex: %dx%d", tex.Width, tex.Height)
end
local b = atlas.Builder
if b then
local packed_sqrt = math.floor(math.sqrt(b.RectsPackedSurface or 0))
local discarded_sqrt = math.floor(math.sqrt(b.RectsDiscardedSurface or 0))
ImGui.Text("Packed rects: %d, area: about %d px ~%dx%d px", b.RectsPackedCount or 0, b.RectsPackedSurface or 0, packed_sqrt, packed_sqrt)
ImGui.Text("incl. Discarded rects: %d, area: about %d px ~%dx%d px", b.RectsDiscardedCount or 0, b.RectsDiscardedSurface or 0, discarded_sqrt, discarded_sqrt)
end
-- Texture list
-- (ensure the last texture always use the same ID, so we can keep it open neatly)
for tex_n, tex in atlas.TexList:iter() do
if tex_n == atlas.TexList.Size then ImGui.SetNextItemOpen(true, 2 --[[ImGuiCond.Once]]) end
ImGui.DebugNodeTexture(tex, atlas.TexList.Size - tex_n, nil)
end
end
-------------------------------------------------------------------------------
-- [SECTION] DEBUG LOG WINDOW
-------------------------------------------------------------------------------
-- FIXME-LAYOUT: To be done automatically via layout mode once we rework ItemSize/ItemAdd into ItemLayout.
local function SameLineOrWrap(size)
local g = ImGui.GetCurrentContext()
local window = g.CurrentWindow
local pos = ImVec2(window.DC.CursorPosPrevLine.x + g.Style.ItemSpacing.x, window.DC.CursorPosPrevLine.y)
if window.WorkRect:Contains(ImRect(pos, pos + size)) then ImGui.SameLine() end
end
local function ShowDebugLogFlag(name, flags)
local g = ImGui.GetCurrentContext()
local size = ImVec2(ImGui.GetFrameHeight() + g.Style.ItemInnerSpacing.x + ImGui.CalcTextSize(name).x, ImGui.GetFrameHeight())
SameLineOrWrap(size) -- FIXME-LAYOUT: To be done automatically once we rework ItemSize/ItemAdd into ItemLayout.
local pressed
pressed, g.DebugLogFlags = ImGui.CheckboxFlags(name, g.DebugLogFlags, flags)
if pressed and g.IO.KeyShift and bit32.band(g.DebugLogFlags, flags) ~= 0 then
g.DebugLogAutoDisableFrames = 2
g.DebugLogAutoDisableFlags = bit32.bor(g.DebugLogAutoDisableFlags or 0, flags)
end
ImGui.SetItemTooltip("Hold Shift when clicking to enable for 2 frames only (useful for spammy log entries)")
end
function ImGui.ShowDebugLogWindow(p_open)
local g = ImGui.GetCurrentContext()
if bit32.band(g.NextWindowData.HasFlags, 2 --[[ImGuiNextWindowDataFlags.HasSize]]) == 0 then
ImGui.SetNextWindowSize(ImVec2(0.0, ImGui.GetFontSize() * 12.0), 4 --[[ImGuiCond.FirstUseEver]])
end
local visible
p_open, visible = ImGui.Begin("Dear ImGui Debug Log", p_open)
if not visible or ImGui.GetCurrentWindow().BeginCount > 1 then
ImGui.End()
return p_open
end
local L = ImGuiDebugLogFlags
local all_enable_flags = bit32.band(L.EventMask_, bit32.bnot(L.EventInputRouting))
_, g.DebugLogFlags = ImGui.CheckboxFlags("All", g.DebugLogFlags, all_enable_flags)
ImGui.SetItemTooltip("(except InputRouting which is spammy)")
for _, e in ipairs({ { "Errors", L.EventError }, { "ActiveId", L.EventActiveId }, { "Clipper", L.EventClipper }, { "Docking", L.EventDocking },
{ "Focus", L.EventFocus }, { "IO", L.EventIO }, { "Font", L.EventFont }, { "Nav", L.EventNav }, { "Popup", L.EventPopup },
{ "Selection", L.EventSelection }, { "Table", L.EventTable }, { "Viewport", L.EventViewport }, { "InputRouting", L.EventInputRouting } }) do
ShowDebugLogFlag(e[1], e[2])
end
if ImGui.SmallButton("Clear") then
g.DebugLogBuf:clear()
g.DebugLogLines = { "" }
end
ImGui.SameLine()
if ImGui.SmallButton("Copy") then ImGui.SetClipboardText(g.DebugLogBuf:c_str()) end
ImGui.SameLine()
if ImGui.SmallButton("Configure Outputs..") then ImGui.OpenPopup("Outputs") end
if ImGui.BeginPopup("Outputs") then
_, g.DebugLogFlags = ImGui.CheckboxFlags("OutputToTTY", g.DebugLogFlags, L.OutputToTTY)
ImGui.BeginDisabled()
_, g.DebugLogFlags = ImGui.CheckboxFlags("OutputToDebugger", g.DebugLogFlags, L.OutputToDebugger)
_, g.DebugLogFlags = ImGui.CheckboxFlags("OutputToTestEngine", g.DebugLogFlags, L.OutputToTestEngine)
ImGui.EndDisabled()
ImGui.EndPopup()
end
ImGui.BeginChild("##log", ImVec2(0.0, 0.0), 1 --[[ImGuiChildFlags.Borders]], bit32.bor(16384 --[[ImGuiWindowFlags.AlwaysVerticalScrollbar]], 32768 --[[ImGuiWindowFlags.AlwaysHorizontalScrollbar]]))
local backup_log_flags = g.DebugLogFlags
g.DebugLogFlags = bit32.band(g.DebugLogFlags, bit32.bnot(L.EventClipper))
local lines = g.DebugLogLines
local count = #lines
if lines[count] == "" then count = count - 1 end -- trailing partial line
local clipper = ImGuiListClipper()
clipper:Begin(count)
while clipper:Step() do
for line_no = clipper.DisplayStart, clipper.DisplayEnd - 1 do ImGui.DebugTextUnformattedWithLocateItem(lines[line_no + 1]) end
end
g.DebugLogFlags = backup_log_flags
if ImGui.GetScrollY() >= ImGui.GetScrollMaxY() then ImGui.SetScrollHereY(1.0) end
ImGui.EndChild()
ImGui.End()
return p_open
end
-- Display line, search for 0xXXXXXXXX identifiers and call DebugLocateItemOnHover() when hovered.
-- [Luau] line_begin is the line string, line_end its optional exclusive end byte position (default: whole string).
function ImGui.DebugTextUnformattedWithLocateItem(line_begin, line_end)
if line_end == nil then line_end = #line_begin + 1 end
ImGui.TextUnformatted(line_begin, line_end)
if not ImGui.IsItemHovered(bit32.bor(32 --[[ImGuiHoveredFlags.AllowWhenBlockedByPopup]], 128 --[[ImGuiHoveredFlags.AllowWhenBlockedByActiveItem]])) then
return
end
local g = GImGui
local text_rect = ImRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max) -- [Luau] copy (LastItemData.Rect is overwritten below)
local p = 1
while p <= line_end - 10 do repeat -- [Luau] 1-based (C: p <= line_end - 10 with 0-based offsets)
local b0, b1 = string.byte(line_begin, p, p + 1)
local hex = string.match(line_begin, "^%x+", p + 2)
if b0 ~= 48 or (b1 ~= 120 and b1 ~= 88) or hex == nil or string.find(string.sub(line_begin, p + 10, p + 10), "^%x") then -- '0', 'x', 'X'
p = p + 1
break -- continue
end
local id = tonumber(hex, 16) -- [Luau] sscanf("%X")
local p0 = ImGui.CalcTextSizeEx(line_begin, 1, p)
local p1 = ImGui.CalcTextSizeEx(line_begin, p, p + 10)
g.LastItemData.Rect = ImRect(text_rect.Min + ImVec2(p0.x, 0.0), text_rect.Min + ImVec2(p0.x + p1.x, p1.y))
if ImGui.IsMouseHoveringRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, true) then
ImGui.DebugLocateItemOnHover(id)
end
p = p + 11 -- p += 10, then the loop's p++
until true end
end
-------------------------------------------------------------------------------
-- [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL)
-------------------------------------------------------------------------------
-- Draw a small cross at current CursorPos in current window's DrawList
--- @param col? ImU32
function ImGui.DebugDrawCursorPos(col)
if col == nil then col = IM_COL32(255, 0, 0, 255) end
local g = GImGui
local window = g.CurrentWindow
local pos = window.DC.CursorPos
window.DrawList:AddLineV(pos.x, pos.y - 3.0, pos.y + 4.0, col, 1.0)
window.DrawList:AddLineH(pos.x - 3.0, pos.x + 4.0, pos.y, col, 1.0)
end
-- Draw a 10px wide rectangle around CurposPos.x using Line Y1/Y2 in current window's DrawList
--- @param col? ImU32
function ImGui.DebugDrawLineExtents(col)
if col == nil then col = IM_COL32(255, 0, 0, 255) end
local g = GImGui
local window = g.CurrentWindow
local curr_x = window.DC.CursorPos.x
local line_y1 = window.DC.IsSameLine and window.DC.CursorPosPrevLine.y or window.DC.CursorPos.y
local line_y2 = line_y1 + (window.DC.IsSameLine and window.DC.PrevLineSize.y or window.DC.CurrLineSize.y)
window.DrawList:AddLineH(curr_x - 4.0, curr_x + 5.0, line_y1, col, 1.0)
window.DrawList:AddLineV(curr_x, line_y1, line_y2, col, 1.0)
window.DrawList:AddLineH(curr_x - 4.0, curr_x + 5.0, line_y2, col, 1.0)
end
-- Draw last item rect in ForegroundDrawList (so it is always visible)
--- @param col? ImU32
function ImGui.DebugDrawItemRect(col)
if col == nil then col = IM_COL32(255, 0, 0, 255) end
local g = GImGui
local window = g.CurrentWindow
ImGui.GetForegroundDrawList(window.Viewport):AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col)
end
-- [DEBUG] Locate item position/rectangle given an ID.
local DEBUG_LOCATE_ITEM_COLOR = IM_COL32(0, 255, 0, 255) -- Green
--- @param target_id ImGuiID
function ImGui.DebugLocateItem(target_id)
local g = GImGui
g.DebugLocateId = target_id
g.DebugLocateFrames = 2
g.DebugBreakInLocateId = false
end
-- FIXME: Doesn't work over through a modal window, because they clear HoveredWindow.
--- @param target_id ImGuiID
function ImGui.DebugLocateItemOnHover(target_id)
if target_id == 0 or not ImGui.IsItemHovered(bit32.bor(128 --[[ImGuiHoveredFlags.AllowWhenBlockedByActiveItem]], 32 --[[ImGuiHoveredFlags.AllowWhenBlockedByPopup]])) then
return
end
local g = GImGui
ImGui.DebugLocateItem(target_id)
ImGui.GetForegroundDrawList(g.CurrentWindow.Viewport):AddRect(g.LastItemData.Rect.Min - ImVec2(3.0, 3.0), g.LastItemData.Rect.Max + ImVec2(3.0, 3.0), DEBUG_LOCATE_ITEM_COLOR)
-- Can't easily use a context menu here because it will mess with focus, active id etc.
if g.IO.ConfigDebugIsDebuggerPresent and g.MouseStationaryTimer > 1.0 then
ImGui.DebugBreakButtonTooltip(false, "in ItemAdd()")
if ImGui.IsKeyChordPressed(g.DebugBreakKeyChord) then
g.DebugBreakInLocateId = true
end
end
end
function ImGui.DebugLocateItemResolveWithLastItem()
local g = GImGui
-- [DEBUG] Debug break requested by user
if g.DebugBreakInLocateId then
IM_DEBUG_BREAK()
end
local item_data = g.LastItemData
g.DebugLocateId = 0
local draw_list = ImGui.GetForegroundDrawList(g.CurrentWindow.Viewport)
local r = ImRect(item_data.Rect.Min, item_data.Rect.Max) -- [Luau] copy
r:Expand(3.0)
local p1 = g.IO.MousePos
local p2 = ImVec2((p1.x < r.Min.x) and r.Min.x or ((p1.x > r.Max.x) and r.Max.x or p1.x), (p1.y < r.Min.y) and r.Min.y or ((p1.y > r.Max.y) and r.Max.y or p1.y))
draw_list:AddRect(r.Min, r.Max, DEBUG_LOCATE_ITEM_COLOR)
draw_list:AddLine(p1, p2, DEBUG_LOCATE_ITEM_COLOR)
end
function ImGui.DebugStartItemPicker()
local g = GImGui
g.DebugItemPickerActive = true
end
-- [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
function ImGui.UpdateDebugToolItemPicker()
local g = GImGui
g.DebugItemPickerBreakId = 0
if not g.DebugItemPickerActive then
return
end
local hovered_id = g.HoveredIdPreviousFrame
ImGui.SetMouseCursor(7 --[[ImGuiMouseCursor.Hand]])
if ImGui.IsKeyPressed(526 --[[ImGuiKey.Escape]]) then
g.DebugItemPickerActive = false
end
local change_mapping = g.IO.KeyMods == bit32.bor(4096 --[[ImGuiMod_Ctrl]], 8192 --[[ImGuiMod_Shift]])
if not change_mapping and ImGui.IsMouseClicked(g.DebugItemPickerMouseButton) and hovered_id ~= 0 then
g.DebugItemPickerBreakId = hovered_id
g.DebugItemPickerActive = false
end
for mouse_button = 0, 2 do
if change_mapping and ImGui.IsMouseClicked(mouse_button) then
g.DebugItemPickerMouseButton = mouse_button
end
end
ImGui.SetNextWindowBgAlpha(0.70)
if not ImGui.BeginTooltip() then
return
end
ImGui.Text("HoveredId: 0x%08X", hovered_id)
ImGui.Text("Press ESC to abort picking.")
local mouse_button_names = { "Left", "Right", "Middle" }
if change_mapping then
ImGui.Text("Remap w/ Ctrl+Shift: click anywhere to select new mouse button.")
else
ImGui.TextColored(ImGui.GetStyleColorVec4((hovered_id ~= 0) and 0 --[[ImGuiCol.Text]] or 1 --[[ImGuiCol.TextDisabled]]), "Click %s Button to break in debugger! (remap w/ Ctrl+Shift)", mouse_button_names[g.DebugItemPickerMouseButton + 1])
end
ImGui.EndTooltip()
end
local function PathQuery(g)
return g.DebugItemPathQuery
end
-- Update queries. The steps are: -1: query Stack, >= 0: query each stack item
-- We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time
local function DebugItemPathQuery_UpdateAndGetHookId(query, id)
-- Update query. Clear hook when no active query
if query.MainID ~= id then
query.MainID = id
query.Step = -1
query.Complete = false
query.Results = {}
end
query.Active = false
if id == 0 then return 0 end
local res = query.Results
-- Advance to next stack level when we got our result, or after 2 frames (in case we never get a result)
if query.Step >= 0 and query.Step < #res then
if res[query.Step + 1].QuerySuccess or res[query.Step + 1].QueryFrameCount > 2 then query.Step = query.Step + 1 end
end
-- Update status and hook
query.Complete = (query.Step == #res)
if query.Step == -1 then
query.Active = true
return id
elseif query.Step >= 0 and query.Step < #res then
res[query.Step + 1].QueryFrameCount = res[query.Step + 1].QueryFrameCount + 1
query.Active = true
return res[query.Step + 1].ID
end
return 0
end
-- [DEBUG] ID Stack Tool: update query. Called by NewFrame()
function ImGui.UpdateDebugToolItemPathQuery()
local g = ImGui.GetCurrentContext()
local q = PathQuery(g)
local id = 0
if g.DebugIDStackTool.LastActiveFrame + 1 == g.FrameCount then
id = (g.HoveredIdPreviousFrame ~= 0 and g.HoveredIdPreviousFrame) or g.ActiveId
end
g.DebugHookIdInfoId = DebugItemPathQuery_UpdateAndGetHookId(q, id)
end
-- [DEBUG] ID Stack tool: hooks called by GetID() family functions
-- `data_id` is the string/number that was hashed.
function ImGui.DebugHookIdInfo(id, data_type, data_id)
local g = ImGui.GetCurrentContext()
local query = PathQuery(g)
if not query.Active then return end
local window = g.CurrentWindow
-- Step -1: stack query
-- This assumes that the ID was computed with the current ID stack, which tends to be the case for our widget.
if query.Step == -1 then
query.Step = 0
query.Results = {}
for n = 0, window.IDStack.Size do
local info = ImGuiStackLevelInfo()
info.ID = (n < window.IDStack.Size) and window.IDStack.Data[n + 1] or id
query.Results[n + 1] = info
end
return
end
-- Step 0+: query for individual level
if query.Step ~= window.IDStack.Size then return end
local info = query.Results[query.Step + 1]
if info == nil or info.ID ~= id then return end
if info.Desc == nil then
if data_type == 4 --[[ImGuiDataType.S32]] then info.Desc = string.format("%d", data_id)
elseif data_type == 9 --[[ImGuiDataType.String]] then info.Desc = tostring(data_id)
else info.Desc = string.format("0x%08X [override]", id) end -- PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one.
end
info.QuerySuccess = true
if info.DataType == -1 then info.DataType = data_type end
end
local function SkipUncontributingPrefix(label)
local p = string.find(label, "###", 1, true)
if p then return string.sub(label, p) end
return label
end
local function DebugItemPathQuery_FormatLevelInfo(query, n, format_for_ui)
local info = query.Results[n + 1]
local window = (info.Desc == nil and n == 0) and ImGui.FindWindowByID(info.ID) or nil
if window then return string.format(format_for_ui and "\"%s\" [window]" or "%s", SkipUncontributingPrefix(window.Name)) end -- Source: window name (because the root ID don't call GetID() and so doesn't get hooked)
if info.QuerySuccess then return string.format((format_for_ui and info.DataType == 9 --[[ImGuiDataType.String]]) and "\"%s\"" or "%s", SkipUncontributingPrefix(info.Desc)) end -- Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id)
if query.Step < #query.Results then return "" end -- Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers.
return "???"
end
local function DebugItemPathQuery_GetResultAsPath(query, hex_encode_non_ascii_chars)
local out = {}
for n = 0, #query.Results - 1 do
local desc = DebugItemPathQuery_FormatLevelInfo(query, n, false)
out[#out + 1] = (n == 0) and "//" or "/"
desc = string.gsub(desc, "/", "\\/")
if hex_encode_non_ascii_chars then
desc = string.gsub(desc, "[\128-\255]", function(c) return string.format("\\x%02x", string.byte(c)) end)
end
out[#out + 1] = desc
end
return table.concat(out)
end
-- ID Stack Tool: Display UI
function ImGui.ShowIDStackToolWindow(p_open)
local g = ImGui.GetCurrentContext()
if bit32.band(g.NextWindowData.HasFlags, 2 --[[ImGuiNextWindowDataFlags.HasSize]]) == 0 then
ImGui.SetNextWindowSize(ImVec2(0.0, ImGui.GetFontSize() * 8.0), 4 --[[ImGuiCond.FirstUseEver]])
end
local visible
p_open, visible = ImGui.Begin("Dear ImGui ID Stack Tool", p_open)
if not visible or ImGui.GetCurrentWindow().BeginCount > 1 then
ImGui.End()
return p_open
end
local query = PathQuery(g)
local tool = g.DebugIDStackTool
tool.LastActiveFrame = g.FrameCount
local result_path = DebugItemPathQuery_GetResultAsPath(query, tool.OptHexEncodeNonAsciiChars)
ImGui.Text("0x%08X", query.MainID)
ImGui.SameLine()
MetricsHelpMarker("Hover an item with the mouse to display elements of the ID Stack leading to the item's final ID.\nEach level of the stack correspond to a PushID() call.\nAll levels of the stack are hashed together to make the final ID of a widget (ID displayed at the bottom level of the stack).\nRead FAQ entry about the ID stack for details.")
-- Ctrl+C to copy path
local time_since_copy = g.Time - tool.CopyToClipboardLastTime
ImGui.PushStyleVarY(11 --[[ImGuiStyleVar.FramePadding]], 0.0)
_, tool.OptHexEncodeNonAsciiChars = ImGui.Checkbox("Hex-encode non-ASCII", tool.OptHexEncodeNonAsciiChars)
ImGui.SameLine()
_, tool.OptCopyToClipboardOnCtrlC = ImGui.Checkbox("Ctrl+C: copy path", tool.OptCopyToClipboardOnCtrlC)
ImGui.PopStyleVar()
ImGui.SameLine()
ImGui.TextColored((time_since_copy >= 0.0 and time_since_copy < 0.75 and math.fmod(time_since_copy, 0.25) < 0.25 * 0.5) and ImVec4(1.0, 1.0, 0.3, 1.0) or ImVec4(), "*COPIED*")
if tool.OptCopyToClipboardOnCtrlC and ImGui.Shortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 548 --[[ImGuiKey.C]]), bit32.bor(4096 --[[ImGuiInputFlags.RouteGlobal]], 16384 --[[ImGuiInputFlags.RouteOverFocused]])) then
tool.CopyToClipboardLastTime = g.Time
ImGui.SetClipboardText(result_path)
end
ImGui.Text("- Path \"%s\"", query.Complete and result_path or "")
ImGui.Separator()
-- Display decorated stack
if #query.Results > 0 and ImGui.BeginTable("##table", 3, 1920 --[[ImGuiTableFlags.Borders]]) then
local id_width = ImGui.CalcTextSize("0xDDDDDDDD").x
ImGui.TableSetupColumn("Seed", 16 --[[ImGuiTableColumnFlags.WidthFixed]], id_width)
ImGui.TableSetupColumn("PushID", 8 --[[ImGuiTableColumnFlags.WidthStretch]])
ImGui.TableSetupColumn("Result", 16 --[[ImGuiTableColumnFlags.WidthFixed]], id_width)
ImGui.TableHeadersRow()
for n = 0, #query.Results - 1 do
local info = query.Results[n + 1]
ImGui.TableNextColumn()
ImGui.Text("0x%08X", (n > 0) and query.Results[n].ID or 0)
ImGui.TableNextColumn()
ImGui.TextUnformatted(DebugItemPathQuery_FormatLevelInfo(query, n, true))
ImGui.TableNextColumn()
ImGui.Text("0x%08X", info.ID)
if n == #query.Results - 1 then ImGui.TableSetBgColor(3 --[[ImGuiTableBgTarget.CellBg]], ImGui.GetColorU32(25 --[[ImGuiCol.Header]])) end
end
ImGui.EndTable()
end
ImGui.End()
return p_open
end
-------------------------------------------------------------------------------
-- [SECTION] METRICS/DEBUGGER WINDOW
-------------------------------------------------------------------------------
-- - DebugTextEncoding()
-- - ShowMetricsWindow()
-- - DebugNodeColumns() [Internal]
-- - DebugNodeDockNode() [Internal]
-- - DebugNodeDrawList() [Internal]
-- - DebugNodeStorage() [Internal]
-- - DebugNodeTabBar() [Internal]
-- - DebugNodeTable() [Internal]
-- - DebugNodeTableSettings() [Internal]
-- - DebugNodeViewport() [Internal]
-- - DebugNodeWindow() [Internal]
-- - DebugNodeWindowSettings() [Internal]
-- - DebugNodeWindowsList() [Internal]
-- - DebugNodeWindowsListByBeginStackParent() [Internal]
-------------------------------------------------------------------------------
local function B(v) return v and 1 or 0 end
local function FG(window) return ImGui.GetForegroundDrawList(window and window.Viewport or ImGui.GetMainViewport()) end
local function YellowRect(window, min, max) FG(window):AddRect(min, max, IM_COL32(255, 255, 0, 255)) end
-- [DEBUG] Display contents of Columns
function ImGui.DebugNodeColumns(columns)
if not ImGui.TreeNode(columns.ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns.ID, columns.Count, columns.Flags) then return end
ImGui.BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns.OffMaxX - columns.OffMinX, columns.OffMinX, columns.OffMaxX)
for i, column in columns.Columns:iter() do
ImGui.BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", i - 1, column.OffsetNorm, ImGui.GetColumnOffsetFromNorm(columns, column.OffsetNorm))
end
ImGui.TreePop()
end
-- [DEBUG] Display contents of ImGuiStorage
function ImGui.DebugNodeStorage(storage, label)
local n = 0
for _ in pairs(storage) do n = n + 1 end
if not ImGui.TreeNode(label, "%s: %d entries", label, n) then return end
for k, v in pairs(storage) do
ImGui.BulletText("Key 0x%08X Value { %s }", tonumber(k) or 0, tostring(v))
ImGui.DebugLocateItemOnHover(tonumber(k) or 0)
end
ImGui.TreePop()
end
-- [DEBUG] Display contents of ImDrawList
-- Note that both 'window' and 'viewport' may be nil here. Viewport is generally nil of destroyed popups which previously owned a viewport.
function ImGui.DebugNodeDrawList(window, viewport, draw_list, label)
local g = ImGui.GetCurrentContext()
local cfg = g.DebugMetricsConfig
local cmd_count = draw_list.CmdBuffer.Size
if cmd_count > 0 and draw_list.CmdBuffer.Data[cmd_count].ElemCount == 0 and draw_list.CmdBuffer.Data[cmd_count].UserCallback == nil then
cmd_count = cmd_count - 1
end
local node_open = ImGui.TreeNode(tostring(draw_list), "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list._OwnerName or "", draw_list.VtxBuffer.Size, draw_list.IdxBuffer.Size, cmd_count)
if draw_list == ImGui.GetWindowDrawList() then
ImGui.SameLine()
ImGui.TextColored(ImVec4(1.0, 0.4, 0.4, 1.0), "CURRENTLY APPENDING") -- Can't display stats for active draw list! (we don't have the data double-buffered)
if node_open then ImGui.TreePop() end
return
end
local fg_draw_list = viewport and ImGui.GetForegroundDrawList(viewport) or nil -- Render additional visuals into the top-most draw list
if window and ImGui.IsItemHovered() and fg_draw_list then
fg_draw_list:AddRect(window.Pos, window.Pos + window.Size, IM_COL32(255, 255, 0, 255))
end
if not node_open then return end
if window and not window.WasActive then ImGui.TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!") end
for cmd_n = 1, cmd_count do repeat
local pcmd = draw_list.CmdBuffer.Data[cmd_n]
if pcmd.UserCallback then
ImGui.BulletText("Callback %s, user_data %s", tostring(pcmd.UserCallback), tostring(pcmd.UserCallbackData))
break -- continue
end
local texid_desc = FormatTextureRefForDebugDisplay(pcmd.TexRef)
local cr = pcmd.ClipRect
local buf = string.format("DrawCmd:%5d tris, Tex %s, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
math.floor(pcmd.ElemCount / 3), texid_desc, cr.x, cr.y, cr.z, cr.w)
local pcmd_node_open = ImGui.TreeNode(cmd_n - 1, "%s", buf)
if ImGui.IsItemHovered() and (cfg.ShowDrawCmdMesh or cfg.ShowDrawCmdBoundingBoxes) and fg_draw_list then
ImGui.DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg.ShowDrawCmdMesh, cfg.ShowDrawCmdBoundingBoxes)
end
if not pcmd_node_open then
break -- continue
end
-- Calculate approximate coverage area (touched pixel count)
-- This will be in pixels squared as long there's no post-scaling happening to the renderer output.
-- [Luau] index values are 1-based vertex indices relative to VtxOffset (see ImDrawVtx_Pos())
local idx_buffer = draw_list.IdxBuffer.Data
local vtx_buffer = draw_list.VtxBuffer.Buf
local total_area = 0.0
local idx_n = pcmd.IdxOffset
while idx_n < pcmd.IdxOffset + pcmd.ElemCount do
local triangle = {}
for n = 1, 3 do
triangle[n] = ImVec2(ImDrawVtx_Pos(vtx_buffer, idx_buffer[idx_n + 1] + pcmd.VtxOffset))
idx_n = idx_n + 1
end
total_area = total_area + ImTriangleArea(triangle[1], triangle[2], triangle[3])
end
-- Display vertex information summary. Hover to get all triangles drawn in wire-frame
buf = string.format("Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd.ElemCount, pcmd.VtxOffset, pcmd.IdxOffset, total_area)
ImGui.Selectable(buf)
if ImGui.IsItemHovered() and fg_draw_list then
ImGui.DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false)
end
-- Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
local clipper = ImGuiListClipper()
clipper:Begin(math.floor(pcmd.ElemCount / 3)) -- Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
while clipper:Step() do
local idx_i = pcmd.IdxOffset + clipper.DisplayStart * 3
for prim = clipper.DisplayStart, clipper.DisplayEnd - 1 do
local lines = {}
local triangle = {}
for n = 1, 3 do
local vi = idx_buffer[idx_i + 1] + pcmd.VtxOffset
local o = (vi - 1) * 36 --[[VTX_STRIDE]] -- [Luau] packed vertex: f64 pos.x, pos.y, uv.x, uv.y, u32 col
local px, py = ImDrawVtx_Pos(vtx_buffer, vi)
triangle[n] = ImVec2(px, py)
lines[n] = string.format("%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
(n == 1) and "Vert:" or " ", idx_i, px, py, buffer.readf64(vtx_buffer, o + 16), buffer.readf64(vtx_buffer, o + 24), ImDrawVtx_Col(vtx_buffer, vi))
idx_i = idx_i + 1
end
ImGui.Selectable(table.concat(lines), false)
if fg_draw_list and ImGui.IsItemHovered() then
fg_draw_list:PushDrawFlag(8192 --[[ImDrawFlags.AALines]], false) -- Disable AA on triangle outlines is more readable for very large and thin triangles.
fg_draw_list:AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), 1.0, 512 --[[ImDrawFlags.Closed]])
fg_draw_list:PopDrawFlag()
end
end
end
ImGui.TreePop()
until true end
ImGui.TreePop()
end
-- [DEBUG] Display mesh/aabb of a ImDrawCmd
--- @param out_draw_list ImDrawList
--- @param draw_list ImDrawList
--- @param draw_cmd ImDrawCmd
--- @param show_mesh bool
--- @param show_aabb bool
function ImGui.DebugNodeDrawCmdShowMeshAndBoundingBox(out_draw_list, draw_list, draw_cmd, show_mesh, show_aabb)
assert(show_mesh or show_aabb)
-- Draw wire-frame version of all triangles
local cr = draw_cmd.ClipRect
local clip_rect = ImRect(cr.x, cr.y, cr.z, cr.w)
local vtxs_rect = ImRect(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]], -3.4028234663853e+38 --[[FLT_MAX]], -3.4028234663853e+38 --[[FLT_MAX]])
out_draw_list:PushDrawFlag(8192 --[[ImDrawFlags.AALines]], false) -- Disable AA on triangle outlines is more readable for very large and thin triangles.
local idx_n, idx_end = draw_cmd.IdxOffset, draw_cmd.IdxOffset + draw_cmd.ElemCount
while idx_n < idx_end do
local idx_buffer = draw_list.IdxBuffer.Data -- We don't hold on those pointers past iterations as ->AddPolyline() may invalidate them if out_draw_list==draw_list
local vtx_buffer = draw_list.VtxBuffer.Buf
local triangle = {}
for n = 1, 3 do
triangle[n] = ImVec2(ImDrawVtx_Pos(vtx_buffer, idx_buffer[idx_n + 1] + draw_cmd.VtxOffset))
vtxs_rect:Add(triangle[n])
idx_n = idx_n + 1
end
if show_mesh then
out_draw_list:AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), 1.0, 512 --[[ImDrawFlags.Closed]]) -- In yellow: mesh triangles
end
end
-- Draw bounding boxes
if show_aabb then
out_draw_list:AddRect(ImTrunc(clip_rect.Min), ImTrunc(clip_rect.Max), IM_COL32(255, 0, 255, 255)) -- In pink: clipping rectangle submitted to GPU
out_draw_list:AddRect(ImTrunc(vtxs_rect.Min), ImTrunc(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)) -- In cyan: bounding box of triangles
end
out_draw_list:PopDrawFlag()
end
-- [DEBUG] Display contents of ImGuiTabBar
function ImGui.DebugNodeTabBar(tab_bar, label)
-- Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
local g = ImGui.GetCurrentContext()
local is_active = (tab_bar.PrevFrameVisible >= ImGui.GetFrameCount() - 2)
local names = {}
for tab_n = 1, math.min(tab_bar.Tabs.Size, 3) do
local tab = tab_bar.Tabs.Data[tab_n]
names[#names + 1] = "'" .. (ImGui.TabBarGetTabName and ImGui.TabBarGetTabName(tab_bar, tab) or "?") .. "'"
end
local buf = string.format("%s 0x%08X (%d tabs)%s { %s%s", label, tab_bar.ID, tab_bar.Tabs.Size, is_active and "" or " *Inactive*", table.concat(names, ", "), (tab_bar.Tabs.Size > 3) and " ... }" or " } ")
if not is_active then ImGui.PushStyleColor(0 --[[ImGuiCol.Text]], g.Style.Colors[1 --[[ImGuiCol.TextDisabled]]]) end
local open = ImGui.TreeNode(label, "%s", buf)
if not is_active then ImGui.PopStyleColor() end
if is_active and ImGui.IsItemHovered() then
local dl = FG(tab_bar.Window)
dl:AddRect(tab_bar.BarRect.Min, tab_bar.BarRect.Max, IM_COL32(255, 255, 0, 255))
dl:AddLineV(tab_bar.ScrollingRectMinX, tab_bar.BarRect.Min.y, tab_bar.BarRect.Max.y, IM_COL32(0, 255, 0, 255))
dl:AddLineV(tab_bar.ScrollingRectMaxX, tab_bar.BarRect.Min.y, tab_bar.BarRect.Max.y, IM_COL32(0, 255, 0, 255))
end
if open then
for tab_n, tab in tab_bar.Tabs:iter() do
ImGui.PushID(tab_n)
if ImGui.SmallButton("<") and ImGui.TabBarQueueReorder then ImGui.TabBarQueueReorder(tab_bar, tab, -1) end ImGui.SameLine(0, 2)
if ImGui.SmallButton(">") and ImGui.TabBarQueueReorder then ImGui.TabBarQueueReorder(tab_bar, tab, 1) end ImGui.SameLine()
ImGui.Text("%02d%s Tab 0x%08X '%s' Offset: %.2f, Width: %.2f/%.2f", tab_n - 1, (tab.ID == tab_bar.SelectedTabId) and "*" or " ", tab.ID,
ImGui.TabBarGetTabName and ImGui.TabBarGetTabName(tab_bar, tab) or "?", tab.Offset, tab.Width, tab.ContentWidth)
ImGui.PopID()
end
ImGui.TreePop()
end
end
function ImGui.DebugNodeTableSettings(settings, tbl)
local open = ImGui.TreeNode(tostring(settings), "Settings 0x%08X (%d columns)", settings.ID, settings.ColumnsCount)
local hovered = ImGui.IsItemHovered()
if hovered and tbl == nil and settings.ID ~= 0 then tbl = ImGui.TableFindByID(settings.ID) end
if hovered and tbl ~= nil then YellowRect(tbl.OuterWindow, tbl.OuterRect.Min, tbl.OuterRect.Max) end
if open then
ImGui.BulletText("SaveFlags: 0x%08X", settings.SaveFlags)
ImGui.BulletText("ColumnsCount: %d (max %d)", settings.ColumnsCount, settings.ColumnsCountMax)
for n = 0, settings.ColumnsCount - 1 do
local c = settings.Columns[n]
local dir = (c.SortOrder ~= -1) and c.SortDirection or 0 --[[ImGuiSortDirection.None]]
ImGui.BulletText("Column %d Order %d SortOrder %2d %s Vis %d %s %7.3f ID 0x%08X", n, c.DisplayOrder, c.SortOrder,
(dir == 1 --[[ImGuiSortDirection.Ascending]]) and "Asc" or ((dir == 2 --[[ImGuiSortDirection.Descending]]) and "Des" or "---"), c.IsEnabled, (c.IsStretch ~= 0) and "Weight" or "Width ", c.WidthOrWeight, c.ID)
end
ImGui.TreePop()
end
end
function ImGui.DebugNodeTable(tbl)
local g = ImGui.GetCurrentContext()
local is_active = (tbl.LastFrameActive >= g.FrameCount - 2) -- Note that fully clipped early out scrolling tables will appear as inactive here.
if not is_active then ImGui.PushStyleColor(0 --[[ImGuiCol.Text]], g.Style.Colors[1 --[[ImGuiCol.TextDisabled]]]) end
local open = ImGui.TreeNode(tostring(tbl), "Table 0x%08X (%d columns, in '%s')%s", tbl.ID, tbl.ColumnsCount, tbl.OuterWindow and tbl.OuterWindow.Name or "?", is_active and "" or " *Inactive*")
if not is_active then ImGui.PopStyleColor() end
if ImGui.IsItemHovered() then YellowRect(tbl.OuterWindow, tbl.OuterRect.Min, tbl.OuterRect.Max) end
if not open then return end
if tbl.InstanceCurrent > 0 then ImGui.Text("** %d instances of same table! Some data below will refer to last instance.", tbl.InstanceCurrent + 1) end
local clear_settings = ImGui.SmallButton("Clear settings")
ImGui.BulletText("OuterRect: Pos: (%.1f,%.1f) Size: (%.1f,%.1f) Sizing: '%s'", tbl.OuterRect.Min.x, tbl.OuterRect.Min.y, tbl.OuterRect:GetWidth(), tbl.OuterRect:GetHeight(), DebugNodeTableGetSizingPolicyDesc(tbl.Flags))
ImGui.BulletText("ColumnsGivenWidth: %.1f, ColumnsAutoFitWidth: %.1f, InnerWidth: %.1f%s", tbl.ColumnsGivenWidth, tbl.ColumnsAutoFitWidth, tbl.InnerWidth, (tbl.InnerWidth == 0.0) and " (auto)" or "")
ImGui.BulletText("CellPaddingX: %.1f, CellSpacingX: %.1f/%.1f, OuterPaddingX: %.1f", tbl.CellPaddingX, tbl.CellSpacingX1, tbl.CellSpacingX2, tbl.OuterPaddingX)
ImGui.BulletText("HoveredColumnBody: %d, HoveredColumnBorder: %d", tbl.HoveredColumnBody, tbl.HoveredColumnBorder)
ImGui.BulletText("ResizedColumn: %d, HeldHeaderColumn: %d, ReorderColumn: %d", tbl.LastResizedColumn, tbl.LastHeldHeaderColumn, tbl.ReorderColumn)
for n = 0, tbl.InstanceCurrent do
local inst = ImGui.TableGetInstanceData(tbl, n)
ImGui.BulletText("Instance %d: HoveredRow: %d, LastOuterHeight: %.2f", n, inst.HoveredRowLast, inst.LastOuterHeight)
end
local sum_weights = 0.0
for n = 0, tbl.ColumnsCount - 1 do
if bit32.band(tbl.Columns[n].Flags, 8 --[[ImGuiTableColumnFlags.WidthStretch]]) ~= 0 then sum_weights = sum_weights + tbl.Columns[n].StretchWeight end
end
for n = 0, tbl.ColumnsCount - 1 do
local c = tbl.Columns[n]
local CF = ImGuiTableColumnFlags
local buf = string.format(
"Column %d order %d '%s': offset %+.2f to %+.2f%s\nEnabled: %d, VisibleX/Y: %d/%d, RequestOutput: %d, SkipItems: %d, DrawChannels: %d,%d\n" ..
"WidthGiven: %.1f, Request/Auto: %.1f/%.1f, StretchWeight: %.3f (%.1f%%)\nMinX: %.1f, MaxX: %.1f (%+.1f), ClipRect: %.1f to %.1f (+%.1f)\n" ..
"ContentWidth: %.1f,%.1f, HeadersUsed/Ideal %.1f/%.1f\nSort: %d%s, UserData: 0x%08X, Flags: 0x%04X: %s%s%s..",
n, c.DisplayOrder, ImGui.TableGetColumnName(tbl, n) or "", c.MinX - tbl.WorkRect.Min.x, c.MaxX - tbl.WorkRect.Min.x, (n < tbl.FreezeColumnsRequest) and " (Frozen)" or "",
B(c.IsEnabled), B(c.IsVisibleX), B(c.IsVisibleY), B(c.IsRequestOutput), B(c.IsSkipItems), c.DrawChannelFrozen, c.DrawChannelUnfrozen,
c.WidthGiven, c.WidthRequest, c.WidthAuto, c.StretchWeight, (c.StretchWeight > 0.0 and sum_weights > 0) and (c.StretchWeight / sum_weights) * 100.0 or 0.0,
c.MinX, c.MaxX, c.MaxX - c.MinX, c.ClipRect.Min.x, c.ClipRect.Max.x, c.ClipRect.Max.x - c.ClipRect.Min.x,
c.ContentMaxXFrozen - c.WorkMinX, c.ContentMaxXUnfrozen - c.WorkMinX, c.ContentMaxXHeadersUsed - c.WorkMinX, c.ContentMaxXHeadersIdeal - c.WorkMinX,
c.SortOrder, (c.SortDirection == 1 --[[ImGuiSortDirection.Ascending]]) and " (Asc)" or ((c.SortDirection == 2 --[[ImGuiSortDirection.Descending]]) and " (Des)" or ""), tonumber(c.UserData) or 0, c.Flags,
(bit32.band(c.Flags, CF.WidthStretch) ~= 0) and "WidthStretch " or "", (bit32.band(c.Flags, CF.WidthFixed) ~= 0) and "WidthFixed " or "", (bit32.band(c.Flags, CF.NoResize) ~= 0) and "NoResize " or "")
ImGui.Bullet()
ImGui.Selectable(buf)
if ImGui.IsItemHovered() then YellowRect(tbl.OuterWindow, ImVec2(c.MinX, tbl.OuterRect.Min.y), ImVec2(c.MaxX, tbl.OuterRect.Max.y)) end
end
local settings = ImGui.TableGetBoundSettings(tbl)
if settings then ImGui.DebugNodeTableSettings(settings, tbl) end
if clear_settings then tbl.IsResetAllRequest = true end -- Queue a call to TableResetSettings()
ImGui.TreePop()
end
local function DebugNodeDockNodeFlags(flags, label, enabled)
local D = ImGuiDockNodeFlags
ImGui.PushID(label)
ImGui.PushStyleVar(11 --[[ImGuiStyleVar.FramePadding]], ImVec2(0.0, 0.0))
ImGui.Text("%s:", label)
if not enabled then ImGui.BeginDisabled() end
for _, n in ipairs({ "NoResize", "NoResizeX", "NoResizeY", "NoTabBar", "HiddenTabBar", "NoWindowMenuButton", "NoCloseButton", "DockedWindowsInFocusRoute",
"NoDocking", "NoDockingSplit", "NoDockingSplitOther", "NoDockingOverMe", "NoDockingOverOther", "NoDockingOverEmpty", "NoUndocking" }) do
if D[n] then _, flags = ImGui.CheckboxFlags(n, flags, D[n]) end
end
if not enabled then ImGui.EndDisabled() end
ImGui.PopStyleVar()
ImGui.PopID()
return flags
end
-- [DEBUG] Display contents of ImDockNode
function ImGui.DebugNodeDockNode(node, label)
local g = ImGui.GetCurrentContext()
local is_alive = (g.FrameCount - node.LastFrameAlive < 2) -- Submitted with ImGuiDockNodeFlags.KeepAliveOnly
local is_active = (g.FrameCount - node.LastFrameActive < 2) -- Submitted
if not is_alive then ImGui.PushStyleColor(0 --[[ImGuiCol.Text]], g.Style.Colors[1 --[[ImGuiCol.TextDisabled]]]) end
local tree_node_flags = node.IsFocused and 1 --[[ImGuiTreeNodeFlags.Selected]] or 0 --[[ImGuiTreeNodeFlags.None]]
local open
if node.Windows.Size > 0 then
open = ImGui.TreeNodeEx(node.ID, tree_node_flags, "%s 0x%04X%s: %d windows (vis: '%s')", label, node.ID, node.IsVisible and "" or " (hidden)", node.Windows.Size, node.VisibleWindow and node.VisibleWindow.Name or "NULL")
else
local kind = (node.SplitAxis == 1 --[[ImGuiAxis.X]]) and "horizontal split" or ((node.SplitAxis == 2 --[[ImGuiAxis.Y]]) and "vertical split" or "empty")
open = ImGui.TreeNodeEx(node.ID, tree_node_flags, "%s 0x%04X%s: %s (vis: '%s')", label, node.ID, node.IsVisible and "" or " (hidden)", kind, node.VisibleWindow and node.VisibleWindow.Name or "NULL")
end
if not is_alive then ImGui.PopStyleColor() end
if is_active and ImGui.IsItemHovered() then
local window = node.HostWindow or node.VisibleWindow
if window then YellowRect(window, node.Pos, node.Pos + node.Size) end
end
if open then
ImGui.BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f) Ref (%.0f, %.0f)", node.Pos.x, node.Pos.y, node.Size.x, node.Size.y, node.SizeRef.x, node.SizeRef.y)
ImGui.DebugNodeWindow(node.HostWindow, "HostWindow")
ImGui.DebugNodeWindow(node.VisibleWindow, "VisibleWindow")
ImGui.BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node.SelectedTabId, node.LastFocusedNodeId)
ImGui.BulletText("Misc:%s%s%s%s%s%s%s", node:IsDockSpace() and " IsDockSpace" or "", node:IsCentralNode() and " IsCentralNode" or "",
is_alive and " IsAlive" or "", is_active and " IsActive" or "", node.IsFocused and " IsFocused" or "", node.WantLockSizeOnce and " WantLockSizeOnce" or "", node.HasCentralNodeChild and " HasCentralNodeChild" or "")
if ImGui.TreeNode("flags", "Flags Merged: 0x%04X, Local: 0x%04X, InWindows: 0x%04X, Shared: 0x%04X", node.MergedFlags, node.LocalFlags, node.LocalFlagsInWindows, node.SharedFlags) then
if ImGui.BeginTable("flags", 4) then
ImGui.TableNextColumn(); DebugNodeDockNodeFlags(node.MergedFlags, "MergedFlags", false)
ImGui.TableNextColumn(); node.LocalFlags = DebugNodeDockNodeFlags(node.LocalFlags, "LocalFlags", true)
ImGui.TableNextColumn(); DebugNodeDockNodeFlags(node.LocalFlagsInWindows, "LocalFlagsInWindows", false)
ImGui.TableNextColumn(); node.SharedFlags = DebugNodeDockNodeFlags(node.SharedFlags, "SharedFlags", true)
ImGui.EndTable()
end
ImGui.TreePop()
end
if node.ParentNode then ImGui.DebugNodeDockNode(node.ParentNode, "ParentNode") end
if node.ChildNodes[0] then ImGui.DebugNodeDockNode(node.ChildNodes[0], "Child[0]") end
if node.ChildNodes[1] then ImGui.DebugNodeDockNode(node.ChildNodes[1], "Child[1]") end
if node.TabBar then ImGui.DebugNodeTabBar(node.TabBar, "TabBar") end
ImGui.DebugNodeWindowsList(node.Windows, "Windows")
ImGui.TreePop()
end
end
function ImGui.DebugNodeWindow(window, label)
if window == nil then
ImGui.BulletText("%s: NULL", label)
return
end
local g = ImGui.GetCurrentContext()
local is_active = window.WasActive
local tree_node_flags = (window == g.NavWindow) and 1 --[[ImGuiTreeNodeFlags.Selected]] or 0 --[[ImGuiTreeNodeFlags.None]]
if not is_active then ImGui.PushStyleColor(0 --[[ImGuiCol.Text]], g.Style.Colors[1 --[[ImGuiCol.TextDisabled]]]) end
local open = ImGui.TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window.Name, is_active and "" or " *Inactive*")
if not is_active then ImGui.PopStyleColor() end
if ImGui.IsItemHovered() and is_active then YellowRect(window, window.Pos, window.Pos + window.Size) end
if not open then return end
if window.MemoryCompacted then ImGui.TextDisabled("Note: some memory buffers have been compacted/freed.") end
if g.IO.ConfigDebugIsDebuggerPresent and ImGui.DebugBreakButton("**DebugBreak**", "in Begin()") then
g.DebugBreakInWindow = window.ID
end
local W = ImGuiWindowFlags
local flags = window.Flags
local function F(f, s) return (bit32.band(flags, f) ~= 0) and s or "" end
ImGui.DebugNodeDrawList(window, window.Viewport, window.DrawList, "DrawList")
ImGui.BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)", window.Pos.x, window.Pos.y, window.Size.x, window.Size.y, window.ContentSize.x, window.ContentSize.y, window.ContentSizeIdeal.x, window.ContentSizeIdeal.y)
ImGui.BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags, F(W.ChildWindow, "Child "), F(W.Tooltip, "Tooltip "), F(W.Popup, "Popup "), F(W.Modal, "Modal "), F(W.ChildMenu, "ChildMenu "),
F(W.NoSavedSettings, "NoSavedSettings "), F(W.NoMouseInputs, "NoMouseInputs"), F(W.NoNavInputs, "NoNavInputs"), F(W.AlwaysAutoResize, "AlwaysAutoResize"))
if bit32.band(flags, W.ChildWindow) ~= 0 then
local cf = window.ChildFlags or 0
local C = ImGuiChildFlags
ImGui.BulletText("ChildFlags: 0x%08X (%s%s%s%s..)", cf, (bit32.band(cf, C.Borders) ~= 0) and "Borders " or "", (bit32.band(cf, C.ResizeX) ~= 0) and "ResizeX " or "",
(bit32.band(cf, C.ResizeY) ~= 0) and "ResizeY " or "", (bit32.band(cf, C.NavFlattened) ~= 0) and "NavFlattened " or "")
end
ImGui.BulletText("WindowClassId: 0x%08X", window.WindowClass and window.WindowClass.ClassId or 0)
ImGui.BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window.Scroll.x, window.ScrollMax.x, window.Scroll.y, window.ScrollMax.y, window.ScrollbarX and "X" or "", window.ScrollbarY and "Y" or "")
ImGui.BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", B(window.Active), B(window.WasActive), B(window.WriteAccessed), (window.Active or window.WasActive) and window.BeginOrderWithinContext or -1)
ImGui.BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", B(window.Appearing), B(window.Hidden), window.HiddenFramesCanSkipItems, window.HiddenFramesCannotSkipItems, B(window.SkipItems))
for layer = 0, 1 do
local r = window.NavRectRel[layer]
if r.Min.x >= r.Max.x and r.Min.y >= r.Max.y then
ImGui.BulletText("NavLastIds[%d]: 0x%08X", layer, window.NavLastIds[layer])
else
ImGui.BulletText("NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)", layer, window.NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y)
end
ImGui.DebugLocateItemOnHover(window.NavLastIds[layer])
end
ImGui.BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window.DC.NavLayersActiveMask, window.NavLastChildNavWindow and window.NavLastChildNavWindow.Name or "NULL")
ImGui.BulletText("Viewport: %d%s, ViewportId: 0x%08X, ViewportPos: (%.1f,%.1f)", window.Viewport and (window.Viewport.Idx or 0) or -1, window.ViewportOwned and " (Owned)" or "", window.ViewportId or 0, window.ViewportPos.x, window.ViewportPos.y)
ImGui.BulletText("DockId: 0x%04X, DockOrder: %d, Act: %d, Vis: %d", window.DockId or 0, window.DockOrder or -1, B(window.DockIsActive), B(window.DockTabIsVisible))
if window.DockNode or window.DockNodeAsHost then
ImGui.DebugNodeDockNode(window.DockNodeAsHost or window.DockNode, window.DockNodeAsHost and "DockNodeAsHost" or "DockNode")
end
if window.RootWindow ~= window then ImGui.DebugNodeWindow(window.RootWindow, "RootWindow") end
if window.RootWindowDockTree and window.RootWindowDockTree ~= window.RootWindow then ImGui.DebugNodeWindow(window.RootWindowDockTree, "RootWindowDockTree") end
if window.ParentWindow ~= nil then ImGui.DebugNodeWindow(window.ParentWindow, "ParentWindow") end
if window.ParentWindowForFocusRoute ~= nil then ImGui.DebugNodeWindow(window.ParentWindowForFocusRoute, "ParentWindowForFocusRoute") end
if window.DC.ChildWindows.Size > 0 then ImGui.DebugNodeWindowsList(window.DC.ChildWindows, "ChildWindows") end
if window.ColumnsStorage.Size > 0 and ImGui.TreeNode("Columns", "Columns sets (%d)", window.ColumnsStorage.Size) then
for _, columns in window.ColumnsStorage:iter() do ImGui.DebugNodeColumns(columns) end
ImGui.TreePop()
end
ImGui.DebugNodeStorage(window.StateStorage, "Storage")
ImGui.TreePop()
end
function ImGui.DebugNodeWindowSettings(settings)
if settings.WantDelete then ImGui.BeginDisabled() end
ImGui.BulletText("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d", settings.ID, settings.Name or "", settings.Pos.x, settings.Pos.y, settings.Size.x, settings.Size.y, B(settings.Collapsed))
if settings.WantDelete then ImGui.EndDisabled() end
end
function ImGui.DebugNodeWindowsList(windows, label)
if not ImGui.TreeNode(label, "%s (%d)", label, windows.Size) then return end
for i = windows.Size, 1, -1 do -- Iterate front to back
ImGui.PushID(tostring(windows.Data[i]))
ImGui.DebugNodeWindow(windows.Data[i], "Window")
ImGui.PopID()
end
ImGui.TreePop()
end
-- FIXME-OPT: This is technically suboptimal, but it is simpler this way.
function ImGui.DebugNodeWindowsListByBeginStackParent(windows, first, last, parent_in_begin_stack)
for i = first, last do
local window = windows[i]
if window.ParentWindowInBeginStack == parent_in_begin_stack then
local buf = string.format("[%04d] Window", window.BeginOrderWithinContext)
-- ImGui.BulletText("[%04d] Window '%s'", window.BeginOrderWithinContext, window.Name)
ImGui.DebugNodeWindow(window, buf)
ImGui.TreePush(buf)
ImGui.DebugNodeWindowsListByBeginStackParent(windows, i + 1, last, window)
ImGui.TreePop()
end
end
end
function ImGui.DebugNodeViewport(viewport)
local g = ImGui.GetCurrentContext()
ImGui.SetNextItemOpen(true, 2 --[[ImGuiCond.Once]])
local open = ImGui.TreeNode(viewport.ID, "Viewport #%d, ID: 0x%08X, Parent: 0x%08X, Window: \"%s\"", viewport.Idx or 0, viewport.ID, viewport.ParentViewportId or 0, viewport.Window and viewport.Window.Name or "N/A")
if ImGui.IsItemHovered() then g.DebugMetricsConfig.HighlightViewportID = viewport.ID end
if open then
ImGui.BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nFrameBufferScale: (%.2f,%.2f)\nWorkArea Inset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f\nMonitor: %d, DpiScale: %.0f%%",
viewport.Pos.x, viewport.Pos.y, viewport.Size.x, viewport.Size.y, viewport.FramebufferScale and viewport.FramebufferScale.x or 1, viewport.FramebufferScale and viewport.FramebufferScale.y or 1,
viewport.WorkInsetMin.x, viewport.WorkInsetMin.y, viewport.WorkInsetMax.x, viewport.WorkInsetMax.y, viewport.PlatformMonitor or 0, (viewport.DpiScale or 1) * 100.0)
ImGui.BulletText("Flags: 0x%04X", viewport.Flags or 0)
if viewport.DrawDataP then
for _, draw_list in viewport.DrawDataP.CmdLists:iter() do ImGui.DebugNodeDrawList(nil, viewport, draw_list, "DrawList") end
end
ImGui.TreePop()
end
end
--- @param monitor ImGuiPlatformMonitor
--- @param label string
--- @param idx int
function ImGui.DebugNodePlatformMonitor(monitor, label, idx)
ImGui.BulletText("%s %d: DPI %.0f%%\n MainMin (%.0f,%.0f), MainMax (%.0f,%.0f), MainSize (%.0f,%.0f)\n WorkMin (%.0f,%.0f), WorkMax (%.0f,%.0f), WorkSize (%.0f,%.0f)",
label, idx, monitor.DpiScale * 100.0,
monitor.MainPos.x, monitor.MainPos.y, monitor.MainPos.x + monitor.MainSize.x, monitor.MainPos.y + monitor.MainSize.y, monitor.MainSize.x, monitor.MainSize.y,
monitor.WorkPos.x, monitor.WorkPos.y, monitor.WorkPos.x + monitor.WorkSize.x, monitor.WorkPos.y + monitor.WorkSize.y, monitor.WorkSize.x, monitor.WorkSize.y)
end
-- Helper tool to diagnose between text encoding issues and font loading issues. Pass your UTF-8 string and verify that there are correct.
function ImGui.DebugTextEncoding(str)
ImGui.Text("Text: \"%s\"", str)
if not ImGui.BeginTable("##DebugTextEncoding", 4, bit32.bor(1920 --[[ImGuiTableFlags.Borders]], 64 --[[ImGuiTableFlags.RowBg]], 8192 --[[ImGuiTableFlags.SizingFixedFit]], 1 --[[ImGuiTableFlags.Resizable]])) then return end
ImGui.TableSetupColumn("Offset")
ImGui.TableSetupColumn("UTF-8")
ImGui.TableSetupColumn("Glyph")
ImGui.TableSetupColumn("Codepoint")
ImGui.TableHeadersRow()
for pos, c in utf8.codes(str) do
local nxt = utf8.offset(str, 2, pos) or (#str + 1)
ImGui.TableNextColumn()
ImGui.Text("%d", pos - 1)
ImGui.TableNextColumn()
for b = pos, nxt - 1 do
if b > pos then ImGui.SameLine() end
ImGui.Text("0x%02X", string.byte(str, b))
end
ImGui.TableNextColumn()
ImGui.TextUnformatted(string.sub(str, pos, nxt - 1))
ImGui.TableNextColumn()
ImGui.Text("U+%04X", c)
end
ImGui.EndTable()
end
-- Debugging enums
local WRT_Names = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentIdeal", "ContentRegionRect" }
local TRT_Names = { "OuterRect", "InnerRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsWorkRect", "ColumnsClipRect",
"ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" }
local TRT_ColumnsRect = 6
local function GetWindowRect(window, t)
if t == 0 then return window:Rect()
elseif t == 1 then return window.OuterRectClipped
elseif t == 2 then return window.InnerRect
elseif t == 3 then return window.InnerClipRect
elseif t == 4 then return window.WorkRect
elseif t == 5 then local m = window.InnerRect.Min - window.Scroll + window.WindowPadding; return ImRect(m, m + window.ContentSize)
elseif t == 6 then local m = window.InnerRect.Min - window.Scroll + window.WindowPadding; return ImRect(m, m + window.ContentSizeIdeal)
else return window.ContentRegionRect end
end
local function GetTableRect(tbl, t, n)
local inst = ImGui.TableGetInstanceData(tbl, tbl.InstanceCurrent) -- Always using last submitted instance
if t == 0 then return tbl.OuterRect elseif t == 1 then return tbl.InnerRect elseif t == 2 then return tbl.WorkRect
elseif t == 3 then return tbl.HostClipRect elseif t == 4 then return tbl.InnerClipRect elseif t == 5 then return tbl.BgClipRect end
local c = tbl.Columns[n]
if t == 6 then return ImRect(c.MinX, tbl.InnerClipRect.Min.y, c.MaxX, tbl.InnerClipRect.Min.y + inst.LastOuterHeight)
elseif t == 7 then return ImRect(c.WorkMinX, tbl.WorkRect.Min.y, c.WorkMaxX, tbl.WorkRect.Max.y)
elseif t == 8 then return c.ClipRect
elseif t == 9 then return ImRect(c.WorkMinX, tbl.InnerClipRect.Min.y, c.ContentMaxXHeadersUsed, tbl.InnerClipRect.Min.y + inst.LastTopHeadersRowHeight) -- Note: y1/y2 not always accurate
elseif t == 10 then return ImRect(c.WorkMinX, tbl.InnerClipRect.Min.y, c.ContentMaxXHeadersIdeal, tbl.InnerClipRect.Min.y + inst.LastTopHeadersRowHeight)
elseif t == 11 then return ImRect(c.WorkMinX, tbl.InnerClipRect.Min.y, c.ContentMaxXFrozen, tbl.InnerClipRect.Min.y + inst.LastFrozenHeight)
else return ImRect(c.WorkMinX, tbl.InnerClipRect.Min.y + inst.LastFrozenHeight, c.ContentMaxXUnfrozen, tbl.InnerClipRect.Max.y) end
end
local function ForEachTable(g, fn)
for i = 0, g.Tables:GetMapSize() - 1 do
local t = g.Tables:GetByIndex(i)
if t then fn(t) end
end
end
local MetricsEncodingBuf = nil
local MetricsDockRootOnly = true
function ImGui.ShowMetricsWindow(p_open)
local g = ImGui.GetCurrentContext()
local io = g.IO
local cfg = g.DebugMetricsConfig
if cfg.ShowDebugLog then cfg.ShowDebugLog = ImGui.ShowDebugLogWindow(cfg.ShowDebugLog) end
if cfg.ShowIDStackTool then cfg.ShowIDStackTool = ImGui.ShowIDStackToolWindow(cfg.ShowIDStackTool) end
local visible
p_open, visible = ImGui.Begin("Dear ImGui Metrics/Debugger", p_open)
if not visible or ImGui.GetCurrentWindow().BeginCount > 1 then
ImGui.End()
return p_open
end
-- [DEBUG] Clear debug breaks hooks after exactly one cycle.
ImGui.DebugBreakClearData()
-- Basic info
ImGui.Text("Dear ImGui %s (%d)", IMGUI_VERSION, 19298 --[[IMGUI_VERSION_NUM]])
if g.ContextName ~= nil and g.ContextName ~= "" then
ImGui.SameLine()
ImGui.Text("(Context Name: \"%s\")", g.ContextName)
end
ImGui.Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0 / math.max(io.Framerate, 0.0001), io.Framerate)
ImGui.Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, math.floor(io.MetricsRenderIndices / 3))
ImGui.Text("%d visible windows, %d current allocations", io.MetricsRenderWindows, g.DebugAllocInfo.TotalAllocCount - g.DebugAllocInfo.TotalFreeCount)
ImGui.Separator()
if cfg.ShowWindowsRectsType < 0 then cfg.ShowWindowsRectsType = 4 end
if cfg.ShowTablesRectsType < 0 then cfg.ShowTablesRectsType = 2 end
-- Tools
if ImGui.TreeNode("Tools") then
-- Debug Break features
-- The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
ImGui.SeparatorTextEx(0, "Debug breaks", nil, ImGui.CalcTextSize("(?)").x + g.Style.SeparatorTextPadding.x)
ImGui.SameLine()
MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash.")
local picker_toggled
picker_toggled, g.DebugItemPickerActive = ImGui.Checkbox("Show Item Picker", g.DebugItemPickerActive)
if picker_toggled and g.DebugItemPickerActive then
ImGui.DebugStartItemPicker()
end
_, io.ConfigDebugIsDebuggerPresent = ImGui.Checkbox("Show \"Debug Break\" buttons in other sections (io.ConfigDebugIsDebuggerPresent)", io.ConfigDebugIsDebuggerPresent)
ImGui.SeparatorText("Visualize")
_, cfg.ShowDebugLog = ImGui.Checkbox("Show Debug Log", cfg.ShowDebugLog == true)
ImGui.SameLine(); MetricsHelpMarker("You can also call ImGui::ShowDebugLogWindow() from your code.")
_, cfg.ShowIDStackTool = ImGui.Checkbox("Show ID Stack Tool", cfg.ShowIDStackTool == true)
ImGui.SameLine(); MetricsHelpMarker("You can also call ImGui::ShowIDStackToolWindow() from your code.")
_, cfg.ShowWindowsBeginOrder = ImGui.Checkbox("Show windows begin order", cfg.ShowWindowsBeginOrder == true)
_, cfg.ShowWindowsRects = ImGui.Checkbox("Show windows rectangles", cfg.ShowWindowsRects == true)
ImGui.SameLine()
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 12)
local changed
cfg.ShowWindowsRectsType, changed = ImGui.Combo("##show_windows_rect_type", cfg.ShowWindowsRectsType, WRT_Names, #WRT_Names, #WRT_Names)
cfg.ShowWindowsRects = cfg.ShowWindowsRects or changed
if cfg.ShowWindowsRects and g.NavWindow ~= nil then
ImGui.BulletText("'%s':", g.NavWindow.Name)
ImGui.Indent()
for rect_n = 0, #WRT_Names - 1 do
local r = GetWindowRect(g.NavWindow, rect_n)
ImGui.Text("(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r:GetWidth(), r:GetHeight(), WRT_Names[rect_n + 1])
end
ImGui.Unindent()
end
_, cfg.ShowTablesRects = ImGui.Checkbox("Show tables rectangles", cfg.ShowTablesRects == true)
ImGui.SameLine()
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 12)
cfg.ShowTablesRectsType, changed = ImGui.Combo("##show_table_rects_type", cfg.ShowTablesRectsType, TRT_Names, #TRT_Names, #TRT_Names)
cfg.ShowTablesRects = cfg.ShowTablesRects or changed
_, g.DebugShowGroupRects = ImGui.Checkbox("Show groups rectangles", g.DebugShowGroupRects == true) -- Storing in context as this is used by group code and prefers to be in hot-data
ImGui.SeparatorText("Validate")
_, io.ConfigDebugBeginReturnValueLoop = ImGui.Checkbox("Debug Begin/BeginChild return value", io.ConfigDebugBeginReturnValueLoop)
ImGui.SameLine(); MetricsHelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running.")
_, cfg.ShowTextEncodingViewer = ImGui.Checkbox("UTF-8 Encoding viewer", cfg.ShowTextEncodingViewer == true)
ImGui.SameLine(); MetricsHelpMarker("You can also call ImGui::DebugTextEncoding() from your code with a given string to test that your UTF-8 encoding settings are correct.")
if cfg.ShowTextEncodingViewer then
MetricsEncodingBuf = MetricsEncodingBuf or DemoBuf("", 64)
ImGui.SetNextItemWidth(-1.1754943508223e-38 --[[FLT_MIN]])
ImGui.InputText("##DebugTextEncodingBuf", MetricsEncodingBuf, 64)
local str = DemoBufStr(MetricsEncodingBuf)
if str ~= "" then ImGui.DebugTextEncoding(str) end
end
ImGui.TreePop()
end
-- Windows
if ImGui.TreeNode("Windows", "Windows (%d)", g.Windows.Size) then
-- ImGui.SetNextItemOpen(true, ImGuiCond.Once)
ImGui.DebugNodeWindowsList(g.Windows, "By display order")
ImGui.DebugNodeWindowsList(g.WindowsFocusOrder, "By focus order (root windows)")
if ImGui.TreeNode("By submission order (begin stack)") then
-- Here we display windows in their submitted order/hierarchy, however note that the Begin stack doesn't constitute a Parent<>Child relationship!
local tmp = {}
for _, window in g.Windows:iter() do
if window.LastFrameActive + 1 >= g.FrameCount then tmp[#tmp + 1] = window end
end
table.sort(tmp, function(a, b) return a.BeginOrderWithinContext < b.BeginOrderWithinContext end)
ImGui.DebugNodeWindowsListByBeginStackParent(tmp, 1, #tmp, nil)
ImGui.TreePop()
end
ImGui.TreePop()
end
-- DrawLists
local drawlist_count = 0
for _, viewport in g.Viewports:iter() do drawlist_count = drawlist_count + viewport.DrawDataP.CmdLists.Size end
if ImGui.TreeNode("DrawLists", "DrawLists (%d)", drawlist_count) then
_, cfg.ShowDrawCmdMesh = ImGui.Checkbox("Show ImDrawCmd mesh when hovering", cfg.ShowDrawCmdMesh)
_, cfg.ShowDrawCmdBoundingBoxes = ImGui.Checkbox("Show ImDrawCmd bounding boxes when hovering", cfg.ShowDrawCmdBoundingBoxes)
for _, viewport in g.Viewports:iter() do
local has = false
for _, draw_list in viewport.DrawDataP.CmdLists:iter() do
if not has then ImGui.Text("Active DrawLists in Viewport #%d, ID: 0x%08X", viewport.Idx or 0, viewport.ID) end
has = true
ImGui.DebugNodeDrawList(nil, viewport, draw_list, "DrawList")
end
end
ImGui.TreePop()
end
-- Viewports
if ImGui.TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size) then
cfg.HighlightMonitorIdx = -1
local open = ImGui.TreeNode("Monitors", "Monitors (%d)", g.PlatformIO.Monitors.Size)
ImGui.SameLine()
MetricsHelpMarker("Dear ImGui uses monitor data:\n- to query DPI settings on a per monitor basis\n- to position popup/tooltips so they don't straddle monitors.")
if open then
for i = 1, g.PlatformIO.Monitors.Size do
ImGui.DebugNodePlatformMonitor(g.PlatformIO.Monitors.Data[i], "Monitor", i - 1)
if ImGui.IsItemHovered() then
cfg.HighlightMonitorIdx = i - 1 -- 0-based, like Monitors:index_from_ptr()
end
end
if g.FallbackMonitor then ImGui.DebugNodePlatformMonitor(g.FallbackMonitor, "Fallback", 0) end
ImGui.TreePop()
end
ImGui.SetNextItemOpen(true, 2 --[[ImGuiCond.Once]])
if ImGui.TreeNode("Windows Minimap") then
ImGui.RenderViewportsThumbnails()
ImGui.TreePop()
end
cfg.HighlightViewportID = 0
ImGui.BulletText("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport and g.MouseViewport.ID or 0, g.IO.MouseHoveredViewport or 0, g.MouseLastHoveredViewport and g.MouseLastHoveredViewport.ID or 0)
if ImGui.TreeNode("Inferred Z order (front-to-back)") then
local viewports = {}
for n = 1, g.Viewports.Size do viewports[n] = g.Viewports.Data[n] end
if #viewports > 1 then
table.sort(viewports, ViewportComparerByLastFocusedStampCount)
end
for _, viewport in ipairs(viewports) do
ImGui.BulletText("Viewport #%d, ID: 0x%08X, LastFocused = %08d, PlatformFocused = %s, Window: \"%s\"",
viewport.Idx, viewport.ID, viewport.LastFocusedStampCount,
(g.PlatformIO.Platform_GetWindowFocus and viewport.PlatformWindowCreated) and (g.PlatformIO.Platform_GetWindowFocus(viewport) and "1" or "0") or "N/A",
viewport.Window and viewport.Window.Name or "N/A")
if ImGui.IsItemHovered() then
cfg.HighlightViewportID = viewport.ID
end
end
ImGui.TreePop()
end
for _, viewport in g.Viewports:iter() do ImGui.DebugNodeViewport(viewport) end
ImGui.TreePop()
end
-- Details for Fonts
for i, atlas in g.FontAtlases:iter() do
if ImGui.TreeNode(tostring(atlas), "Fonts (%d), Textures (%d)", atlas.Fonts.Size, atlas.TexList.Size) then
ImGui.ShowFontAtlas(atlas)
ImGui.TreePop()
end
end
-- Details for Popups
if ImGui.TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size) then
for _, popup_data in g.OpenPopupStack:iter() do
-- As it's difficult to interact with tree nodes while popups are open, we display everything inline.
local window = popup_data.Window
local W = ImGuiWindowFlags
ImGui.BulletText("PopupID: %08x, Window: '%s' (%s%s), RestoreNavWindow '%s', ParentWindow '%s'", popup_data.PopupId, window and window.Name or "NULL",
(window and bit32.band(window.Flags, W.ChildWindow) ~= 0) and "Child;" or "", (window and bit32.band(window.Flags, W.ChildMenu) ~= 0) and "Menu;" or "",
popup_data.RestoreNavWindow and popup_data.RestoreNavWindow.Name or "NULL", (window and window.ParentWindow) and window.ParentWindow.Name or "NULL")
end
ImGui.TreePop()
end
-- Details for TabBars
if ImGui.TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.AliveCount) then
for n = 0, g.TabBars:GetMapSize() - 1 do
local tab_bar = g.TabBars:GetByIndex(n)
if tab_bar then
ImGui.PushID(n)
ImGui.DebugNodeTabBar(tab_bar, "TabBar")
ImGui.PopID()
end
end
ImGui.TreePop()
end
-- Details for Tables
if ImGui.TreeNode("Tables", "Tables (%d)", g.Tables.AliveCount) then
ForEachTable(g, ImGui.DebugNodeTable)
ImGui.TreePop()
end
-- Details for InputText
if ImGui.TreeNode("InputText") then
ImGui.DebugNodeInputTextState(g.InputTextState)
ImGui.TreePop()
end
-- Details for TypingSelect
if ImGui.TreeNode("TypingSelect", "TypingSelect (%d)", (#g.TypingSelectState.SearchBuffer ~= 0) and 1 or 0) then
ImGui.DebugNodeTypingSelectState(g.TypingSelectState)
ImGui.TreePop()
end
-- Details for MultiSelect
if ImGui.TreeNode("MultiSelect", "MultiSelect (%d)", g.MultiSelectStorage.AliveCount) then
local bs = g.BoxSelectState
ImGui.BulletText("BoxSelect ID=0x%08X, Starting = %d, Active %d", bs.ID, B(bs.IsStarting), B(bs.IsActive))
for n = 0, g.MultiSelectStorage:GetMapSize() - 1 do
local state = g.MultiSelectStorage:GetByIndex(n)
if state then
ImGui.DebugNodeMultiSelectState(state)
end
end
ImGui.TreePop()
end
-- Details for Docking
if ImGui.TreeNode("Docking") then
local dc = g.DockContext
_, MetricsDockRootOnly = ImGui.Checkbox("List root nodes", MetricsDockRootOnly)
_, cfg.ShowDockingNodes = ImGui.Checkbox("Ctrl shows window dock info", cfg.ShowDockingNodes == true)
if ImGui.SmallButton("Clear nodes") then ImGui.DockContextClearNodes(g, 0, true) end
ImGui.SameLine()
if ImGui.SmallButton("Rebuild all") then dc.WantFullRebuild = true end
local ids = {}
for id in pairs(dc.Nodes) do ids[#ids + 1] = id end
table.sort(ids)
for _, id in ipairs(ids) do
local node = dc.Nodes[id]
if node and (not MetricsDockRootOnly or node:IsRootNode()) then ImGui.DebugNodeDockNode(node, "Node") end
end
ImGui.TreePop()
end
-- Settings
if ImGui.TreeNode("Settings") then
if ImGui.SmallButton("Clear") then ImGui.ClearIniSettings() end
ImGui.SameLine()
if ImGui.SmallButton("Save to memory") then ImGui.SaveIniSettingsToMemory() end
ImGui.SameLine()
if ImGui.SmallButton("Save to disk") then ImGui.SaveIniSettingsToDisk(g.IO.IniFilename) end
ImGui.SameLine()
ImGui.TextUnformatted(io.IniFilename and ("\"" .. io.IniFilename .. "\"") or "<NULL>")
ImGui.Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer)
local platform_io = g.PlatformIO
local highlight_older_than_date = 0
ImGui.Text("SessionDate: %d", platform_io.Platform_SessionDate)
ImGui.BeginDisabled(platform_io.Platform_SessionDate == 0)
_, cfg.SettingsHighlightOldEntries = ImGui.Checkbox("Highlight Entries Older Than", cfg.SettingsHighlightOldEntries)
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 8)
ImGui.SameLine()
cfg.SettingsDiscardMonths = ImGui.SliderInt("Months", cfg.SettingsDiscardMonths, 1, 24)
if cfg.SettingsHighlightOldEntries and cfg.SettingsDiscardMonths > 0 then
local cutoff_date = ImGuiPackedDate(platform_io.Platform_SessionDate)
cutoff_date:SubtractMonths(cfg.SettingsDiscardMonths)
highlight_older_than_date = cutoff_date:Unpack()
ImGui.SameLine()
local cleanup_args = ImGuiSettingsCleanupArgs()
cleanup_args.DiscardOlderThanMonths = cfg.SettingsDiscardMonths
if ImGui.Button("Discard") then
ImGui.CleanupIniSettings(cleanup_args)
end
end
ImGui.EndDisabled()
_, io.ConfigDebugIniSettings = ImGui.Checkbox("io.ConfigDebugIniSettings", io.ConfigDebugIniSettings)
-- [Luau] struct ScopedHighlightOlderThan: push in the loop body, pop after (settings.LastUsedDate is unpacked)
local function HighlightOlderThan(cutoff_date, in_date)
local highlight = cutoff_date ~= 0 and in_date < cutoff_date
if highlight then ImGui.PushStyleColor(0 --[[ImGuiCol.Text]], ImVec4(1.0, 0.4, 0.4, 1.0)) end
return highlight
end
if ImGui.TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size) then
for _, handler in g.SettingsHandlers:iter() do ImGui.BulletText("\"%s\"", handler.TypeName) end
ImGui.TreePop()
end
if ImGui.TreeNode("SettingsWindows", "Settings packed data: Windows: %d entries", g.SettingsWindows.Size) then
for _, settings in g.SettingsWindows:iter() do
local highlight = HighlightOlderThan(highlight_older_than_date, settings.LastUsedDate)
ImGui.DebugNodeWindowSettings(settings)
if highlight then ImGui.PopStyleColor() end
end
ImGui.TreePop()
end
if ImGui.TreeNode("SettingsTables", "Settings packed data: Tables: %d entries", g.SettingsTables.Size) then
for _, settings in g.SettingsTables:iter() do
local highlight = HighlightOlderThan(highlight_older_than_date, settings.LastUsedDate)
ImGui.DebugNodeTableSettings(settings, nil)
if highlight then ImGui.PopStyleColor() end
end
ImGui.TreePop()
end
if ImGui.TreeNode("SettingsDocking", "Settings packed data: Docking") then
local dc = g.DockContext
ImGui.Text("In SettingsWindows:")
for _, settings in g.SettingsWindows:iter() do
if (settings.DockId or 0) ~= 0 then ImGui.BulletText("Window '%s' -> DockId %08X DockOrder=%d", settings.Name or "", settings.DockId, settings.DockOrder or -1) end
end
ImGui.Text("In SettingsNodes:")
for _, settings in dc.NodesSettings:iter() do
local selected_tab_name = nil
if (settings.SelectedTabId or 0) ~= 0 then
local window = ImGui.FindWindowByID(settings.SelectedTabId)
selected_tab_name = window and window.Name or nil
end
ImGui.BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings.ID, settings.ParentNodeId or 0, settings.SelectedTabId or 0, selected_tab_name or ((settings.SelectedTabId or 0) ~= 0 and "N/A" or ""))
end
ImGui.TreePop()
end
local ini = g.SettingsIniData or ""
if ImGui.TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", #ini) then
ImGui.InputTextMultiline("##Ini", DemoBuf(ini, #ini + 1), #ini + 1, ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], ImGui.GetTextLineHeight() * 20), 512 --[[ImGuiInputTextFlags.ReadOnly]])
ImGui.TreePop()
end
ImGui.TreePop()
end
-- Inputs
if ImGui.TreeNode("Inputs") then
ImGui.Text("KEYBOARD/GAMEPAD/MOUSE KEYS")
-- User code should never have to go through such hoops! You can generally iterate between ImGuiKey.NamedKey_BEGIN and ImGuiKey.NamedKey_END.
ImGui.Indent()
ImGui.Text("Keys down:")
for key = 512 --[[ImGuiKey.NamedKey_BEGIN]], 667 --[[ImGuiKey.NamedKey_END]] - 1 do
if ImGui.IsKeyDown(key) then ImGui.SameLine(); ImGui.Text("\"%s\"", ImGui.GetKeyName(key)) end
end
ImGui.Text("Keys pressed:")
for key = 512 --[[ImGuiKey.NamedKey_BEGIN]], 667 --[[ImGuiKey.NamedKey_END]] - 1 do
if ImGui.IsKeyPressed(key) then ImGui.SameLine(); ImGui.Text("\"%s\"", ImGui.GetKeyName(key)) end
end
ImGui.Text("Keys released:")
for key = 512 --[[ImGuiKey.NamedKey_BEGIN]], 667 --[[ImGuiKey.NamedKey_END]] - 1 do
if ImGui.IsKeyReleased(key) then ImGui.SameLine(); ImGui.Text("\"%s\"", ImGui.GetKeyName(key)) end
end
ImGui.Text("Keys mods: %s%s%s%s", io.KeyCtrl and "Ctrl " or "", io.KeyShift and "Shift " or "", io.KeyAlt and "Alt " or "", io.KeySuper and "Super " or "")
ImGui.Text("Chars queue:")
for i = 1, io.InputQueueCharacters.Size do
local c = io.InputQueueCharacters.Data[i]
ImGui.SameLine(); ImGui.Text("'%s' (0x%04X)", (c > 32 and c <= 255) and string.char(c) or "?", c) -- FIXME: We should convert 'c' to UTF-8 here but the functions are not public.
end
ImGui.DebugRenderKeyboardPreview(ImGui.GetWindowDrawList())
ImGui.Unindent()
ImGui.Text("MOUSE STATE")
ImGui.Indent()
if ImGui.IsMousePosValid() then ImGui.Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y) else ImGui.Text("Mouse pos: <INVALID>") end
ImGui.Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y)
ImGui.Text("Mouse down:")
for i = 0, 2 do if ImGui.IsMouseDown(i) then ImGui.SameLine(); ImGui.Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]) end end
ImGui.Text("Mouse clicked:")
for i = 0, 2 do if ImGui.IsMouseClicked(i) then ImGui.SameLine(); ImGui.Text("b%d (%d)", i, io.MouseClickedCount[i]) end end
ImGui.Text("Mouse released:")
for i = 0, 2 do if ImGui.IsMouseReleased(i) then ImGui.SameLine(); ImGui.Text("b%d", i) end end
ImGui.Text("Mouse wheel: %.1f", io.MouseWheel)
ImGui.Text("MouseStationaryTimer: %.2f", g.MouseStationaryTimer)
ImGui.Text("Mouse source: %s", GetMouseSourceName(io.MouseSource))
ImGui.Text("Pen Pressure: %.1f", io.PenPressure) -- Note: currently unused
ImGui.Unindent()
ImGui.Text("MOUSE WHEELING")
ImGui.Indent()
ImGui.Text("WheelingWindow: '%s'", g.WheelingWindow and g.WheelingWindow.Name or "NULL")
ImGui.Text("WheelingWindowReleaseTimer: %.2f", g.WheelingWindowReleaseTimer or 0)
ImGui.Text("WheelingAxisAvg[] = { %.3f, %.3f }", g.WheelingAxisAvg.x, g.WheelingAxisAvg.y)
ImGui.Unindent()
ImGui.TreePop()
end
-- Internal state
if ImGui.TreeNode("Internal state") then
local function N(w) return w and w.Name or "NULL" end
ImGui.Text("WINDOWING")
ImGui.Indent()
ImGui.Text("HoveredWindow: '%s'", N(g.HoveredWindow))
ImGui.Text("HoveredWindow->Root: '%s'", g.HoveredWindow and N(g.HoveredWindow.RootWindowDockTree) or "NULL")
ImGui.Text("HoveredWindowUnderMovingWindow: '%s'", N(g.HoveredWindowUnderMovingWindow))
ImGui.Text("HoveredDockNode: 0x%08X", g.DebugHoveredDockNode and g.DebugHoveredDockNode.ID or 0)
ImGui.Text("MovingWindow: '%s'", N(g.MovingWindow))
ImGui.Unindent()
ImGui.Text("ITEMS")
ImGui.Indent()
ImGui.Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, B(g.ActiveIdAllowOverlap), GetInputSourceName(g.ActiveIdSource))
ImGui.DebugLocateItemOnHover(g.ActiveId)
ImGui.Text("ActiveIdWindow: '%s'", N(g.ActiveIdWindow))
ImGui.Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, B(g.HoveredIdAllowOverlap)) -- Not displaying g.HoveredId as it is update mid-frame
ImGui.Text("HoverItemDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverItemDelayId or 0, g.HoverItemDelayTimer or 0, g.HoverItemDelayClearTimer or 0)
ImGui.Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\"", B(g.DragDropActive), g.DragDropPayload.SourceId or 0, tostring(g.DragDropPayload.DataType or ""))
ImGui.DebugLocateItemOnHover(g.DragDropPayload.SourceId or 0)
ImGui.Unindent()
ImGui.Text("NAV,FOCUS")
ImGui.Indent()
ImGui.Text("NavWindow: '%s'", N(g.NavWindow))
ImGui.Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer)
ImGui.DebugLocateItemOnHover(g.NavId)
ImGui.Text("NavInputSource: %s", GetInputSourceName(g.NavInputSource or 0 --[[ImGuiInputSource.None]]))
ImGui.Text("NavLastValidSelectionUserData = %s", tostring(g.NavLastValidSelectionUserData)) -- [Port] any Lua value: tostring() instead of %lld (0x%llX)
ImGui.Text("NavActive: %d, NavVisible: %d", B(io.NavActive), B(io.NavVisible))
ImGui.Text("NavActivateId/DownId/PressedId: %08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId or 0, g.NavActivatePressedId or 0)
ImGui.Text("NavActivateFlags: %04X", g.NavActivateFlags or 0)
ImGui.Text("NavCursorVisible: %d, NavHighlightItemUnderNav: %d", B(g.NavCursorVisible), B(g.NavHighlightItemUnderNav))
ImGui.Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId or 0)
ImGui.Text("NavWindowingTarget: '%s'", N(g.NavWindowingTarget))
ImGui.Unindent()
ImGui.TreePop()
end
-- Overlay: Display windows Rectangles and Begin Order
if cfg.ShowWindowsRects or cfg.ShowWindowsBeginOrder then
for _, window in g.Windows:iter() do
if window.WasActive then
local draw_list = FG(window)
if cfg.ShowWindowsRects then
local r = GetWindowRect(window, cfg.ShowWindowsRectsType)
draw_list:AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255))
end
if cfg.ShowWindowsBeginOrder and bit32.band(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) == 0 then
local font_size = ImGui.GetFontSize()
draw_list:AddRectFilled(window.Pos, window.Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255))
draw_list:AddText(window.Pos, IM_COL32(255, 255, 255, 255), tostring(window.BeginOrderWithinContext))
end
end
end
end
-- Overlay: Display Tables Rectangles
if cfg.ShowTablesRects then
ForEachTable(g, function(tbl)
if tbl.LastFrameActive < g.FrameCount - 1 then return end
local draw_list = FG(tbl.OuterWindow)
if cfg.ShowTablesRectsType >= TRT_ColumnsRect then
for column_n = 0, tbl.ColumnsCount - 1 do
local r = GetTableRect(tbl, cfg.ShowTablesRectsType, column_n)
local hot = (tbl.HoveredColumnBody == column_n)
draw_list:AddRect(r.Min, r.Max, hot and IM_COL32(255, 255, 128, 255) or IM_COL32(255, 0, 128, 255), 0.0, hot and 3.0 or 1.0)
end
else
local r = GetTableRect(tbl, cfg.ShowTablesRectsType, -1)
draw_list:AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255))
end
end)
end
ImGui.End()
return p_open
end
function ImGui.DebugBreakClearData()
-- Those fields are scattered in their respective subsystem to stay in hot-data locations
local g = GImGui
g.DebugBreakInWindow = 0
g.DebugBreakInTable = 0
g.DebugBreakInShortcutRouting = 0 --[[ImGuiKey.None]]
end
--- @param keyboard_only bool
--- @param description_of_location string
function ImGui.DebugBreakButtonTooltip(keyboard_only, description_of_location)
if not ImGui.BeginItemTooltip() then
return
end
ImGui.Text("To call IM_DEBUG_BREAK() %s:", description_of_location)
ImGui.Separator()
ImGui.TextUnformatted(keyboard_only and "- Press 'Pause/Break' on keyboard." or "- Press 'Pause/Break' on keyboard.\n- or Click (may alter focus/active id).\n- or navigate using keyboard and press space.")
ImGui.Separator()
ImGui.TextUnformatted("Choose one way that doesn't interfere with what you are trying to debug!\nYou need a debugger attached or this will crash!") -- [Luau] IM_DEBUG_BREAK() calls g.DebugBreakCallback (if set) instead
ImGui.EndTooltip()
end
-- Special button that doesn't take focus, doesn't take input owner, and can be activated without a click etc.
-- In order to reduce interferences with the contents we are trying to debug into.
--- @param label string
--- @param description_of_location string
--- @return bool
function ImGui.DebugBreakButton(label, description_of_location)
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
local g = GImGui
local id = window:GetID(label)
local label_size = ImGui.CalcTextSize(label, nil, true)
local pos = window.DC.CursorPos + ImVec2(0.0, window.DC.CurrLineTextBaseOffset)
local size = ImVec2(label_size.x + g.Style.FramePadding.x * 2.0, label_size.y)
local bb = ImRect(pos, pos + size)
ImGui.ItemSize(size, 0.0)
if not ImGui.ItemAdd(bb, id) then
return false
end
-- WE DO NOT USE ButtonEx() or ButtonBehavior() in order to reduce our side-effects.
local hovered = ImGui.ItemHoverable(bb, id, g.CurrentItemFlags)
local pressed = hovered and (ImGui.IsKeyChordPressed(g.DebugBreakKeyChord) or ImGui.IsMouseClicked(0) or g.NavActivateId == id)
ImGui.DebugBreakButtonTooltip(false, description_of_location)
local src = ImGui.GetStyleColorVec4(hovered and 23 --[[ImGuiCol.ButtonHovered]] or 22 --[[ImGuiCol.Button]])
local col4f = ImVec4(src.x, src.y, src.z, src.w) -- [Luau] copy
local h, s, v = ImGui.ColorConvertRGBtoHSV(col4f.x, col4f.y, col4f.z)
col4f.x, col4f.y, col4f.z = ImGui.ColorConvertHSVtoRGB(h + 0.20, s, v)
ImGui.RenderNavCursor(bb, id)
ImGui.RenderFrame(bb.Min, bb.Max, ImGui.GetColorU32(col4f), true, g.Style.FrameRounding)
ImGui.RenderTextClipped(bb.Min, bb.Max, label, nil, label_size, g.Style.ButtonTextAlign, bb)
return pressed
end
end --[[ [SECTION] METRICS/DEBUGGER WINDOW, DEBUG LOG, ID STACK TOOL, FONT DEBUG ]]
return true -- [Roblox] ModuleScripts must return exactly one value
end
__FILES['imgui_demo.lua'] = function()
-- dear imgui demo (Luau port of imgui_demo.cpp). Optional: the library never calls into this file.
-- dear imgui, v1.93.0 WIP
-- (demo code)
-- Help:
-- - Read FAQ at http://dearimgui.com/faq
-- - Call and read ImGui.ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
-- - Need help integrating Dear ImGui in your codebase?
-- - Read Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started
-- - Read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase.
-- Read top of imgui.cpp and imgui.h for many details, documentation, comments, links.
-- Get the latest version at https://github.com/ocornut/imgui
-- How to easily locate code?
-- - Use Tools->Item Picker to debug break in code by clicking any widgets: https://github.com/ocornut/imgui/wiki/Debug-Tools
-- - Browse pthom's online imgui_explorer: web version the demo w/ source code browser: https://pthom.github.io/imgui_explorer
-- - Find a visible string and search for it in the code!
-----------------------------------------------------
-- PLEASE DO NOT REMOVE THIS FILE FROM YOUR PROJECT!
-----------------------------------------------------
-- Message to the person tempted to delete this file when integrating Dear ImGui into their codebase:
-- Think again! It is the most useful reference code that you and other coders will want to refer to and call.
-- Have the ImGui.ShowDemoWindow() function wired in an always-available debug menu of your game/app!
-- Also include Metrics! ItemPicker! DebugLog! and other debug features.
-- Removing this file from your project is hindering access to documentation for everyone in your team,
-- likely leading you to poorer usage of the library.
-- Everything in this file will be stripped out by the linker if you don't call ImGui.ShowDemoWindow().
-- If you want to link core Dear ImGui in your shipped builds but want a thorough guarantee that the demo will not be
-- linked, you can setup your imconfig.h with #define IMGUI_DISABLE_DEMO_WINDOWS and those functions will be empty.
-- [Port] There is no linker here: build the bundle with `tools/bundle.py --no-demo` to leave this file out.
-- In another situation, whenever you have Dear ImGui available you probably want this to be available for reference.
-- Thank you,
-- -Your beloved friend, imgui_demo.cpp (which you won't delete)
----------------------------------------------
-- ABOUT THE MEANING OF THE 'static' KEYWORD:
----------------------------------------------
-- In this demo code, we frequently use 'static' variables inside functions.
-- A static variable persists across calls. It is essentially a global variable but declared inside the scope of the function.
-- Think of "static int n = 0;" as "global int n = 0;" !
-- We do this IN THE DEMO because we want:
-- - to gather code and data in the same place.
-- - to make the demo source code faster to read, faster to change, smaller in size.
-- - it is also a convenient way of storing simple UI related information as long as your function
-- doesn't need to be reentrant or used in multiple threads.
-- This might be a pattern you will want to use in your code, but most of the data you would be working
-- with in a complex codebase is likely going to be stored outside your functions.
-- [Port] Lua has no function-level 'static': they are fields of the file-level table S (S.FunctionName.x).
-------------------------------------------
-- ABOUT THE CODING STYLE OF OUR DEMO CODE
-------------------------------------------
-- The Demo code in this file is designed to be easy to copy-and-paste into your application!
-- Because of this:
-- - We never omit the ImGui. prefix when calling functions, even though most code here is in the same namespace.
-- - We try to declare static variables in the local scope, as close as possible to the code using them.
-- - We never use any of the helpers/facilities used internally by Dear ImGui, unless available in the public API.
-- - We never use maths operators on ImVec2/ImVec4. For our other sources files we use them, and they are provided
-- by imgui.h using the IMGUI_DEFINE_MATH_OPERATORS define. For your own sources file they are optional
-- and require you either enable those, either provide your own via IM_VEC2_CLASS_EXTRA in imconfig.h.
-- Because we can't assume anything about your support of maths operators, we cannot use them in imgui_demo.cpp.
-- Navigating this file:
-- - In Visual Studio: Ctrl+Comma ("Edit.GoToAll") can follow symbols inside comments, whereas Ctrl+F12 ("Edit.GoToImplementation") cannot.
-- - In Visual Studio w/ Visual Assist installed: Alt+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
-- - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments.
-- - You can search/grep for all sections listed in the index to find the section.
--[[
Index of this file:
// [SECTION] Forward Declarations
// [SECTION] Helpers
// [SECTION] Demo Window / ShowDemoWindow()
// [SECTION] DemoWindowMenuBar()
// [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor & Multi-Select demos)
// [SECTION] Helpers: ExampleImageViewer
// [SECTION] DemoWindowWidgetsBasic()
// [SECTION] DemoWindowWidgetsBullets()
// [SECTION] DemoWindowWidgetsCollapsingHeaders()
// [SECTION] DemoWindowWidgetsComboBoxes()
// [SECTION] DemoWindowWidgetsColorAndPickers()
// [SECTION] DemoWindowWidgetsDataTypes()
// [SECTION] DemoWindowWidgetsDisableBlocks()
// [SECTION] DemoWindowWidgetsDragAndDrop()
// [SECTION] DemoWindowWidgetsDragsAndSliders()
// [SECTION] DemoWindowWidgetsFonts()
// [SECTION] DemoWindowWidgetsImages()
// [SECTION] DemoWindowWidgetsListBoxes()
// [SECTION] DemoWindowWidgetsLiveEdit()
// [SECTION] DemoWindowWidgetsMixedValues()
// [SECTION] DemoWindowWidgetsMultiComponents()
// [SECTION] DemoWindowWidgetsPlotting()
// [SECTION] DemoWindowWidgetsProgressBars()
// [SECTION] DemoWindowWidgetsQueryingStatuses()
// [SECTION] DemoWindowWidgetsSelectables()
// [SECTION] DemoWindowWidgetsSelectionAndMultiSelect()
// [SECTION] DemoWindowWidgetsTabs()
// [SECTION] DemoWindowWidgetsText()
// [SECTION] DemoWindowWidgetsTextFilter()
// [SECTION] DemoWindowWidgetsTextInput()
// [SECTION] DemoWindowWidgetsTooltips()
// [SECTION] DemoWindowWidgetsTreeNodes()
// [SECTION] DemoWindowWidgetsVerticalSliders()
// [SECTION] DemoWindowWidgets()
// [SECTION] DemoWindowLayout()
// [SECTION] DemoWindowPopups()
// [SECTION] DemoWindowTables()
// [SECTION] DemoWindowInputs()
// [SECTION] About Window / ShowAboutWindow()
// [SECTION] Style Editor / ShowStyleEditor()
// [SECTION] User Guide / ShowUserGuide()
// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()
// [SECTION] Example App: Debug Console / ShowExampleAppConsole()
// [SECTION] Example App: Image Viewer / ShowExampleAppImageViewer()
// [SECTION] Example App: Debug Log / ShowExampleAppLog()
// [SECTION] Example App: Simple Layout / ShowExampleAppLayout()
// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()
// [SECTION] Example App: Long Text / ShowExampleAppLongText()
// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize()
// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize()
// [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay()
// [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen()
// [SECTION] Example App: Manipulating window titles / ShowExampleAppWindowTitles()
// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering()
// [SECTION] Example App: Docking, DockSpace / ShowExampleAppDockSpace()
// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments()
// [SECTION] Example App: Assets Browser / ShowExampleAppAssetsBrowser()
]]
-- [Port] C includes, compiler warning pragmas, snprintf/PRId64/IMGUI_CDECL defines dropped (C only).
-- Play it nice with Windows users (Update: May 2018, Notepad now supports Unix-style carriage returns!)
-- [Port] IM_NEWLINE is always "\n" here.
-- Helpers macros
-- We normally try to not use many helpers in imgui_demo.cpp in order to make code easier to copy and paste,
-- but making an exception here as those are largely simplifying code...
-- In other imgui sources we can use nicer internal functions from imgui_internal.h (ImMin/ImMax) but not in the demo.
-- [Port] IM_MIN/IM_MAX/IM_CLAMP -> the library globals ImMin/ImMax/ImClamp.
-------------------------------------------------------------------------------
-- [SECTION] Forward Declarations
-------------------------------------------------------------------------------
-- Forward Declarations
-- [Port] Not needed in Lua (globals are resolved at call time). Upstream list kept for reference:
-- struct ImGuiDemoWindowData;
-- static void ShowExampleAppMainMenuBar();
-- static void ShowExampleAppAssetsBrowser(bool* p_open);
-- static void ShowExampleAppConsole(bool* p_open);
-- static void ShowExampleAppCustomRendering(bool* p_open);
-- static void ShowExampleAppDockSpace(bool* p_open);
-- static void ShowExampleAppDocuments(bool* p_open);
-- static void ShowExampleAppImageViewer(bool* p_open);
-- static void ShowExampleAppLog(bool* p_open);
-- static void ShowExampleAppLayout(bool* p_open);
-- static void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data);
-- static void ShowExampleAppSimpleOverlay(bool* p_open);
-- static void ShowExampleAppAutoResize(bool* p_open);
-- static void ShowExampleAppConstrainedResize(bool* p_open);
-- static void ShowExampleAppFullscreen(bool* p_open);
-- static void ShowExampleAppLongText(bool* p_open);
-- static void ShowExampleAppWindowTitles(bool* p_open);
-- static void ShowExampleMenuFile();
-- We split the contents of the big ShowDemoWindow() function into smaller functions
-- (because the link time of very large functions tends to grow non-linearly)
-- static void DemoWindowMenuBar(ImGuiDemoWindowData* demo_data);
-- static void DemoWindowWidgets(ImGuiDemoWindowData* demo_data);
-- static void DemoWindowLayout();
-- static void DemoWindowPopups();
-- static void DemoWindowTables();
-- static void DemoWindowColumns();
-- static void DemoWindowInputs();
-- Helper tree functions used by Property Editor & Multi-Select demos
-- struct ExampleTreeNode;
-- static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, ExampleTreeNode* parent);
-- static void ExampleTree_DestroyNode(ExampleTreeNode* node);
-------------------------------------------------------------------------------
-- [SECTION] Helpers
-------------------------------------------------------------------------------
-- [Port] Storage for C++ function-level `static` variables: one sub-table per upstream function (S.FunctionName),
-- declared right above the function. Shared by every part of this file.
local S = {}
local _ -- [Port] discard target for unused return values (`_, v = ImGui.Checkbox(...)`)
-- Helper to display a little (?) mark which shows a tooltip when hovered.
-- In your own code you may want to display an actual icon if you are using a merged icon fonts (see docs/FONTS.md)
function HelpMarker(desc)
ImGui.TextDisabled("(?)")
if ImGui.BeginItemTooltip() then
ImGui.PushTextWrapPos(ImGui.GetFontSize() * 35.0)
ImGui.TextUnformatted(desc)
ImGui.PopTextWrapPos()
ImGui.EndTooltip()
end
end
function ShowDockingDisabledMessage()
local io = ImGui.GetIO()
ImGui.Text("ERROR: Docking is not enabled! See Demo > Configuration.")
ImGui.Text("Set io.ConfigFlags |= ImGuiConfigFlags_DockingEnable in your code, or ")
ImGui.SameLine(0.0, 0.0)
if ImGui.SmallButton("click here") then
io.ConfigFlags = bit32.bor(io.ConfigFlags, 128 --[[ImGuiConfigFlags.DockingEnable]])
end
end
-- 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) 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
-- [Port] ImGui.ShowFontAtlas(atlas) and ImGui.TreeNodeSetOpen(storage_id, is_open) are already public here.
-------------------------------------------------------------------------------
-- [SECTION] Demo Window / ShowDemoWindow()
-------------------------------------------------------------------------------
-- Data to be shared across different functions of the demo.
function ImGuiDemoWindowData()
return {
-- Examples Apps (accessible from the "Examples" menu)
ShowMainMenuBar = false,
ShowAppAssetsBrowser = false,
ShowAppConsole = false,
ShowAppCustomRendering = false,
ShowAppDocuments = false,
ShowAppDockSpace = false,
ShowAppImageViewer = false,
ShowAppLog = false,
ShowAppLayout = false,
ShowAppPropertyEditor = false,
ShowAppSimpleOverlay = false,
ShowAppAutoResize = false,
ShowAppConstrainedResize = false,
ShowAppFullscreen = false,
ShowAppLongText = false,
ShowAppWindowTitles = false,
-- Dear ImGui Tools (accessible from the "Tools" menu)
ShowMetrics = false,
ShowDebugLog = false,
ShowIDStackTool = false,
ShowStyleEditor = false,
ShowAbout = false,
-- Other data
DisableSections = false,
LiveEditOverride = false,
LiveEditFlags = 128 --[[ImGuiItemFlags.LiveEditOnInputText]],
DemoTree = nil,
-- [Port] no destructor: DemoTree is garbage collected (upstream calls ExampleTree_DestroyNode(DemoTree)).
}
end
S.ShowDemoWindow = {
-- Stored data
demo_data = ImGuiDemoWindowData(),
-- Demonstrate the various window flags. Typically you would just use the default!
no_titlebar = false,
no_scrollbar = false,
no_menu = false,
no_move = false,
no_resize = false,
no_collapse = false,
no_close = false,
no_nav = false,
no_background = false,
no_bring_to_front = false,
no_docking = false,
unsaved_document = false,
}
-- Demonstrate most Dear ImGui features (this is big function!)
-- You may execute this function to experiment with the UI and understand what it does.
-- You may then search for keywords in the code when you are interested by a specific feature.
-- [Port] p_open: true/false/nil (nil = no close button). Returns the new p_open value.
function ImGui.ShowDemoWindow(p_open)
local s = S.ShowDemoWindow
-- Exceptionally add an extra assert here for people confused about initial Dear ImGui setup
-- Most functions would normally just assert/crash if the context is missing.
assert(ImGui.GetCurrentContext() ~= nil, "Missing Dear ImGui context. Refer to examples app!")
-- Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues.
-- [Port] IMGUI_CHECKVERSION() dropped: no ABI to verify.
-- Stored data
local demo_data = s.demo_data
-- Examples Apps (accessible from the "Examples" menu)
if demo_data.ShowMainMenuBar then ShowExampleAppMainMenuBar() end
if demo_data.ShowAppDockSpace then demo_data.ShowAppDockSpace = ShowExampleAppDockSpace(demo_data.ShowAppDockSpace) end -- Important: Process the Docking app first, as explicit DockSpace() nodes needs to be submitted early (read comments near the DockSpace function)
if demo_data.ShowAppDocuments then demo_data.ShowAppDocuments = ShowExampleAppDocuments(demo_data.ShowAppDocuments) end -- ...process the Document app next, as it may also use a DockSpace()
if demo_data.ShowAppAssetsBrowser then demo_data.ShowAppAssetsBrowser = ShowExampleAppAssetsBrowser(demo_data.ShowAppAssetsBrowser) end
if demo_data.ShowAppConsole then demo_data.ShowAppConsole = ShowExampleAppConsole(demo_data.ShowAppConsole) end
if demo_data.ShowAppCustomRendering then demo_data.ShowAppCustomRendering = ShowExampleAppCustomRendering(demo_data.ShowAppCustomRendering) end
if demo_data.ShowAppImageViewer then demo_data.ShowAppImageViewer = ShowExampleAppImageViewer(demo_data.ShowAppImageViewer) end
if demo_data.ShowAppLog then demo_data.ShowAppLog = ShowExampleAppLog(demo_data.ShowAppLog) end
if demo_data.ShowAppLayout then demo_data.ShowAppLayout = ShowExampleAppLayout(demo_data.ShowAppLayout) end
if demo_data.ShowAppPropertyEditor then demo_data.ShowAppPropertyEditor = ShowExampleAppPropertyEditor(demo_data.ShowAppPropertyEditor, demo_data) end
if demo_data.ShowAppSimpleOverlay then demo_data.ShowAppSimpleOverlay = ShowExampleAppSimpleOverlay(demo_data.ShowAppSimpleOverlay) end
if demo_data.ShowAppAutoResize then demo_data.ShowAppAutoResize = ShowExampleAppAutoResize(demo_data.ShowAppAutoResize) end
if demo_data.ShowAppConstrainedResize then demo_data.ShowAppConstrainedResize = ShowExampleAppConstrainedResize(demo_data.ShowAppConstrainedResize) end
if demo_data.ShowAppFullscreen then demo_data.ShowAppFullscreen = ShowExampleAppFullscreen(demo_data.ShowAppFullscreen) end
if demo_data.ShowAppLongText then demo_data.ShowAppLongText = ShowExampleAppLongText(demo_data.ShowAppLongText) end
if demo_data.ShowAppWindowTitles then demo_data.ShowAppWindowTitles = ShowExampleAppWindowTitles(demo_data.ShowAppWindowTitles) end
-- Dear ImGui Tools (accessible from the "Tools" menu)
if demo_data.ShowMetrics then demo_data.ShowMetrics = ImGui.ShowMetricsWindow(demo_data.ShowMetrics) end
if demo_data.ShowDebugLog then demo_data.ShowDebugLog = ImGui.ShowDebugLogWindow(demo_data.ShowDebugLog) end
if demo_data.ShowIDStackTool then demo_data.ShowIDStackTool = ImGui.ShowIDStackToolWindow(demo_data.ShowIDStackTool) end
if demo_data.ShowAbout then demo_data.ShowAbout = ImGui.ShowAboutWindow(demo_data.ShowAbout) end
if demo_data.ShowStyleEditor then
demo_data.ShowStyleEditor = ImGui.Begin("Dear ImGui Style Editor", demo_data.ShowStyleEditor)
ImGui.ShowStyleEditor()
ImGui.End()
end
-- Demonstrate the various window flags. Typically you would just use the default!
local window_flags = 0
if s.no_titlebar then window_flags = bit32.bor(window_flags, 1 --[[ImGuiWindowFlags.NoTitleBar]]) end
if s.no_scrollbar then window_flags = bit32.bor(window_flags, 8 --[[ImGuiWindowFlags.NoScrollbar]]) end
if not s.no_menu then window_flags = bit32.bor(window_flags, 1024 --[[ImGuiWindowFlags.MenuBar]]) end
if s.no_move then window_flags = bit32.bor(window_flags, 4 --[[ImGuiWindowFlags.NoMove]]) end
if s.no_resize then window_flags = bit32.bor(window_flags, 2 --[[ImGuiWindowFlags.NoResize]]) end
if s.no_collapse then window_flags = bit32.bor(window_flags, 32 --[[ImGuiWindowFlags.NoCollapse]]) end
if s.no_nav then window_flags = bit32.bor(window_flags, 196608 --[[ImGuiWindowFlags.NoNav]]) end
if s.no_background then window_flags = bit32.bor(window_flags, 128 --[[ImGuiWindowFlags.NoBackground]]) end
if s.no_bring_to_front then window_flags = bit32.bor(window_flags, 8192 --[[ImGuiWindowFlags.NoBringToFrontOnFocus]]) end
if s.no_docking then window_flags = bit32.bor(window_flags, 524288 --[[ImGuiWindowFlags.NoDocking]]) end
if s.unsaved_document then window_flags = bit32.bor(window_flags, 262144 --[[ImGuiWindowFlags.UnsavedDocument]]) end
local begin_p_open = p_open -- [Port] keep the caller's value intact when not passed to Begin()
if s.no_close then begin_p_open = nil end -- Don't pass our bool* to Begin
-- We specify a default position/size in case there's no data in the .ini file.
-- We only do it to make the demo applications a little more welcoming, but typically this isn't required.
local main_viewport = ImGui.GetMainViewport()
ImGui.SetNextWindowPos(ImVec2(main_viewport.WorkPos.x + 650, main_viewport.WorkPos.y + 20), 4 --[[ImGuiCond.FirstUseEver]])
ImGui.SetNextWindowSize(ImVec2(550, 680), 4 --[[ImGuiCond.FirstUseEver]])
-- Main body of the Demo window starts here.
local open, visible = ImGui.Begin("Dear ImGui Demo", begin_p_open, window_flags)
if begin_p_open ~= nil then p_open = open end -- [Port] write back the bool* output
if not visible then
-- Early out if the window is collapsed, as an optimization.
ImGui.End()
return p_open
end
-- Most framed widgets share a common width settings. Remaining width is used for the label.
-- The width of the frame may be changed with PushItemWidth() or SetNextItemWidth().
-- - Positive value for absolute size, negative value for right-alignment.
-- - The default value is about GetWindowWidth() * 0.65f.
-- - See 'Demo->Layout->Widgets Width' for details.
-- Here we change the frame width based on how much width we want to give to the label.
local label_width_base = ImGui.GetFontSize() * 12 -- Some amount of width for label, based on font size.
local label_width_max = ImGui.GetContentRegionAvail().x * 0.40 -- ...but always leave some room for framed widgets.
local label_width = ImMin(label_width_base, label_width_max)
ImGui.PushItemWidth(-label_width) -- Right-align: framed items will leave 'label_width' available for the label.
--ImGui.PushItemWidth(ImGui.GetContentRegionAvail().x * 0.40) -- e.g. Use 40% width for framed widgets, leaving 60% width for labels.
--ImGui.PushItemWidth(-ImGui.GetContentRegionAvail().x * 0.40) -- e.g. Use 40% width for labels, leaving 60% width for framed widgets.
--ImGui.PushItemWidth(ImGui.GetFontSize() * -12) -- e.g. Use XXX width for labels, leaving the rest for framed widgets.
-- Menu Bar
DemoWindowMenuBar(demo_data)
ImGui.Text("dear imgui says hello! (%s) (%d)", IMGUI_VERSION, 19298 --[[IMGUI_VERSION_NUM]])
ImGui.Spacing()
if ImGui.CollapsingHeader("Help") then
IMGUI_DEMO_MARKER("Help")
ImGui.SeparatorText("ABOUT THIS DEMO:")
ImGui.BulletText("Sections below are demonstrating many aspects of the library.")
ImGui.BulletText("The \"Examples\" menu above leads to more demo contents.")
ImGui.BulletText("The \"Tools\" menu above gives access to: About Box, Style Editor,\n" ..
"and Metrics/Debugger (general purpose Dear ImGui debugging tool).")
ImGui.BulletText("Web demo (w/ source code browser): ")
ImGui.SameLine(0, 0)
ImGui.TextLinkOpenURL("https://pthom.github.io/imgui_explorer")
ImGui.SeparatorText("PROGRAMMER GUIDE:")
ImGui.BulletText("See the ShowDemoWindow() code in imgui_demo.cpp. <- you are here!")
ImGui.BulletText("See comments in imgui.cpp.")
ImGui.BulletText("See example applications in the examples/ folder.")
ImGui.BulletText("Read the FAQ at ")
ImGui.SameLine(0, 0)
ImGui.TextLinkOpenURL("https://www.dearimgui.com/faq/")
ImGui.BulletText("Set 'io.ConfigFlags |= NavEnableKeyboard' for keyboard controls.")
ImGui.BulletText("Set 'io.ConfigFlags |= NavEnableGamepad' for gamepad controls.")
ImGui.SeparatorText("USER GUIDE:")
ImGui.ShowUserGuide()
end
if ImGui.CollapsingHeader("Configuration") then
local io = ImGui.GetIO()
if ImGui.TreeNode("Configuration##2") then
IMGUI_DEMO_MARKER("Configuration")
ImGui.SeparatorText("General")
_, io.ConfigFlags = ImGui.CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", io.ConfigFlags, 1 --[[ImGuiConfigFlags.NavEnableKeyboard]])
ImGui.SameLine(); HelpMarker("Enable keyboard controls.")
_, io.ConfigFlags = ImGui.CheckboxFlags("io.ConfigFlags: NavEnableGamepad", io.ConfigFlags, 2 --[[ImGuiConfigFlags.NavEnableGamepad]])
ImGui.SameLine(); HelpMarker("Enable gamepad controls. Require backend to set io.BackendFlags |= ImGuiBackendFlags_HasGamepad.\n\nRead instructions in imgui.cpp for details.")
_, io.ConfigFlags = ImGui.CheckboxFlags("io.ConfigFlags: NoMouse", io.ConfigFlags, 16 --[[ImGuiConfigFlags.NoMouse]])
ImGui.SameLine(); HelpMarker("Instruct dear imgui to disable mouse inputs and interactions.")
-- The "NoMouse" option can get us stuck with a disabled mouse! Let's provide an alternative way to fix it:
if bit32.band(io.ConfigFlags, 16 --[[ImGuiConfigFlags.NoMouse]]) ~= 0 then
if math.fmod(ImGui.GetTime(), 0.40) < 0.20 then
ImGui.SameLine()
ImGui.Text("<<PRESS SPACE TO DISABLE>>")
end
-- Prevent both being checked
if ImGui.IsKeyPressed(524 --[[ImGuiKey.Space]]) or bit32.band(io.ConfigFlags, 64 --[[ImGuiConfigFlags.NoKeyboard]]) ~= 0 then
io.ConfigFlags = bit32.band(io.ConfigFlags, bit32.bnot(16 --[[ImGuiConfigFlags.NoMouse]]))
end
end
_, io.ConfigFlags = ImGui.CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", io.ConfigFlags, 32 --[[ImGuiConfigFlags.NoMouseCursorChange]])
ImGui.SameLine(); HelpMarker("Instruct backend to not alter mouse cursor shape and visibility.")
_, io.ConfigFlags = ImGui.CheckboxFlags("io.ConfigFlags: NoKeyboard", io.ConfigFlags, 64 --[[ImGuiConfigFlags.NoKeyboard]])
ImGui.SameLine(); HelpMarker("Instruct dear imgui to disable keyboard inputs and interactions.")
_, io.ConfigInputTrickleEventQueue = ImGui.Checkbox("io.ConfigInputTrickleEventQueue", io.ConfigInputTrickleEventQueue)
ImGui.SameLine(); HelpMarker("Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates.")
_, io.MouseDrawCursor = ImGui.Checkbox("io.MouseDrawCursor", io.MouseDrawCursor)
ImGui.SameLine(); HelpMarker("Instruct Dear ImGui to render a mouse cursor itself. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something).")
ImGui.SeparatorText("Keyboard/Gamepad Navigation")
_, io.ConfigNavSwapGamepadButtons = ImGui.Checkbox("io.ConfigNavSwapGamepadButtons", io.ConfigNavSwapGamepadButtons)
_, io.ConfigNavMoveSetMousePos = ImGui.Checkbox("io.ConfigNavMoveSetMousePos", io.ConfigNavMoveSetMousePos)
ImGui.SameLine(); HelpMarker("Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult")
_, io.ConfigNavCaptureKeyboard = ImGui.Checkbox("io.ConfigNavCaptureKeyboard", io.ConfigNavCaptureKeyboard)
_, io.ConfigNavEscapeClearFocusItem = ImGui.Checkbox("io.ConfigNavEscapeClearFocusItem", io.ConfigNavEscapeClearFocusItem)
ImGui.SameLine(); HelpMarker("Pressing Escape clears focused item.")
_, io.ConfigNavEscapeClearFocusWindow = ImGui.Checkbox("io.ConfigNavEscapeClearFocusWindow", io.ConfigNavEscapeClearFocusWindow)
ImGui.SameLine(); HelpMarker("Pressing Escape clears focused window.")
_, io.ConfigNavCursorVisibleAuto = ImGui.Checkbox("io.ConfigNavCursorVisibleAuto", io.ConfigNavCursorVisibleAuto)
ImGui.SameLine(); HelpMarker("Using directional navigation key makes the cursor visible. Mouse click hides the cursor.")
_, io.ConfigNavCursorVisibleAlways = ImGui.Checkbox("io.ConfigNavCursorVisibleAlways", io.ConfigNavCursorVisibleAlways)
ImGui.SameLine(); HelpMarker("Navigation cursor is always visible.")
ImGui.SeparatorText("Docking")
_, io.ConfigFlags = ImGui.CheckboxFlags("io.ConfigFlags: DockingEnable", io.ConfigFlags, 128 --[[ImGuiConfigFlags.DockingEnable]])
ImGui.SameLine()
if io.ConfigDockingWithShift then
HelpMarker("Drag from window title bar or their tab to dock/undock. Hold SHIFT to enable docking.\n\nDrag from window menu button (upper-left button) to undock an entire node (all windows).")
else
HelpMarker("Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking.\n\nDrag from window menu button (upper-left button) to undock an entire node (all windows).")
end
if bit32.band(io.ConfigFlags, 128 --[[ImGuiConfigFlags.DockingEnable]]) ~= 0 then
ImGui.Indent()
_, io.ConfigDockingNoSplit = ImGui.Checkbox("io.ConfigDockingNoSplit", io.ConfigDockingNoSplit)
ImGui.SameLine(); HelpMarker("Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars.")
_, io.ConfigDockingNoDockingOver = ImGui.Checkbox("io.ConfigDockingNoDockingOver", io.ConfigDockingNoDockingOver)
ImGui.SameLine(); HelpMarker("Simplified docking mode: disable window merging into a same tab-bar, so docking is limited to splitting windows.")
_, io.ConfigDockingWithShift = ImGui.Checkbox("io.ConfigDockingWithShift", io.ConfigDockingWithShift)
ImGui.SameLine(); HelpMarker("Enable docking when holding Shift only (allow to drop in wider space, reduce visual noise)")
_, io.ConfigDockingAlwaysTabBar = ImGui.Checkbox("io.ConfigDockingAlwaysTabBar", io.ConfigDockingAlwaysTabBar)
ImGui.SameLine(); HelpMarker("Create a docking node and tab-bar on single floating windows.")
_, io.ConfigDockingTransparentPayload = ImGui.Checkbox("io.ConfigDockingTransparentPayload", io.ConfigDockingTransparentPayload)
ImGui.SameLine(); HelpMarker("Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport cannot be synced. Best used with ConfigViewportsNoAutoMerge.")
ImGui.Unindent()
end
ImGui.SeparatorText("Multi-viewports")
_, io.ConfigFlags = ImGui.CheckboxFlags("io.ConfigFlags: ViewportsEnable", io.ConfigFlags, 1024 --[[ImGuiConfigFlags.ViewportsEnable]])
ImGui.SameLine(); HelpMarker("[beta] Enable beta multi-viewports support. See ImGuiPlatformIO for details.")
if bit32.band(io.ConfigFlags, 1024 --[[ImGuiConfigFlags.ViewportsEnable]]) ~= 0 then
ImGui.Indent()
_, io.ConfigViewportsNoAutoMerge = ImGui.Checkbox("io.ConfigViewportsNoAutoMerge", io.ConfigViewportsNoAutoMerge)
ImGui.SameLine(); HelpMarker("Set to make all floating imgui windows always create their own viewport. Otherwise, they are merged into the main host viewports when overlapping it.")
_, io.ConfigViewportsNoTaskBarIcon = ImGui.Checkbox("io.ConfigViewportsNoTaskBarIcon", io.ConfigViewportsNoTaskBarIcon)
ImGui.SameLine(); HelpMarker("(note: some platform backends may not reflect a change of this value for existing viewports, and may need the viewport to be recreated)")
_, io.ConfigViewportsNoDecoration = ImGui.Checkbox("io.ConfigViewportsNoDecoration", io.ConfigViewportsNoDecoration)
ImGui.SameLine(); HelpMarker("(note: some platform backends may not reflect a change of this value for existing viewports, and may need the viewport to be recreated)")
_, io.ConfigViewportsNoDefaultParent = ImGui.Checkbox("io.ConfigViewportsNoDefaultParent", io.ConfigViewportsNoDefaultParent)
ImGui.SameLine(); HelpMarker("(note: some platform backends may not reflect a change of this value for existing viewports, and may need the viewport to be recreated)")
_, io.ConfigViewportsPlatformFocusSetsImGuiFocus = ImGui.Checkbox("io.ConfigViewportsPlatformFocusSetsImGuiFocus", io.ConfigViewportsPlatformFocusSetsImGuiFocus)
ImGui.SameLine(); HelpMarker("When a platform window is focused (e.g. using Alt+Tab, clicking Platform Title Bar), apply corresponding focus on imgui windows (may clear focus/active id from imgui windows location in other platform windows). In principle this is better enabled but we provide an opt-out, because some Linux window managers tend to eagerly focus windows (e.g. on mouse hover, or even a simple window pos/size change).")
ImGui.Unindent()
end
--ImGui.SeparatorText("DPI/Scaling")
--_, io.ConfigDpiScaleFonts = ImGui.Checkbox("io.ConfigDpiScaleFonts", io.ConfigDpiScaleFonts)
--ImGui.SameLine(); HelpMarker("Experimental: Automatically update style.FontScaleDpi when Monitor DPI changes. This will scale fonts but NOT style sizes/padding for now.")
--_, io.ConfigDpiScaleViewports = ImGui.Checkbox("io.ConfigDpiScaleViewports", io.ConfigDpiScaleViewports)
--ImGui.SameLine(); HelpMarker("Experimental: Scale Dear ImGui and Platform Windows when Monitor DPI changes.")
ImGui.SeparatorText("Windows")
_, io.ConfigWindowsResizeFromEdges = ImGui.Checkbox("io.ConfigWindowsResizeFromEdges", io.ConfigWindowsResizeFromEdges)
ImGui.SameLine(); HelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires ImGuiBackendFlags_HasMouseCursors for better mouse cursor feedback.")
_, io.ConfigWindowsMoveFromTitleBarOnly = ImGui.Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", io.ConfigWindowsMoveFromTitleBarOnly)
_, io.ConfigWindowsCopyContentsWithCtrlC = ImGui.Checkbox("io.ConfigWindowsCopyContentsWithCtrlC", io.ConfigWindowsCopyContentsWithCtrlC) -- [EXPERIMENTAL]
ImGui.SameLine(); HelpMarker("*EXPERIMENTAL* Ctrl+C copy the contents of focused window into the clipboard.\n\nExperimental because:\n- (1) has known issues with nested Begin/End pairs.\n- (2) text output quality varies.\n- (3) text output is in submission order rather than spatial order.")
_, io.ConfigScrollbarScrollByPage = ImGui.Checkbox("io.ConfigScrollbarScrollByPage", io.ConfigScrollbarScrollByPage)
ImGui.SameLine(); HelpMarker("Enable scrolling page by page when clicking outside the scrollbar grab.\nWhen disabled, always scroll to clicked location.\nWhen enabled, Shift+Click scrolls to clicked location.")
ImGui.SeparatorText("Widgets")
_, io.ConfigInputTextCursorBlink = ImGui.Checkbox("io.ConfigInputTextCursorBlink", io.ConfigInputTextCursorBlink)
ImGui.SameLine(); HelpMarker("Enable blinking cursor (optional as some users consider it to be distracting).")
_, io.ConfigInputTextEnterKeepActive = ImGui.Checkbox("io.ConfigInputTextEnterKeepActive", io.ConfigInputTextEnterKeepActive)
ImGui.SameLine(); HelpMarker("Pressing Enter will reactivate item and select all text (single-line only).")
_, io.ConfigDragClickToInputText = ImGui.Checkbox("io.ConfigDragClickToInputText", io.ConfigDragClickToInputText)
ImGui.SameLine(); HelpMarker("Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving).")
_, io.ConfigMacOSXBehaviors = ImGui.Checkbox("io.ConfigMacOSXBehaviors", io.ConfigMacOSXBehaviors)
ImGui.SameLine(); HelpMarker("Swap Cmd<>Ctrl keys, enable various MacOS style behaviors.")
ImGui.Text("Also see Style->Rendering for rendering options.")
ImGui.SeparatorText("Settings")
_, io.ConfigIniSettingsSaveLastUsedDate = ImGui.Checkbox("io.ConfigIniSettingsSaveLastUsedDate", io.ConfigIniSettingsSaveLastUsedDate)
-- Also read: https://github.com/ocornut/imgui/wiki/Error-Handling
ImGui.SeparatorText("Error Handling")
_, io.ConfigErrorRecovery = ImGui.Checkbox("io.ConfigErrorRecovery", io.ConfigErrorRecovery)
ImGui.SameLine(); HelpMarker(
"Options to configure how we handle recoverable errors.\n" ..
"- Error recovery is not perfect nor guaranteed! It is a feature to ease development.\n" ..
"- You not are not supposed to rely on it in the course of a normal application run.\n" ..
"- Possible usage: facilitate recovery from errors triggered from a scripting language or after specific exceptions handlers.\n" ..
"- Always ensure that on programmers seat you have at minimum Asserts or Tooltips enabled when making direct imgui API call! " ..
"Otherwise it would severely hinder your ability to catch and correct mistakes!")
_, io.ConfigErrorRecoveryEnableAssert = ImGui.Checkbox("io.ConfigErrorRecoveryEnableAssert", io.ConfigErrorRecoveryEnableAssert)
_, io.ConfigErrorRecoveryEnableDebugLog = ImGui.Checkbox("io.ConfigErrorRecoveryEnableDebugLog", io.ConfigErrorRecoveryEnableDebugLog)
_, io.ConfigErrorRecoveryEnableTooltip = ImGui.Checkbox("io.ConfigErrorRecoveryEnableTooltip", io.ConfigErrorRecoveryEnableTooltip)
if not io.ConfigErrorRecoveryEnableAssert and not io.ConfigErrorRecoveryEnableDebugLog and not io.ConfigErrorRecoveryEnableTooltip then
io.ConfigErrorRecoveryEnableAssert = true; io.ConfigErrorRecoveryEnableDebugLog = true; io.ConfigErrorRecoveryEnableTooltip = true
end
-- Also read: https://github.com/ocornut/imgui/wiki/Debug-Tools
ImGui.SeparatorText("Debug")
_, io.ConfigDebugIsDebuggerPresent = ImGui.Checkbox("io.ConfigDebugIsDebuggerPresent", io.ConfigDebugIsDebuggerPresent)
ImGui.SameLine(); HelpMarker("Enable various tools calling IM_DEBUG_BREAK().\n\nRequires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.")
_, io.ConfigDebugHighlightIdConflicts = ImGui.Checkbox("io.ConfigDebugHighlightIdConflicts", io.ConfigDebugHighlightIdConflicts)
ImGui.SameLine(); HelpMarker("Highlight and show an error message when multiple items have conflicting identifiers.")
ImGui.BeginDisabled()
_, io.ConfigDebugBeginReturnValueOnce = ImGui.Checkbox("io.ConfigDebugBeginReturnValueOnce", io.ConfigDebugBeginReturnValueOnce)
ImGui.EndDisabled()
ImGui.SameLine(); HelpMarker("First calls to Begin()/BeginChild() will return false.\n\nTHIS OPTION IS DISABLED because it needs to be set at application boot-time to make sense. Showing the disabled option is a way to make this feature easier to discover.")
_, io.ConfigDebugBeginReturnValueLoop = ImGui.Checkbox("io.ConfigDebugBeginReturnValueLoop", io.ConfigDebugBeginReturnValueLoop)
ImGui.SameLine(); HelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running.")
_, io.ConfigDebugIgnoreFocusLoss = ImGui.Checkbox("io.ConfigDebugIgnoreFocusLoss", io.ConfigDebugIgnoreFocusLoss)
ImGui.SameLine(); HelpMarker("Option to deactivate io.AddFocusEvent(false) handling. May facilitate interactions with a debugger when focus loss leads to clearing inputs data.")
_, io.ConfigDebugDrawListDefaultsToStrokeLegacy = ImGui.Checkbox("io.ConfigDebugDrawListDefaultsToStrokeLegacy", io.ConfigDebugDrawListDefaultsToStrokeLegacy)
ImGui.SameLine(); HelpMarker("Option to default all ImDrawList to ImDrawFlags_StrokeLegacy mode, mimicking pre-1.93.0 rendering.")
_, io.ConfigDebugIniSettings = ImGui.Checkbox("io.ConfigDebugIniSettings", io.ConfigDebugIniSettings)
ImGui.SameLine(); HelpMarker("Option to save .ini data with extra comments (particularly helpful for Docking, but makes saving slower).")
ImGui.TreePop()
ImGui.Spacing()
end
if ImGui.TreeNode("Backend Flags") then
IMGUI_DEMO_MARKER("Configuration/Backend Flags")
HelpMarker(
"Those flags are set by the backends (imgui_impl_xxx files) to specify their capabilities.\n" ..
"Here we expose them as read-only fields to avoid breaking interactions with your backend.")
-- Make a local copy to avoid modifying actual backend flags.
-- FIXME: Maybe we need a BeginReadonly() equivalent to keep label bright?
ImGui.BeginDisabled()
_, io.BackendFlags = ImGui.CheckboxFlags("io.BackendFlags: HasGamepad", io.BackendFlags, 1 --[[ImGuiBackendFlags.HasGamepad]])
_, io.BackendFlags = ImGui.CheckboxFlags("io.BackendFlags: HasMouseCursors", io.BackendFlags, 2 --[[ImGuiBackendFlags.HasMouseCursors]])
_, io.BackendFlags = ImGui.CheckboxFlags("io.BackendFlags: HasSetMousePos", io.BackendFlags, 4 --[[ImGuiBackendFlags.HasSetMousePos]])
_, io.BackendFlags = ImGui.CheckboxFlags("io.BackendFlags: PlatformHasViewports", io.BackendFlags, 2048 --[[ImGuiBackendFlags.PlatformHasViewports]])
_, io.BackendFlags = ImGui.CheckboxFlags("io.BackendFlags: HasMouseHoveredViewport",io.BackendFlags, 4096 --[[ImGuiBackendFlags.HasMouseHoveredViewport]])
_, io.BackendFlags = ImGui.CheckboxFlags("io.BackendFlags: HasParentViewport", io.BackendFlags, 8192 --[[ImGuiBackendFlags.HasParentViewport]])
_, io.BackendFlags = ImGui.CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", io.BackendFlags, 8 --[[ImGuiBackendFlags.RendererHasVtxOffset]])
_, io.BackendFlags = ImGui.CheckboxFlags("io.BackendFlags: RendererHasTextures", io.BackendFlags, 16 --[[ImGuiBackendFlags.RendererHasTextures]])
_, io.BackendFlags = ImGui.CheckboxFlags("io.BackendFlags: RendererHasViewports", io.BackendFlags, 1024 --[[ImGuiBackendFlags.RendererHasViewports]])
ImGui.EndDisabled()
ImGui.TreePop()
ImGui.Spacing()
end
if ImGui.TreeNode("Style, Fonts") then
IMGUI_DEMO_MARKER("Configuration/Style, Fonts")
_, demo_data.ShowStyleEditor = ImGui.Checkbox("Style Editor", demo_data.ShowStyleEditor)
ImGui.SameLine()
HelpMarker("The same contents can be accessed in 'Tools->Style Editor' or by calling the ShowStyleEditor() function.")
ImGui.TreePop()
ImGui.Spacing()
end
if ImGui.TreeNode("Capture/Logging") then
IMGUI_DEMO_MARKER("Configuration/Capture, Logging")
HelpMarker(
"The logging API redirects all text output so you can easily capture the content of " ..
"a window or a block. Tree nodes can be automatically expanded.\n" ..
"Try opening any of the contents below in this window and then click one of the \"Log To\" button.")
ImGui.LogButtons()
HelpMarker("You can also call ImGui::LogText() to output directly to the log without a visual output.")
if ImGui.Button("Copy \"Hello, world!\" to clipboard") then
ImGui.LogToClipboard()
ImGui.LogText("Hello, world!")
ImGui.LogFinish()
end
ImGui.TreePop()
end
end
if ImGui.CollapsingHeader("Window options") then
IMGUI_DEMO_MARKER("Window options")
if ImGui.BeginTable("split", 3) then
ImGui.TableNextColumn(); _, s.no_titlebar = ImGui.Checkbox("No titlebar", s.no_titlebar)
ImGui.TableNextColumn(); _, s.no_scrollbar = ImGui.Checkbox("No scrollbar", s.no_scrollbar)
ImGui.TableNextColumn(); _, s.no_menu = ImGui.Checkbox("No menu", s.no_menu)
ImGui.TableNextColumn(); _, s.no_move = ImGui.Checkbox("No move", s.no_move)
ImGui.TableNextColumn(); _, s.no_resize = ImGui.Checkbox("No resize", s.no_resize)
ImGui.TableNextColumn(); _, s.no_collapse = ImGui.Checkbox("No collapse", s.no_collapse)
ImGui.TableNextColumn(); _, s.no_close = ImGui.Checkbox("No close", s.no_close)
ImGui.TableNextColumn(); _, s.no_nav = ImGui.Checkbox("No nav", s.no_nav)
ImGui.TableNextColumn(); _, s.no_background = ImGui.Checkbox("No background", s.no_background)
ImGui.TableNextColumn(); _, s.no_bring_to_front = ImGui.Checkbox("No bring to front", s.no_bring_to_front)
ImGui.TableNextColumn(); _, s.no_docking = ImGui.Checkbox("No docking", s.no_docking)
ImGui.TableNextColumn(); _, s.unsaved_document = ImGui.Checkbox("Unsaved document", s.unsaved_document)
ImGui.EndTable()
end
end
-- All demo contents
DemoWindowWidgets(demo_data)
DemoWindowLayout()
DemoWindowPopups()
DemoWindowTables()
DemoWindowInputs()
-- End of ShowDemoWindow()
ImGui.PopItemWidth()
ImGui.End()
return p_open
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowMenuBar()
-------------------------------------------------------------------------------
function DemoWindowMenuBar(demo_data)
if ImGui.BeginMenuBar() then
if ImGui.BeginMenu("Menu") then
IMGUI_DEMO_MARKER("Menu/File")
ShowExampleMenuFile()
ImGui.EndMenu()
end
if ImGui.BeginMenu("Examples") then
IMGUI_DEMO_MARKER("Menu/Examples")
_, demo_data.ShowMainMenuBar = ImGui.MenuItem("Main menu bar", nil, demo_data.ShowMainMenuBar)
ImGui.SeparatorText("Mini apps")
_, demo_data.ShowAppAssetsBrowser = ImGui.MenuItem("Assets Browser", nil, demo_data.ShowAppAssetsBrowser)
_, demo_data.ShowAppConsole = ImGui.MenuItem("Console", nil, demo_data.ShowAppConsole)
_, demo_data.ShowAppCustomRendering = ImGui.MenuItem("Custom rendering", nil, demo_data.ShowAppCustomRendering)
_, demo_data.ShowAppDocuments = ImGui.MenuItem("Documents", nil, demo_data.ShowAppDocuments)
_, demo_data.ShowAppDockSpace = ImGui.MenuItem("Dockspace", nil, demo_data.ShowAppDockSpace)
_, demo_data.ShowAppImageViewer = ImGui.MenuItem("Image Viewer", nil, demo_data.ShowAppImageViewer)
_, demo_data.ShowAppLog = ImGui.MenuItem("Log", nil, demo_data.ShowAppLog)
_, demo_data.ShowAppPropertyEditor = ImGui.MenuItem("Property editor", nil, demo_data.ShowAppPropertyEditor)
_, demo_data.ShowAppLayout = ImGui.MenuItem("Simple layout", nil, demo_data.ShowAppLayout)
_, demo_data.ShowAppSimpleOverlay = ImGui.MenuItem("Simple overlay", nil, demo_data.ShowAppSimpleOverlay)
ImGui.SeparatorText("Concepts")
_, demo_data.ShowAppAutoResize = ImGui.MenuItem("Auto-resizing window", nil, demo_data.ShowAppAutoResize)
_, demo_data.ShowAppConstrainedResize = ImGui.MenuItem("Constrained-resizing window", nil, demo_data.ShowAppConstrainedResize)
_, demo_data.ShowAppFullscreen = ImGui.MenuItem("Fullscreen window", nil, demo_data.ShowAppFullscreen)
_, demo_data.ShowAppLongText = ImGui.MenuItem("Long text display", nil, demo_data.ShowAppLongText)
_, demo_data.ShowAppWindowTitles = ImGui.MenuItem("Manipulating window titles", nil, demo_data.ShowAppWindowTitles)
ImGui.EndMenu()
end
--if ImGui.MenuItem("MenuItem") then end -- You can also use MenuItem() inside a menu bar!
if ImGui.BeginMenu("Tools") then
IMGUI_DEMO_MARKER("Menu/Tools")
local io = ImGui.GetIO()
local has_debug_tools = true -- [Port] IMGUI_DISABLE_DEBUG_TOOLS is never defined here
_, demo_data.ShowMetrics = ImGui.MenuItem("Metrics/Debugger", nil, demo_data.ShowMetrics, has_debug_tools)
if ImGui.BeginMenu("Debug Options") then
ImGui.BeginDisabled(not has_debug_tools)
_, io.ConfigDebugHighlightIdConflicts = ImGui.Checkbox("Highlight ID Conflicts", io.ConfigDebugHighlightIdConflicts)
ImGui.EndDisabled()
_, io.ConfigErrorRecoveryEnableAssert = ImGui.Checkbox("Assert on error recovery", io.ConfigErrorRecoveryEnableAssert)
ImGui.TextDisabled("(see Demo->Configuration for more)")
ImGui.EndMenu()
end
_, 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)
local is_debugger_present = io.ConfigDebugIsDebuggerPresent
if ImGui.MenuItem("Item Picker", nil, false, has_debug_tools) then -- and is_debugger_present
ImGui.DebugStartItemPicker()
end
if not is_debugger_present then
ImGui.SetItemTooltip("Requires io.ConfigDebugIsDebuggerPresent=true to be set.\n\nWe otherwise disable some extra features to avoid casual users crashing the application.")
end
_, demo_data.ShowStyleEditor = ImGui.MenuItem("Style Editor", nil, demo_data.ShowStyleEditor)
_, demo_data.ShowAbout = ImGui.MenuItem("About Dear ImGui", nil, demo_data.ShowAbout)
ImGui.EndMenu()
end
ImGui.EndMenuBar()
end
end
-------------------------------------------------------------------------------
-- [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor & Multi-Select demos)
-------------------------------------------------------------------------------
-- Simple representation for a tree
-- (this is designed to be simple to understand for our demos, not to be fancy or efficient etc.)
function ExampleTreeNode()
return {
-- Tree structure
Name = "", -- [Port] Lua string (upstream char Name[28]), max 27 bytes
UID = 0,
Parent = nil,
Childs = {}, -- [Port] plain 1-based Lua array (upstream ImVector<ExampleTreeNode*>): #Childs, Childs[n + 1]
IndexInParent = 0, -- Maintaining this allows us to implement linear traversal more easily (0-based)
-- Leaf Data
HasData = false, -- All leaves have data
DataMyBool = true,
DataMyInt = 128,
DataMyVec2 = ImVec2(0.0, 3.141592),
}
end
-- Simple representation of struct metadata/serialization data.
-- (this is a minimal version of what a typical advanced application may provide)
function ExampleMemberInfo(name, data_type, data_count, offset)
return {
Name = name, -- Member name
DataType = data_type, -- Member type
DataCount = data_count, -- Member count (1 when scalar)
Offset = offset, -- Offset inside parent structure -- [Port] field key in the node table (no offsetof)
}
end
-- Metadata description of ExampleTreeNode struct.
ExampleTreeNodeMemberInfos =
{
ExampleMemberInfo("MyName", ImGuiDataType.String, 1, "Name"),
ExampleMemberInfo("MyBool", ImGuiDataType.Bool, 1, "DataMyBool"),
ExampleMemberInfo("MyInt", ImGuiDataType.S32, 1, "DataMyInt"),
ExampleMemberInfo("MyVec2", ImGuiDataType.Float, 2, "DataMyVec2"),
}
function ExampleTree_CreateNode(name, uid, parent)
local node = ExampleTreeNode()
node.Name = string.sub(name, 1, 27) -- [Port] snprintf() into char[28]
node.UID = uid
node.Parent = parent
node.IndexInParent = parent and #parent.Childs or 0
if parent then
parent.Childs[#parent.Childs + 1] = node
end
return node
end
function ExampleTree_DestroyNode(node)
for _, child_node in ipairs(node.Childs) do
ExampleTree_DestroyNode(child_node)
end
-- [Port] IM_DELETE(node): nothing to do, garbage collected
end
S.ExampleTree_CreateDemoTree = {
category_names = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pear", "Pineapple", "Strawberry", "Watermelon" },
}
-- Create example tree data
-- (warning: this can allocates MANY MANY more times than other code in all of Dear ImGui + demo combined)
-- (a real application managing one million nodes would likely store its tree data differently)
function ExampleTree_CreateDemoTree()
local s = S.ExampleTree_CreateDemoTree
-- 20 root nodes -> 211 total nodes, ~261 allocs.
-- 1000 root nodes -> ~11K total nodes, ~14K allocs.
-- 10000 root nodes -> ~123K total nodes, ~154K allocs.
-- 100000 root nodes -> ~1338K total nodes, ~1666K allocs.
local ROOT_ITEMS_COUNT = 20
local category_names = s.category_names
local category_count = #category_names
local name_buf -- [Port] Lua string, truncated to sizeof(ExampleTreeNode::Name) by ExampleTree_CreateNode()
local uid = 0
uid = uid + 1
local node_L0 = ExampleTree_CreateNode("<ROOT>", uid, nil)
for idx_L0 = 0, ROOT_ITEMS_COUNT - 1 do
name_buf = string.format("%s %d", category_names[math.floor(idx_L0 / math.floor(ROOT_ITEMS_COUNT / category_count)) + 1], idx_L0 % math.floor(ROOT_ITEMS_COUNT / category_count))
uid = uid + 1
local node_L1 = ExampleTree_CreateNode(name_buf, uid, node_L0)
local number_of_childs = #node_L1.Name
for idx_L1 = 0, number_of_childs - 1 do
name_buf = string.format("Child %d", idx_L1)
uid = uid + 1
local node_L2 = ExampleTree_CreateNode(name_buf, uid, node_L1)
node_L2.HasData = true
if idx_L1 == 0 then
name_buf = string.format("Sub-child %d", 0)
uid = uid + 1
local node_L3 = ExampleTree_CreateNode(name_buf, uid, node_L2)
node_L3.HasData = true
end
end
end
return node_L0
end
-------------------------------------------------------------------------------
-- [SECTION] Helpers: ExampleImageViewer
-------------------------------------------------------------------------------
function ExampleImageViewerData()
return {
ImageBgColor = IM_COL32(100, 100, 100, 255),
GridColor = IM_COL32(255, 255, 255, 100),
GridEnabled = true,
ViewReset = true,
ViewOffset = ImVec2(0.0, 0.0), -- in image space
Zoom = 10.0,
ZoomMin = 1.0,
ZoomMax = 10000.0,
}
end
function ExampleImageViewer_DrawOptions(data)
ImGui.SetNextItemShortcut(552 --[[ImGuiKey.G]], 262144 --[[ImGuiInputFlags.Tooltip]]) -- | ImGuiInputFlags.RouteGlobal
_, data.GridEnabled = ImGui.Checkbox("Grid", data.GridEnabled)
ImGui.SameLine()
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 10.0)
local zoom_100 = data.Zoom * 100.0
local changed
zoom_100, changed = ImGui.DragFloat("##Zoom", zoom_100, 5.0, data.ZoomMin * 100.0, data.ZoomMax * 100.0, "%.0f%%", 1536 --[[ImGuiSliderFlags.AlwaysClamp]])
if changed then
data.Zoom = zoom_100 / 100.0
end
end
function ExampleImageViewer_DrawCanvas(data, canvas_size, image_tex_ref, image_w, image_h)
local io = ImGui.GetIO()
local platform_io = ImGui.GetPlatformIO()
local draw_list = ImGui.GetWindowDrawList()
assert(canvas_size.x >= 0.0 and canvas_size.y >= 0.0)
-- Layout canvas
ImGui.InvisibleButton("##Canvas", canvas_size)
local canvas_min = ImGui.GetItemRectMin()
local canvas_max = ImGui.GetItemRectMax()
if data.ViewReset then
data.ViewOffset = ImVec2((canvas_size.x * 0.5 / data.Zoom) - 0.5, (canvas_size.y * 0.5 / data.Zoom) - 0.5) -- Add half a pixel padding
end
data.ViewReset = false
-- Handle inputs
if ImGui.SetItemKeyOwner(662 --[[ImGuiKey.MouseWheelY]]) then
if io.MouseWheel ~= 0.0 then
data.Zoom = ImClamp(data.Zoom * (1.0 + io.MouseWheel * 0.10), data.ZoomMin, data.ZoomMax)
end
end
local zoom = data.Zoom -- (float)(int)ViewZoom;
if ImGui.IsItemActive() and ImGui.IsMouseDragging(0) then
data.ViewOffset.x = data.ViewOffset.x - io.MouseDelta.x / zoom
data.ViewOffset.y = data.ViewOffset.y - io.MouseDelta.y / zoom
end
-- Display image
local image_min, image_max = ImVec2(), ImVec2()
image_min.x = ImTrunc((canvas_min.x - (data.ViewOffset.x * zoom)) + (canvas_size.x * 0.5))
image_min.y = ImTrunc((canvas_min.y - (data.ViewOffset.y * zoom)) + (canvas_size.y * 0.5))
image_max.x = ImTrunc(image_min.x + image_w * zoom)
image_max.y = ImTrunc(image_min.y + image_h * zoom)
draw_list:AddRect(ImVec2(canvas_min.x - 1.0, canvas_min.y - 1.0), ImVec2(canvas_max.x + 1.0, canvas_max.y + 1.0), IM_COL32(255, 255, 255, 255))
draw_list:PushClipRect(canvas_min, canvas_max, true)
draw_list:AddRectFilled(image_min, image_max, data.ImageBgColor)
if platform_io.DrawCallback_SetSamplerNearest ~= nil then
draw_list:AddCallback(platform_io.DrawCallback_SetSamplerNearest)
end
draw_list:AddImage(image_tex_ref, image_min, image_max)
if platform_io.DrawCallback_SetSamplerLinear ~= nil then
draw_list:AddCallback(ImGui.GetPlatformIO().DrawCallback_SetSamplerLinear)
end
-- Display grid lines for visible pixels
if data.GridEnabled and zoom > 6.0 then
local step = zoom
for px = ImTrunc((canvas_min.x - image_min.x) / step), ImTrunc((canvas_max.x - image_min.x) / step) do
draw_list:AddLineV(image_min.x + px * step, canvas_min.y, canvas_max.y, data.GridColor, 1.0)
end
for py = ImTrunc((canvas_min.y - image_min.y) / step), ImTrunc((canvas_max.y - image_min.y) / step) do
draw_list:AddLineH(canvas_min.x, canvas_max.x, image_min.y + py * step, data.GridColor, 1.0)
end
end
draw_list:PopClipRect()
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsBasic()
-------------------------------------------------------------------------------
S.DemoWindowWidgetsBasic = {
clicked = 0,
check = true,
e = 0,
counter = 0,
inputs = {
str0 = ImGui.NewTextBuffer("Hello, world!", 128),
str1 = ImGui.NewTextBuffer("", 128),
i0 = 123,
f0 = 0.001,
d0 = 999999.00000001,
f1 = 1.e10,
vec4a = { 0.10, 0.20, 0.30, 0.44 },
},
drags = { i1 = 50, i2 = 42, i3 = 128, f1 = 1.00, f2 = 0.0067 },
sliders = { i1 = 0, f1 = 0.123, f2 = 0.0, angle = 0.0, elem = 0 },
pickers = { col1 = { 1.0, 0.0, 0.2 }, col2 = { 0.4, 0.7, 0.0, 0.5 } },
combo = { item_current = 0 },
listbox = { item_current = 1 },
}
function DemoWindowWidgetsBasic()
local _
if ImGui.TreeNode("Basic") then
local s = S.DemoWindowWidgetsBasic
IMGUI_DEMO_MARKER("Widgets/Basic")
ImGui.SeparatorText("General")
IMGUI_DEMO_MARKER("Widgets/Basic/Button")
if ImGui.Button("Button") then
s.clicked = s.clicked + 1
end
if bit32.band(s.clicked, 1) ~= 0 then
ImGui.SameLine()
ImGui.Text("Thanks for clicking me!")
end
IMGUI_DEMO_MARKER("Widgets/Basic/Checkbox")
_, s.check = ImGui.Checkbox("checkbox", s.check)
IMGUI_DEMO_MARKER("Widgets/Basic/RadioButton")
_, s.e = ImGui.RadioButton("radio a", s.e, 0); ImGui.SameLine()
_, s.e = ImGui.RadioButton("radio b", s.e, 1); ImGui.SameLine()
_, s.e = ImGui.RadioButton("radio c", s.e, 2)
ImGui.AlignTextToFramePadding()
ImGui.TextLinkOpenURL("Hyperlink", "https://github.com/ocornut/imgui/wiki/Error-Handling")
-- Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style.
IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Colored)")
for i = 0, 6 do
if i > 0 then
ImGui.SameLine()
end
ImGui.PushID(i)
ImGui.PushStyleColor(22 --[[ImGuiCol.Button]], ImColor.HSV(i / 7.0, 0.6, 0.6).Value)
ImGui.PushStyleColor(23 --[[ImGuiCol.ButtonHovered]], ImColor.HSV(i / 7.0, 0.7, 0.7).Value)
ImGui.PushStyleColor(24 --[[ImGuiCol.ButtonActive]], ImColor.HSV(i / 7.0, 0.8, 0.8).Value)
ImGui.Button("Click")
ImGui.PopStyleColor(3)
ImGui.PopID()
end
-- Use AlignTextToFramePadding() to align text baseline to the baseline of framed widgets elements
-- (otherwise a Text+SameLine+Button sequence will have the text a little too high by default!)
-- See 'Demo->Layout->Text Baseline Alignment' for details.
ImGui.AlignTextToFramePadding()
ImGui.Text("Hold to repeat:")
ImGui.SameLine()
-- Arrow buttons with Repeater
IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Repeating)")
local spacing = ImGui.GetStyle().ItemInnerSpacing.x
ImGui.PushItemFlag(8 --[[ImGuiItemFlags.ButtonRepeat]], true)
if ImGui.ArrowButton("##left", 0 --[[ImGuiDir.Left]]) then s.counter = s.counter - 1 end
ImGui.SameLine(0.0, spacing)
if ImGui.ArrowButton("##right", 1 --[[ImGuiDir.Right]]) then s.counter = s.counter + 1 end
ImGui.PopItemFlag()
ImGui.SameLine()
ImGui.Text("%d", s.counter)
ImGui.Button("Tooltip")
ImGui.SetItemTooltip("I am a tooltip")
ImGui.LabelText("label", "Value")
ImGui.SeparatorText("Inputs")
do
local s = S.DemoWindowWidgetsBasic.inputs
-- If you want to use InputText() with std::string or any custom dynamic string type:
-- - For std::string: use the wrapper in misc/cpp/imgui_stdlib.h/.cpp
-- - Otherwise, see the 'Dear ImGui Demo->Widgets->Text Input->Resize Callback' for using ImGuiInputTextFlags_CallbackResize.
IMGUI_DEMO_MARKER("Widgets/Basic/InputText")
ImGui.InputText("input text", s.str0, 128)
ImGui.SameLine(); HelpMarker(
"USER:\n" ..
"Hold Shift or use mouse to select text.\n" ..
"Ctrl+Left/Right to word jump.\n" ..
"Ctrl+A or Double-Click to select all.\n" ..
"Ctrl+X,Ctrl+C,Ctrl+V for clipboard.\n" ..
"Ctrl+Z to undo, Ctrl+Y/Ctrl+Shift+Z to redo.\n" ..
"Escape to revert.\n\n" ..
"PROGRAMMER:\n" ..
"You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() " ..
"to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated " ..
"in imgui_demo.cpp).")
ImGui.InputTextWithHint("input text (w/ hint)", "enter text here", s.str1, 128)
IMGUI_DEMO_MARKER("Widgets/Basic/InputInt, InputFloat")
s.i0 = ImGui.InputInt("input int", s.i0)
s.f0 = ImGui.InputFloat("input float", s.f0, 0.01, 1.0, "%.3f")
s.d0 = ImGui.InputDouble("input double", s.d0, 0.01, 1.0, "%.8f")
s.f1 = ImGui.InputFloat("input scientific", s.f1, 0.0, 0.0, "%e")
ImGui.SameLine(); HelpMarker(
"You can input value using the scientific notation,\n" ..
" e.g. \"1e+8\" becomes \"100000000\".")
ImGui.InputFloat3("input float3", s.vec4a)
end
ImGui.SeparatorText("Drags")
do
local s = S.DemoWindowWidgetsBasic.drags
IMGUI_DEMO_MARKER("Widgets/Basic/DragInt, DragFloat")
s.i1 = ImGui.DragInt("drag int", s.i1, 1)
ImGui.SameLine(); HelpMarker(
"Click and drag to edit value.\n" ..
"Hold Shift/Alt for faster/slower edit.\n" ..
"Double-Click or Ctrl+Click to input value.")
s.i2 = ImGui.DragInt("drag int 0..100", s.i2, 1, 0, 100, "%d%%", 1536 --[[ImGuiSliderFlags.AlwaysClamp]])
s.i3 = ImGui.DragInt("drag int wrap 100..200", s.i3, 1, 100, 200, "%d", 256 --[[ImGuiSliderFlags.WrapAround]])
s.f1 = ImGui.DragFloat("drag float", s.f1, 0.005)
s.f2 = ImGui.DragFloat("drag small float", s.f2, 0.0001, 0.0, 0.0, "%.06f ns")
--ImGui.DragFloat("drag wrap -1..1", f3, 0.005, -1.0, 1.0, nil, ImGuiSliderFlags.WrapAround)
end
ImGui.SeparatorText("Sliders")
do
local s = S.DemoWindowWidgetsBasic.sliders
IMGUI_DEMO_MARKER("Widgets/Basic/SliderInt, SliderFloat")
s.i1 = ImGui.SliderInt("slider int", s.i1, -1, 3)
ImGui.SameLine(); HelpMarker("Ctrl+Click to input value.")
s.f1 = ImGui.SliderFloat("slider float", s.f1, 0.0, 1.0, "ratio = %.3f")
s.f2 = ImGui.SliderFloat("slider float (log)", s.f2, -10.0, 10.0, "%.4f", 32 --[[ImGuiSliderFlags.Logarithmic]])
IMGUI_DEMO_MARKER("Widgets/Basic/SliderAngle")
s.angle = ImGui.SliderAngle("slider angle", s.angle)
-- Using the format string to display a name instead of an integer.
-- Here we completely omit '%d' from the format string, so it'll only display a name.
-- This technique can also be used with DragInt().
IMGUI_DEMO_MARKER("Widgets/Basic/Slider (enum)")
local Element_Fire, Element_Earth, Element_Air, Element_Water, Element_COUNT = 0, 1, 2, 3, 4
local elems_names = { "Fire", "Earth", "Air", "Water" }
local elem_name = (s.elem >= 0 and s.elem < Element_COUNT) and elems_names[s.elem + 1] or "Unknown"
s.elem = ImGui.SliderInt("slider enum", s.elem, 0, Element_COUNT - 1, elem_name) -- Use ImGuiSliderFlags_NoInput flag to disable Ctrl+Click here.
ImGui.SameLine(); HelpMarker("Using the format string parameter to display a name instead of the underlying integer.")
end
ImGui.SeparatorText("Selectors/Pickers")
do
local s = S.DemoWindowWidgetsBasic.pickers
IMGUI_DEMO_MARKER("Widgets/Basic/ColorEdit3, ColorEdit4")
ImGui.ColorEdit3("color 1", s.col1)
ImGui.SameLine(); HelpMarker(
"Click on the color square to open a color picker.\n" ..
"Click and hold to use drag and drop.\n" ..
"Right-Click on the color square to show options.\n" ..
"Ctrl+Click on individual component to input value.\n")
ImGui.ColorEdit4("color 2", s.col2)
end
do
local s = S.DemoWindowWidgetsBasic.combo
-- Using the _simplified_ one-liner Combo() api here
-- See "Combo" section for examples of how to use the more flexible BeginCombo()/EndCombo() api.
IMGUI_DEMO_MARKER("Widgets/Basic/Combo")
local items = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIIIIII", "JJJJ", "KKKKKKK" }
s.item_current = ImGui.Combo("combo", s.item_current, items, #items)
ImGui.SameLine(); HelpMarker(
"Using the simplified one-liner Combo API here.\n" ..
"Refer to the \"Combo\" section below for an explanation of how to use the more flexible and general BeginCombo/EndCombo API.")
end
do
local s = S.DemoWindowWidgetsBasic.listbox
-- Using the _simplified_ one-liner ListBox() api here
-- See "List boxes" section for examples of how to use the more flexible BeginListBox()/EndListBox() api.
IMGUI_DEMO_MARKER("Widgets/Basic/ListBox")
local items = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }
s.item_current = ImGui.ListBox("listbox", s.item_current, items, #items, 4)
ImGui.SameLine(); HelpMarker(
"Using the simplified one-liner ListBox API here.\n" ..
"Refer to the \"List boxes\" section below for an explanation of how to use the more flexible and general BeginListBox/EndListBox API.")
end
-- Testing ImGuiOnceUponAFrame helper.
--static ImGuiOnceUponAFrame once;
--for (int i = 0; i < 5; i++)
-- if (once)
-- ImGui.Text("This will be displayed only once.");
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsBullets()
-------------------------------------------------------------------------------
function DemoWindowWidgetsBullets()
if ImGui.TreeNode("Bullets") then
IMGUI_DEMO_MARKER("Widgets/Bullets")
ImGui.BulletText("Bullet point 1")
ImGui.BulletText("Bullet point 2\nOn multiple lines")
if ImGui.TreeNode("Tree node") then
ImGui.BulletText("Another bullet point")
ImGui.TreePop()
end
ImGui.Bullet(); ImGui.Text("Bullet point 3 (two calls)")
ImGui.Bullet(); ImGui.SmallButton("Button")
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsCollapsingHeaders()
-------------------------------------------------------------------------------
S.DemoWindowWidgetsCollapsingHeaders = {
closable_group = true,
}
function DemoWindowWidgetsCollapsingHeaders()
local _
if ImGui.TreeNode("Collapsing Headers") then
local s = S.DemoWindowWidgetsCollapsingHeaders
IMGUI_DEMO_MARKER("Widgets/Collapsing Headers")
_, s.closable_group = ImGui.Checkbox("Show 2nd header", s.closable_group)
if ImGui.CollapsingHeader("Header", 0 --[[ImGuiTreeNodeFlags.None]]) then
ImGui.Text("IsItemHovered: %d", ImGui.IsItemHovered() and 1 or 0)
for i = 0, 4 do
ImGui.Text("Some content %d", i)
end
end
local open
open, s.closable_group = ImGui.CollapsingHeader("Header with a close button", s.closable_group)
if open then
ImGui.Text("IsItemHovered: %d", ImGui.IsItemHovered() and 1 or 0)
for i = 0, 4 do
ImGui.Text("More content %d", i)
end
end
--[[
if ImGui.CollapsingHeader("Header with a bullet", ImGuiTreeNodeFlags.Bullet) then
ImGui.Text("IsItemHovered: %d", ImGui.IsItemHovered())
end
]]
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsColorAndPickers()
-------------------------------------------------------------------------------
S.DemoWindowWidgetsColorAndPickers = {
color = ImVec4(114.0 / 255.0, 144.0 / 255.0, 154.0 / 255.0, 200.0 / 255.0),
base_flags = ImGuiColorEditFlags.None,
saved_palette_init = true,
saved_palette = {}, -- 32 x ImVec4, 1-based
backup_color = ImVec4(),
no_border = false,
ref_color = false,
ref_color_v = ImVec4(1.0, 0.0, 1.0, 0.5),
picker_mode = 0,
display_mode = 0,
color_picker_flags = ImGuiColorEditFlags.AlphaBar,
color_hsv = ImVec4(0.23, 1.0, 1.0, 1.0), -- Stored as HSV!
}
function DemoWindowWidgetsColorAndPickers()
local _
if ImGui.TreeNode("Color/Picker Widgets") then
local s = S.DemoWindowWidgetsColorAndPickers
IMGUI_DEMO_MARKER("Widgets/Color")
local color = s.color
ImGui.SeparatorText("Options")
_, s.base_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags_NoAlpha", s.base_flags, 2 --[[ImGuiColorEditFlags.NoAlpha]])
_, s.base_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags_AlphaOpaque", s.base_flags, 4096 --[[ImGuiColorEditFlags.AlphaOpaque]])
_, s.base_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags_AlphaNoBg", s.base_flags, 8192 --[[ImGuiColorEditFlags.AlphaNoBg]])
_, s.base_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags_AlphaPreviewHalf", s.base_flags, 16384 --[[ImGuiColorEditFlags.AlphaPreviewHalf]])
_, s.base_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags_NoOptions", s.base_flags, 8 --[[ImGuiColorEditFlags.NoOptions]]); ImGui.SameLine(); HelpMarker("Right-click on the individual color widget to show options.")
_, s.base_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags_NoDragDrop", s.base_flags, 512 --[[ImGuiColorEditFlags.NoDragDrop]])
_, s.base_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags_NoColorMarkers", s.base_flags, 2048 --[[ImGuiColorEditFlags.NoColorMarkers]])
_, s.base_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags_HDR", s.base_flags, 524288 --[[ImGuiColorEditFlags.HDR]]); ImGui.SameLine(); HelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets.")
local base_flags = s.base_flags
IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit")
ImGui.SeparatorText("Inline color editor")
ImGui.Text("Color widget:")
ImGui.SameLine(); HelpMarker(
"Click on the color square to open a color picker.\n" ..
"Ctrl+Click on individual component to input value.\n")
ImGui.ColorEdit3("MyColor##1", color, base_flags)
IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (HSV, with Alpha)")
ImGui.Text("Color widget HSV with Alpha:")
ImGui.ColorEdit4("MyColor##2", color, bit32.bor(2097152 --[[ImGuiColorEditFlags.DisplayHSV]], base_flags))
IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (float display)")
ImGui.Text("Color widget with Float Display:")
ImGui.ColorEdit4("MyColor##2f", color, bit32.bor(16777216 --[[ImGuiColorEditFlags.Float]], base_flags))
IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with Picker)")
ImGui.Text("Color button with Picker:")
ImGui.SameLine(); HelpMarker(
"With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\n" ..
"With the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only " ..
"be used for the tooltip and picker popup.")
ImGui.ColorEdit4("MyColor##3", color, bit32.bor(32 --[[ImGuiColorEditFlags.NoInputs]], 128 --[[ImGuiColorEditFlags.NoLabel]], base_flags))
IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with custom Picker popup)")
ImGui.Text("Color button with Custom Picker Popup:")
-- Generate a default palette. The palette will persist and can be edited.
local saved_palette = s.saved_palette
if s.saved_palette_init then
for n = 0, 32 - 1 do
saved_palette[n + 1] = ImVec4()
saved_palette[n + 1].x, saved_palette[n + 1].y, saved_palette[n + 1].z = ImGui.ColorConvertHSVtoRGB(n / 31.0, 0.8, 0.8)
saved_palette[n + 1].w = 1.0 -- Alpha
end
s.saved_palette_init = false
end
local open_popup = ImGui.ColorButton("MyColor##3b", color, base_flags)
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.x)
open_popup = ImGui.Button("Palette") or open_popup
if open_popup then
ImGui.OpenPopup("mypicker")
ImVec4_Copy(s.backup_color, color)
end
if ImGui.BeginPopup("mypicker") then
ImGui.Text("MY CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!")
ImGui.Separator()
ImGui.ColorPicker4("##picker", color, bit32.bor(base_flags, 256 --[[ImGuiColorEditFlags.NoSidePreview]], 16 --[[ImGuiColorEditFlags.NoSmallPreview]]))
ImGui.SameLine()
ImGui.BeginGroup() -- Lock X position
ImGui.Text("Current")
ImGui.ColorButton("##current", color, bit32.bor(4 --[[ImGuiColorEditFlags.NoPicker]], 16384 --[[ImGuiColorEditFlags.AlphaPreviewHalf]]), ImVec2(60, 40))
ImGui.Text("Previous")
if ImGui.ColorButton("##previous", s.backup_color, bit32.bor(4 --[[ImGuiColorEditFlags.NoPicker]], 16384 --[[ImGuiColorEditFlags.AlphaPreviewHalf]]), ImVec2(60, 40)) then
ImVec4_Copy(color, s.backup_color)
end
ImGui.Separator()
ImGui.Text("Palette")
for n = 0, #saved_palette - 1 do
ImGui.PushID(n)
if (n % 8) ~= 0 then
ImGui.SameLine(0.0, ImGui.GetStyle().ItemSpacing.y)
end
local palette_button_flags = bit32.bor(2 --[[ImGuiColorEditFlags.NoAlpha]], 4 --[[ImGuiColorEditFlags.NoPicker]], 64 --[[ImGuiColorEditFlags.NoTooltip]])
if ImGui.ColorButton("##palette", saved_palette[n + 1], palette_button_flags, ImVec2(20, 20)) then
color.x, color.y, color.z = saved_palette[n + 1].x, saved_palette[n + 1].y, saved_palette[n + 1].z -- Preserve alpha!
end
-- Allow user to drop colors into each palette entry. Note that ColorButton() is already a
-- drag source by default, unless specifying the ImGuiColorEditFlags_NoDragDrop flag.
if ImGui.BeginDragDropTarget() then
local payload = ImGui.AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)
if payload then
local d = payload.Data -- [Port] memcpy -> copy components (Data is a 1-based float array)
saved_palette[n + 1].x, saved_palette[n + 1].y, saved_palette[n + 1].z = d[1], d[2], d[3]
end
payload = ImGui.AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)
if payload then
local d = payload.Data -- [Port] memcpy -> copy components
saved_palette[n + 1].x, saved_palette[n + 1].y, saved_palette[n + 1].z, saved_palette[n + 1].w = d[1], d[2], d[3], d[4]
end
ImGui.EndDragDropTarget()
end
ImGui.PopID()
end
ImGui.EndGroup()
ImGui.EndPopup()
end
IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (simple)")
ImGui.Text("Color button only:")
_, s.no_border = ImGui.Checkbox("ImGuiColorEditFlags_NoBorder", s.no_border)
ImGui.ColorButton("MyColor##3c", color, bit32.bor(base_flags, s.no_border and 1024 --[[ImGuiColorEditFlags.NoBorder]] or 0), ImVec2(80, 80))
IMGUI_DEMO_MARKER("Widgets/Color/ColorPicker")
ImGui.SeparatorText("Color picker")
ImGui.PushID("Color picker")
_, s.color_picker_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags_NoAlpha", s.color_picker_flags, 2 --[[ImGuiColorEditFlags.NoAlpha]])
_, s.color_picker_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags_AlphaBar", s.color_picker_flags, 262144 --[[ImGuiColorEditFlags.AlphaBar]])
_, s.color_picker_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags_NoSidePreview", s.color_picker_flags, 256 --[[ImGuiColorEditFlags.NoSidePreview]])
if bit32.band(s.color_picker_flags, 256 --[[ImGuiColorEditFlags.NoSidePreview]]) ~= 0 then
ImGui.SameLine()
_, s.ref_color = ImGui.Checkbox("With Ref Color", s.ref_color)
if s.ref_color then
ImGui.SameLine()
ImGui.ColorEdit4("##RefColor", s.ref_color_v, bit32.bor(32 --[[ImGuiColorEditFlags.NoInputs]], base_flags))
end
end
_, s.color_picker_flags = ImGui.CheckboxFlags("ImGuiColorEditFlags_PickerNoRotate", s.color_picker_flags, 134217728 --[[ImGuiColorEditFlags.PickerNoRotate]])
s.picker_mode = ImGui.Combo("Picker Mode", s.picker_mode, "Auto/Current\0ImGuiColorEditFlags_PickerHueBar\0ImGuiColorEditFlags_PickerHueWheel\0")
ImGui.SameLine(); HelpMarker("When not specified explicitly, user can right-click the picker to change mode.")
s.display_mode = ImGui.Combo("Display Mode", s.display_mode, "Auto/Current\0ImGuiColorEditFlags_NoInputs\0ImGuiColorEditFlags_DisplayRGB\0ImGuiColorEditFlags_DisplayHSV\0ImGuiColorEditFlags_DisplayHex\0")
ImGui.SameLine(); HelpMarker(
"ColorEdit defaults to displaying RGB inputs if you don't specify a display mode, " ..
"but the user can change it with a right-click on those inputs.\n\nColorPicker defaults to displaying RGB+HSV+Hex " ..
"if you don't specify a display mode.\n\nYou can change the defaults using io.ConfigColorEditFlags.")
local flags = bit32.bor(base_flags, s.color_picker_flags)
if s.picker_mode == 1 then flags = bit32.bor(flags, 33554432 --[[ImGuiColorEditFlags.PickerHueBar]]) end
if s.picker_mode == 2 then flags = bit32.bor(flags, 67108864 --[[ImGuiColorEditFlags.PickerHueWheel]]) end
if s.display_mode == 1 then flags = bit32.bor(flags, 32 --[[ImGuiColorEditFlags.NoInputs]]) end -- Disable all RGB/HSV/Hex displays
if s.display_mode == 2 then flags = bit32.bor(flags, 1048576 --[[ImGuiColorEditFlags.DisplayRGB]]) end -- Override display mode
if s.display_mode == 3 then flags = bit32.bor(flags, 2097152 --[[ImGuiColorEditFlags.DisplayHSV]]) end
if s.display_mode == 4 then flags = bit32.bor(flags, 4194304 --[[ImGuiColorEditFlags.DisplayHex]]) end
ImGui.ColorPicker4("MyColor##4", color, flags, s.ref_color and s.ref_color_v or nil)
ImGui.Text("Set defaults in code:")
ImGui.SameLine(); HelpMarker(
"io.ConfigColorEditFlags is designed to allow you to set boot-time default.\n" ..
"We don't have Push/Pop functions because you can force options on a per-widget basis if needed, " ..
"and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid " ..
"encouraging you to persistently save values that aren't forward-compatible.")
if ImGui.Button("Overwrite default: Uint8 + HSV + Hue Bar") then
ImGui.GetIO().ConfigColorEditFlags = bit32.bor(8388608 --[[ImGuiColorEditFlags.Uint8]], 2097152 --[[ImGuiColorEditFlags.DisplayHSV]], 33554432 --[[ImGuiColorEditFlags.PickerHueBar]], 268435456 --[[ImGuiColorEditFlags.InputRGB]]) -- [Port] + InputRGB: without it ColorEdit4's InputMask_ power-of-two assert trips
end
if ImGui.Button("Overwrite default: Float + HDR + Hue Wheel") then
ImGui.GetIO().ConfigColorEditFlags = bit32.bor(16777216 --[[ImGuiColorEditFlags.Float]], 524288 --[[ImGuiColorEditFlags.HDR]], 67108864 --[[ImGuiColorEditFlags.PickerHueWheel]], 1048576 --[[ImGuiColorEditFlags.DisplayRGB]], 268435456 --[[ImGuiColorEditFlags.InputRGB]]) -- [Port] + DisplayRGB + InputRGB: same asserts (DisplayMask_/InputMask_)
end
-- Always display a small version of both types of pickers
-- (that's in order to make it more visible in the demo to people who are skimming quickly through it)
ImGui.Text("Both types:")
local w = (ImGui.GetContentRegionAvail().x - ImGui.GetStyle().ItemSpacing.y) * 0.40
ImGui.SetNextItemWidth(w)
ImGui.ColorPicker3("##MyColor##5", color, bit32.bor(33554432 --[[ImGuiColorEditFlags.PickerHueBar]], 256 --[[ImGuiColorEditFlags.NoSidePreview]], 32 --[[ImGuiColorEditFlags.NoInputs]], 2 --[[ImGuiColorEditFlags.NoAlpha]]))
ImGui.SameLine()
ImGui.SetNextItemWidth(w)
ImGui.ColorPicker3("##MyColor##6", color, bit32.bor(67108864 --[[ImGuiColorEditFlags.PickerHueWheel]], 256 --[[ImGuiColorEditFlags.NoSidePreview]], 32 --[[ImGuiColorEditFlags.NoInputs]], 2 --[[ImGuiColorEditFlags.NoAlpha]]))
ImGui.PopID()
-- HSV encoded support (to avoid RGB<>HSV round trips and singularities when S==0 or V==0)
local color_hsv = s.color_hsv -- Stored as HSV!
ImGui.Spacing()
ImGui.Text("HSV encoded colors")
ImGui.SameLine(); HelpMarker(
"By default, colors are given to ColorEdit and ColorPicker in RGB, but ImGuiColorEditFlags_InputHSV " ..
"allows you to store colors as HSV and pass them to ColorEdit and ColorPicker as HSV. This comes with the " ..
"added benefit that you can manipulate hue values with the picker even when saturation or value are zero.")
ImGui.Text("Color widget with InputHSV:")
ImGui.ColorEdit4("HSV shown as RGB##1", color_hsv, bit32.bor(1048576 --[[ImGuiColorEditFlags.DisplayRGB]], 536870912 --[[ImGuiColorEditFlags.InputHSV]], 16777216 --[[ImGuiColorEditFlags.Float]]))
ImGui.ColorEdit4("HSV shown as HSV##1", color_hsv, bit32.bor(2097152 --[[ImGuiColorEditFlags.DisplayHSV]], 536870912 --[[ImGuiColorEditFlags.InputHSV]], 16777216 --[[ImGuiColorEditFlags.Float]]))
ImGui.DragFloat4("Raw HSV values", color_hsv, 0.01, 0.0, 1.0)
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsComboBoxes()
-------------------------------------------------------------------------------
S.DemoWindowWidgetsComboBoxes = {
flags = 0,
item_selected_idx = 0, -- Here we store our selection data as an index.
filter = nil, -- ImGuiTextFilter, created lazily
item_current_2 = 0,
item_current_3 = -1, -- If the selection isn't within 0..count, Combo won't display a preview
item_current_4 = 0,
}
function DemoWindowWidgetsComboBoxes()
local _, pressed
if ImGui.TreeNode("Combo") then
local s = S.DemoWindowWidgetsComboBoxes
IMGUI_DEMO_MARKER("Widgets/Combo")
-- Combo Boxes are also called "Dropdown" in other systems
-- Expose flags as checkbox for the demo
_, s.flags = ImGui.CheckboxFlags("ImGuiComboFlags_PopupAlignLeft", s.flags, 1 --[[ImGuiComboFlags.PopupAlignLeft]])
ImGui.SameLine(); HelpMarker("Only makes a difference if the popup is larger than the combo")
pressed, s.flags = ImGui.CheckboxFlags("ImGuiComboFlags_NoArrowButton", s.flags, 32 --[[ImGuiComboFlags.NoArrowButton]])
if pressed then
s.flags = bit32.band(s.flags, bit32.bnot(64 --[[ImGuiComboFlags.NoPreview]])) -- Clear incompatible flags
end
pressed, s.flags = ImGui.CheckboxFlags("ImGuiComboFlags_NoPreview", s.flags, 64 --[[ImGuiComboFlags.NoPreview]])
if pressed then
s.flags = bit32.band(s.flags, bit32.bnot(bit32.bor(32 --[[ImGuiComboFlags.NoArrowButton]], 128 --[[ImGuiComboFlags.WidthFitPreview]]))) -- Clear incompatible flags
end
pressed, s.flags = ImGui.CheckboxFlags("ImGuiComboFlags_WidthFitPreview", s.flags, 128 --[[ImGuiComboFlags.WidthFitPreview]])
if pressed then
s.flags = bit32.band(s.flags, bit32.bnot(64 --[[ImGuiComboFlags.NoPreview]]))
end
-- Override default popup height
pressed, s.flags = ImGui.CheckboxFlags("ImGuiComboFlags_HeightSmall", s.flags, 2 --[[ImGuiComboFlags.HeightSmall]])
if pressed then
s.flags = bit32.band(s.flags, bit32.bnot(bit32.band(30 --[[ImGuiComboFlags.HeightMask_]], bit32.bnot(2 --[[ImGuiComboFlags.HeightSmall]]))))
end
pressed, s.flags = ImGui.CheckboxFlags("ImGuiComboFlags_HeightRegular", s.flags, 4 --[[ImGuiComboFlags.HeightRegular]])
if pressed then
s.flags = bit32.band(s.flags, bit32.bnot(bit32.band(30 --[[ImGuiComboFlags.HeightMask_]], bit32.bnot(4 --[[ImGuiComboFlags.HeightRegular]]))))
end
pressed, s.flags = ImGui.CheckboxFlags("ImGuiComboFlags_HeightLargest", s.flags, 16 --[[ImGuiComboFlags.HeightLargest]])
if pressed then
s.flags = bit32.band(s.flags, bit32.bnot(bit32.band(30 --[[ImGuiComboFlags.HeightMask_]], bit32.bnot(16 --[[ImGuiComboFlags.HeightLargest]]))))
end
-- Using the generic BeginCombo() API, you have full control over how to display the combo contents.
-- (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively
-- stored in the object itself, etc.)
local items = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }
-- Pass in the preview value visible before opening the combo (it could technically be different contents or not pulled from items[])
local combo_preview_value = items[s.item_selected_idx + 1]
if ImGui.BeginCombo("combo 1", combo_preview_value, s.flags) then
for n = 0, #items - 1 do
local is_selected = (s.item_selected_idx == n)
if ImGui.Selectable(items[n + 1], is_selected) then
s.item_selected_idx = n
end
-- Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
if is_selected then
ImGui.SetItemDefaultFocus()
end
end
ImGui.EndCombo()
end
-- Show case embedding a filter using a simple trick: displaying the filter inside combo contents.
-- See https://github.com/ocornut/imgui/issues/718 for advanced/esoteric alternatives.
if ImGui.BeginCombo("combo 2 (w/ filter)", combo_preview_value, s.flags) then
if s.filter == nil then s.filter = ImGuiTextFilter() end
local filter = s.filter
if ImGui.IsWindowAppearing() then
ImGui.SetKeyboardFocusHere()
filter:Clear()
end
ImGui.SetNextItemShortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 551 --[[ImGuiKey.F]]))
ImGui.SetNextItemWidth(-1.1754943508223e-38 --[[FLT_MIN]])
filter:DrawWithHint("##Filter", "Filter (incl -excl)")
for n = 0, #items - 1 do
local is_selected = (s.item_selected_idx == n)
if filter:PassFilter(items[n + 1]) then
if ImGui.Selectable(items[n + 1], is_selected) then
s.item_selected_idx = n
end
end
end
ImGui.EndCombo()
end
ImGui.Spacing()
ImGui.SeparatorText("One-liner variants")
HelpMarker("The Combo() function is not greatly useful apart from cases were you want to embed all options in a single strings.\nFlags above don't apply to this section.")
-- Simplified one-liner Combo() API, using values packed in a single constant string
-- This is a convenience for when the selection set is small and known at compile-time.
s.item_current_2 = ImGui.Combo("combo 3 (one-liner)", s.item_current_2, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0")
-- Simplified one-liner Combo() using an array of const char*
-- This is not very useful (may obsolete): prefer using BeginCombo()/EndCombo() for full control.
s.item_current_3 = ImGui.Combo("combo 4 (array)", s.item_current_3, items, #items)
-- Simplified one-liner Combo() using an accessor function
s.item_current_4 = ImGui.Combo("combo 5 (function)", s.item_current_4, function(data, n) return data[n + 1] end, items, #items)
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsDataTypes()
-------------------------------------------------------------------------------
S.DemoWindowWidgetsDataTypes = {
-- State
s8_v = 127,
u8_v = 255,
s16_v = 32767,
u16_v = 65535,
s32_v = -1,
u32_v = 4294967295, -- (ImU32)-1
-- [Port] s64_v / u64_v: 64-bit integer types are not supported (Lua numbers are doubles)
f32_v = 0.123,
f64_v = 90000.01234567890123456789,
drag_clamp = false,
inputs_step = true,
flags = ImGuiInputTextFlags.None,
}
function DemoWindowWidgetsDataTypes()
local _
if ImGui.TreeNode("Data Types") then
local s = S.DemoWindowWidgetsDataTypes
IMGUI_DEMO_MARKER("Widgets/Data Types")
-- DragScalar/InputScalar/SliderScalar functions allow various data types
-- - signed/unsigned
-- - 8/16/32/64-bits
-- - integer/float/double
-- To avoid polluting the public API with all possible combinations, we use the ImGuiDataType enum
-- to pass the type, and passing all arguments by pointer.
-- This is the reason the test code below creates local variables to hold "zero" "one" etc. for each type.
-- In practice, if you frequently use a given type that is not covered by the normal API entry points,
-- you can wrap it yourself inside a 1 line function which can take typed argument as value instead of void*,
-- and then pass their address to the generic function. For example:
-- bool MySliderU64(const char *label, u64* value, u64 min = 0, u64 max = 0, const char* format = "%lld")
-- {
-- return SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format);
-- }
-- [Port] in this port the value is passed and returned by value: `v = ImGui.SliderScalar(label, data_type, v, min, max, format)`
-- Setup limits (as helper variables so we can take their address, as explained above)
-- Note: SliderScalar() functions have a maximum usable range of half the natural type maximum, hence the /2.
-- [Port] LLONG_MIN/LLONG_MAX/ULLONG_MAX fallback block dropped: no 64-bit integer types
local s8_zero, s8_one, s8_fifty, s8_min, s8_max = 0, 1, 50, -128, 127
local u8_zero, u8_one, u8_fifty, u8_min, u8_max = 0, 1, 50, 0, 255
local s16_zero, s16_one, s16_fifty, s16_min, s16_max = 0, 1, 50, -32768, 32767
local u16_zero, u16_one, u16_fifty, u16_min, u16_max = 0, 1, 50, 0, 65535
local s32_zero, s32_one, s32_fifty, s32_min, s32_max, s32_hi_a, s32_hi_b = 0, 1, 50, -1073741824, 1073741823, 1073741823 - 100, 1073741823 -- INT_MIN/2, INT_MAX/2
local u32_zero, u32_one, u32_fifty, u32_min, u32_max, u32_hi_a, u32_hi_b = 0, 1, 50, 0, 2147483647, 2147483647 - 100, 2147483647 -- UINT_MAX/2
-- [Port] s64_* / u64_* limits: ImGuiDataType.S64/U64 are not supported
local f32_zero, f32_one, f32_lo_a, f32_hi_a = 0.0, 1.0, -10000000000.0, 10000000000.0
local f64_zero, f64_one, f64_lo_a, f64_hi_a = 0.0, 1.0, -1000000000000000.0, 1000000000000000.0
-- State
-- (see S.DemoWindowWidgetsDataTypes)
local drag_speed = 0.2
IMGUI_DEMO_MARKER("Widgets/Data Types/Drags")
ImGui.SeparatorText("Drags")
_, s.drag_clamp = ImGui.Checkbox("Clamp integers to 0..50", s.drag_clamp)
ImGui.SameLine(); HelpMarker(
"As with every widget in dear imgui, we never modify values unless there is a user interaction.\n" ..
"You can override the clamping limits by using Ctrl+Click to input a value.")
local drag_clamp = s.drag_clamp
s.s8_v = ImGui.DragScalar("drag s8", 0 --[[ImGuiDataType.S8]], s.s8_v, drag_speed, drag_clamp and s8_zero or nil, drag_clamp and s8_fifty or nil)
s.u8_v = ImGui.DragScalar("drag u8", 1 --[[ImGuiDataType.U8]], s.u8_v, drag_speed, drag_clamp and u8_zero or nil, drag_clamp and u8_fifty or nil, "%u ms")
s.s16_v = ImGui.DragScalar("drag s16", 2 --[[ImGuiDataType.S16]], s.s16_v, drag_speed, drag_clamp and s16_zero or nil, drag_clamp and s16_fifty or nil)
s.u16_v = ImGui.DragScalar("drag u16", 3 --[[ImGuiDataType.U16]], s.u16_v, drag_speed, drag_clamp and u16_zero or nil, drag_clamp and u16_fifty or nil, "%u ms")
s.s32_v = ImGui.DragScalar("drag s32", 4 --[[ImGuiDataType.S32]], s.s32_v, drag_speed, drag_clamp and s32_zero or nil, drag_clamp and s32_fifty or nil)
s.s32_v = ImGui.DragScalar("drag s32 hex", 4 --[[ImGuiDataType.S32]], s.s32_v, drag_speed, drag_clamp and s32_zero or nil, drag_clamp and s32_fifty or nil, "0x%08X")
s.u32_v = ImGui.DragScalar("drag u32", 5 --[[ImGuiDataType.U32]], s.u32_v, drag_speed, drag_clamp and u32_zero or nil, drag_clamp and u32_fifty or nil, "%u ms")
-- [Port] ImGui.DragScalar("drag s64", ImGuiDataType.S64, s64_v, ...) -- 64-bit types not supported
-- [Port] ImGui.DragScalar("drag u64", ImGuiDataType.U64, u64_v, ...) -- 64-bit types not supported
s.f32_v = ImGui.DragScalar("drag float", 6 --[[ImGuiDataType.Float]], s.f32_v, 0.005, f32_zero, f32_one, "%f")
s.f32_v = ImGui.DragScalar("drag float log", 6 --[[ImGuiDataType.Float]], s.f32_v, 0.005, f32_zero, f32_one, "%f", 32 --[[ImGuiSliderFlags.Logarithmic]])
s.f64_v = ImGui.DragScalar("drag double", 7 --[[ImGuiDataType.Double]], s.f64_v, 0.0005, f64_zero, nil, "%.10f grams")
s.f64_v = ImGui.DragScalar("drag double log",7 --[[ImGuiDataType.Double]], s.f64_v, 0.0005, f64_zero, f64_one, "0 < %.10f < 1", 32 --[[ImGuiSliderFlags.Logarithmic]])
IMGUI_DEMO_MARKER("Widgets/Data Types/Sliders")
ImGui.SeparatorText("Sliders")
s.s8_v = ImGui.SliderScalar("slider s8 full", 0 --[[ImGuiDataType.S8]], s.s8_v, s8_min, s8_max, "%d")
s.u8_v = ImGui.SliderScalar("slider u8 full", 1 --[[ImGuiDataType.U8]], s.u8_v, u8_min, u8_max, "%u")
s.s16_v = ImGui.SliderScalar("slider s16 full", 2 --[[ImGuiDataType.S16]], s.s16_v, s16_min, s16_max, "%d")
s.u16_v = ImGui.SliderScalar("slider u16 full", 3 --[[ImGuiDataType.U16]], s.u16_v, u16_min, u16_max, "%u")
s.s32_v = ImGui.SliderScalar("slider s32 low", 4 --[[ImGuiDataType.S32]], s.s32_v, s32_zero, s32_fifty,"%d")
s.s32_v = ImGui.SliderScalar("slider s32 high", 4 --[[ImGuiDataType.S32]], s.s32_v, s32_hi_a, s32_hi_b, "%d")
s.s32_v = ImGui.SliderScalar("slider s32 full", 4 --[[ImGuiDataType.S32]], s.s32_v, s32_min, s32_max, "%d")
s.s32_v = ImGui.SliderScalar("slider s32 hex", 4 --[[ImGuiDataType.S32]], s.s32_v, s32_zero, s32_fifty, "0x%04X")
s.u32_v = ImGui.SliderScalar("slider u32 low", 5 --[[ImGuiDataType.U32]], s.u32_v, u32_zero, u32_fifty,"%u")
s.u32_v = ImGui.SliderScalar("slider u32 high", 5 --[[ImGuiDataType.U32]], s.u32_v, u32_hi_a, u32_hi_b, "%u")
s.u32_v = ImGui.SliderScalar("slider u32 full", 5 --[[ImGuiDataType.U32]], s.u32_v, u32_min, u32_max, "%u")
-- [Port] "slider s64 low" / "slider s64 high" / "slider s64 full" (ImGuiDataType.S64): 64-bit types not supported
-- [Port] "slider u64 low" / "slider u64 high" / "slider u64 full" (ImGuiDataType.U64): 64-bit types not supported
s.f32_v = ImGui.SliderScalar("slider float low", 6 --[[ImGuiDataType.Float]], s.f32_v, f32_zero, f32_one)
s.f32_v = ImGui.SliderScalar("slider float low log", 6 --[[ImGuiDataType.Float]], s.f32_v, f32_zero, f32_one, "%.10f", 32 --[[ImGuiSliderFlags.Logarithmic]])
s.f32_v = ImGui.SliderScalar("slider float high", 6 --[[ImGuiDataType.Float]], s.f32_v, f32_lo_a, f32_hi_a, "%e")
s.f64_v = ImGui.SliderScalar("slider double low", 7 --[[ImGuiDataType.Double]], s.f64_v, f64_zero, f64_one, "%.10f grams")
s.f64_v = ImGui.SliderScalar("slider double low log",7 --[[ImGuiDataType.Double]], s.f64_v, f64_zero, f64_one, "%.10f", 32 --[[ImGuiSliderFlags.Logarithmic]])
s.f64_v = ImGui.SliderScalar("slider double high", 7 --[[ImGuiDataType.Double]], s.f64_v, f64_lo_a, f64_hi_a, "%e grams")
ImGui.SeparatorText("Sliders (reverse)")
s.s8_v = ImGui.SliderScalar("slider s8 reverse", 0 --[[ImGuiDataType.S8]], s.s8_v, s8_max, s8_min, "%d")
s.u8_v = ImGui.SliderScalar("slider u8 reverse", 1 --[[ImGuiDataType.U8]], s.u8_v, u8_max, u8_min, "%u")
s.s32_v = ImGui.SliderScalar("slider s32 reverse", 4 --[[ImGuiDataType.S32]], s.s32_v, s32_fifty, s32_zero, "%d")
s.u32_v = ImGui.SliderScalar("slider u32 reverse", 5 --[[ImGuiDataType.U32]], s.u32_v, u32_fifty, u32_zero, "%u")
-- [Port] "slider s64 reverse" / "slider u64 reverse": 64-bit types not supported
IMGUI_DEMO_MARKER("Widgets/Data Types/Inputs")
ImGui.SeparatorText("Inputs")
_, s.inputs_step = ImGui.Checkbox("Show step buttons", s.inputs_step)
_, s.flags = ImGui.CheckboxFlags("ImGuiInputTextFlags_ReadOnly", s.flags, 512 --[[ImGuiInputTextFlags.ReadOnly]])
_, s.flags = ImGui.CheckboxFlags("ImGuiInputTextFlags_ParseEmptyRefVal", s.flags, 8192 --[[ImGuiInputTextFlags.ParseEmptyRefVal]])
_, s.flags = ImGui.CheckboxFlags("ImGuiInputTextFlags_DisplayEmptyRefVal", s.flags, 16384 --[[ImGuiInputTextFlags.DisplayEmptyRefVal]])
local inputs_step, flags = s.inputs_step, s.flags
s.s8_v = ImGui.InputScalar("input s8", 0 --[[ImGuiDataType.S8]], s.s8_v, inputs_step and s8_one or nil, nil, "%d", flags)
s.u8_v = ImGui.InputScalar("input u8", 1 --[[ImGuiDataType.U8]], s.u8_v, inputs_step and u8_one or nil, nil, "%u", flags)
s.s16_v = ImGui.InputScalar("input s16", 2 --[[ImGuiDataType.S16]], s.s16_v, inputs_step and s16_one or nil, nil, "%d", flags)
s.u16_v = ImGui.InputScalar("input u16", 3 --[[ImGuiDataType.U16]], s.u16_v, inputs_step and u16_one or nil, nil, "%u", flags)
s.s32_v = ImGui.InputScalar("input s32", 4 --[[ImGuiDataType.S32]], s.s32_v, inputs_step and s32_one or nil, nil, "%d", flags)
s.s32_v = ImGui.InputScalar("input s32 hex", 4 --[[ImGuiDataType.S32]], s.s32_v, inputs_step and s32_one or nil, nil, "%04X", flags)
s.u32_v = ImGui.InputScalar("input u32", 5 --[[ImGuiDataType.U32]], s.u32_v, inputs_step and u32_one or nil, nil, "%u", flags)
s.u32_v = ImGui.InputScalar("input u32 hex", 5 --[[ImGuiDataType.U32]], s.u32_v, inputs_step and u32_one or nil, nil, "%08X", flags)
-- [Port] ImGui.InputScalar("input s64", ImGuiDataType.S64, ...) -- 64-bit types not supported
-- [Port] ImGui.InputScalar("input u64", ImGuiDataType.U64, ...) -- 64-bit types not supported
s.f32_v = ImGui.InputScalar("input float", 6 --[[ImGuiDataType.Float]], s.f32_v, inputs_step and f32_one or nil, nil, nil, flags)
s.f64_v = ImGui.InputScalar("input double", 7 --[[ImGuiDataType.Double]], s.f64_v, inputs_step and f64_one or nil, nil, nil, flags)
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsDisableBlocks()
-------------------------------------------------------------------------------
function DemoWindowWidgetsDisableBlocks(demo_data)
local _
if ImGui.TreeNode("Disable Blocks") then
IMGUI_DEMO_MARKER("Widgets/Disable Blocks")
_, demo_data.DisableSections = ImGui.Checkbox("Disable entire section above", demo_data.DisableSections)
ImGui.SameLine(); HelpMarker("Demonstrate using BeginDisabled()/EndDisabled() across other sections.")
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsDragAndDrop()
-------------------------------------------------------------------------------
S.DemoWindowWidgetsDragAndDrop = {
col1 = { 1.0, 0.0, 0.2 },
col2 = { 0.4, 0.7, 0.0, 0.5 },
mode = 0,
names = { -- 1-based
"Bobby", "Beatrice", "Betty",
"Brianna", "Barry", "Bernard",
"Bibi", "Blaine", "Bryn"
},
item_names = { "Item One", "Item Two", "Item Three", "Item Four", "Item Five" }, -- 1-based
col4 = ImVec4(1.0, 0.0, 0.2, 1.0),
}
function DemoWindowWidgetsDragAndDrop()
if ImGui.TreeNode("Drag and Drop") then
local s = S.DemoWindowWidgetsDragAndDrop
IMGUI_DEMO_MARKER("Widgets/Drag and drop")
if ImGui.TreeNode("Drag and drop in standard widgets") then
IMGUI_DEMO_MARKER("Widgets/Drag and drop/Standard widgets")
-- ColorEdit widgets automatically act as drag source and drag target.
-- They are using standardized payload strings IMGUI_PAYLOAD_TYPE_COLOR_3F and IMGUI_PAYLOAD_TYPE_COLOR_4F
-- to allow your own widgets to use colors in their drag and drop interaction.
-- Also see 'Demo->Widgets->Color/Picker Widgets->Palette' demo.
HelpMarker("You can drag from the color squares.")
ImGui.ColorEdit3("color 1", s.col1)
ImGui.ColorEdit4("color 2", s.col2)
ImGui.TreePop()
end
if ImGui.TreeNode("Drag and drop to copy/swap items") then
IMGUI_DEMO_MARKER("Widgets/Drag and drop/Copy-swap items")
local Mode_Copy, Mode_Move, Mode_Swap = 0, 1, 2
if ImGui.RadioButtonEx("Copy", s.mode == Mode_Copy) then s.mode = Mode_Copy end ImGui.SameLine()
if ImGui.RadioButtonEx("Move", s.mode == Mode_Move) then s.mode = Mode_Move end ImGui.SameLine()
if ImGui.RadioButtonEx("Swap", s.mode == Mode_Swap) then s.mode = Mode_Swap end
local names = s.names
for n = 0, #names - 1 do
ImGui.PushID(n)
if (n % 3) ~= 0 then
ImGui.SameLine()
end
ImGui.Button(names[n + 1], ImVec2(60, 60))
-- Our buttons are both drag sources and drag targets here!
if ImGui.BeginDragDropSource(0 --[[ImGuiDragDropFlags.None]]) then
-- Set payload to carry the index of our item (could be anything)
ImGui.SetDragDropPayload("DND_DEMO_CELL", n, 4) -- [Port] sizeof(int) -> 4, payload carries the Lua value
-- Display preview (could be anything, e.g. when dragging an image we could decide to display
-- the filename and a small preview of the image, etc.)
if s.mode == Mode_Copy then ImGui.Text("Copy %s", names[n + 1]) end
if s.mode == Mode_Move then ImGui.Text("Move %s", names[n + 1]) end
if s.mode == Mode_Swap then ImGui.Text("Swap %s", names[n + 1]) end
ImGui.EndDragDropSource()
end
if ImGui.BeginDragDropTarget() then
local payload = ImGui.AcceptDragDropPayload("DND_DEMO_CELL")
if payload then
assert(payload.DataSize == 4)
local payload_n = payload.Data
if s.mode == Mode_Copy then
names[n + 1] = names[payload_n + 1]
end
if s.mode == Mode_Move then
names[n + 1] = names[payload_n + 1]
names[payload_n + 1] = ""
end
if s.mode == Mode_Swap then
local tmp = names[n + 1]
names[n + 1] = names[payload_n + 1]
names[payload_n + 1] = tmp
end
end
ImGui.EndDragDropTarget()
end
ImGui.PopID()
end
ImGui.TreePop()
end
if ImGui.TreeNode("Drag to reorder items (simple)") then
IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Drag to reorder items (simple)")
-- FIXME: there is temporary (usually single-frame) ID Conflict during reordering as a same item may be submitting twice.
-- This code was always slightly faulty but in a way which was not easily noticeable.
-- Until we fix this, enable ImGuiItemFlags_AllowDuplicateId to disable detecting the issue.
ImGui.PushItemFlag(32 --[[ImGuiItemFlags.AllowDuplicateId]], true)
-- Simple reordering
HelpMarker(
"We don't use the drag and drop api at all here! " ..
"Instead we query when the item is held but not hovered, and order items accordingly.")
local item_names = s.item_names
for n = 0, #item_names - 1 do
local item = item_names[n + 1]
ImGui.Selectable(item)
if ImGui.IsItemActive() and not ImGui.IsItemHovered() then
local n_next = n + ((ImGui.GetMouseDragDelta(0).y < 0.0) and -1 or 1)
if n_next >= 0 and n_next < #item_names then
item_names[n + 1] = item_names[n_next + 1]
item_names[n_next + 1] = item
ImGui.ResetMouseDragDelta()
end
end
end
ImGui.PopItemFlag()
ImGui.TreePop()
end
if ImGui.TreeNode("Tooltip at target location") then
IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Tooltip at target location")
for n = 0, 1 do
-- Drop targets
ImGui.Button((n ~= 0) and "drop here##1" or "drop here##0")
if ImGui.BeginDragDropTarget() then
local drop_target_flags = bit32.bor(1024 --[[ImGuiDragDropFlags.AcceptBeforeDelivery]], 4096 --[[ImGuiDragDropFlags.AcceptNoPreviewTooltip]])
local payload = ImGui.AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, drop_target_flags)
if payload then
-- IM_UNUSED(payload)
ImGui.SetMouseCursor(10 --[[ImGuiMouseCursor.NotAllowed]])
ImGui.SetTooltip("Cannot drop here!")
end
ImGui.EndDragDropTarget()
end
-- Drop source
if n == 0 then
ImGui.ColorButton("drag me", s.col4)
end
end
ImGui.TreePop()
end
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsDragsAndSliders()
-------------------------------------------------------------------------------
S.DemoWindowWidgetsDragsAndSliders = {
flags = ImGuiSliderFlags.None,
-- Drags
drag_f = 0.5,
drag_f4 = { 0.0, 0.0, 0.0, 0.0 },
drag_i = 50,
-- Sliders
slider_f = 0.5,
slider_f4 = { 0.0, 0.0, 0.0, 0.0 },
slider_i = 50,
}
function DemoWindowWidgetsDragsAndSliders()
local _
if ImGui.TreeNode("Drag/Slider Flags") then
local s = S.DemoWindowWidgetsDragsAndSliders
IMGUI_DEMO_MARKER("Widgets/Drag and Slider Flags")
-- Demonstrate using advanced flags for DragXXX and SliderXXX functions. Note that the flags are the same!
_, s.flags = ImGui.CheckboxFlags("ImGuiSliderFlags_AlwaysClamp", s.flags, 1536 --[[ImGuiSliderFlags.AlwaysClamp]])
_, s.flags = ImGui.CheckboxFlags("ImGuiSliderFlags_ClampOnInput", s.flags, 512 --[[ImGuiSliderFlags.ClampOnInput]])
ImGui.SameLine(); HelpMarker("Clamp value to min/max bounds when input manually with Ctrl+Click. By default Ctrl+Click allows going out of bounds.")
_, s.flags = ImGui.CheckboxFlags("ImGuiSliderFlags_ClampZeroRange", s.flags, 1024 --[[ImGuiSliderFlags.ClampZeroRange]])
ImGui.SameLine(); HelpMarker("Clamp even if min==max==0.0f. Otherwise DragXXX functions don't clamp.")
_, s.flags = ImGui.CheckboxFlags("ImGuiSliderFlags_Logarithmic", s.flags, 32 --[[ImGuiSliderFlags.Logarithmic]])
ImGui.SameLine(); HelpMarker("Enable logarithmic editing (more precision for small values).")
_, s.flags = ImGui.CheckboxFlags("ImGuiSliderFlags_NoRoundToFormat", s.flags, 64 --[[ImGuiSliderFlags.NoRoundToFormat]])
ImGui.SameLine(); HelpMarker("Disable rounding underlying value to match precision of the format string (e.g. %.3f values are rounded to those 3 digits).")
_, s.flags = ImGui.CheckboxFlags("ImGuiSliderFlags_NoInput", s.flags, 128 --[[ImGuiSliderFlags.NoInput]])
ImGui.SameLine(); HelpMarker("Disable Ctrl+Click or Enter key allowing to input text directly into the widget.")
_, s.flags = ImGui.CheckboxFlags("ImGuiSliderFlags_NoSpeedTweaks", s.flags, 2048 --[[ImGuiSliderFlags.NoSpeedTweaks]])
ImGui.SameLine(); HelpMarker("Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic.")
_, s.flags = ImGui.CheckboxFlags("ImGuiSliderFlags_WrapAround", s.flags, 256 --[[ImGuiSliderFlags.WrapAround]])
ImGui.SameLine(); HelpMarker("Enable wrapping around from max to min and from min to max (only supported by DragXXX() functions)")
_, s.flags = ImGui.CheckboxFlags("ImGuiSliderFlags_ColorMarkers", s.flags, 4096 --[[ImGuiSliderFlags.ColorMarkers]])
local flags = s.flags
-- Drags
ImGui.Text("Underlying float value: %f", s.drag_f)
s.drag_f = ImGui.DragFloat("DragFloat (0 -> 1)", s.drag_f, 0.005, 0.0, 1.0, "%.3f", flags)
s.drag_f = ImGui.DragFloat("DragFloat (0 -> +inf)", s.drag_f, 0.005, 0.0, 3.4028234663853e+38 --[[FLT_MAX]], "%.3f", flags)
s.drag_f = ImGui.DragFloat("DragFloat (-inf -> 1)", s.drag_f, 0.005, -3.4028234663853e+38 --[[FLT_MAX]], 1.0, "%.3f", flags)
s.drag_f = ImGui.DragFloat("DragFloat (-inf -> +inf)", s.drag_f, 0.005, -3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]], "%.3f", flags)
--ImGui.DragFloat("DragFloat (0 -> 0)", drag_f, 0.005, 0.0, 0.0, "%.3f", flags) -- To test ClampZeroRange
--ImGui.DragFloat("DragFloat (100 -> 100)", drag_f, 0.005, 100.0, 100.0, "%.3f", flags)
s.drag_i = ImGui.DragInt("DragInt (0 -> 100)", s.drag_i, 0.5, 0, 100, "%d", flags)
ImGui.DragFloat4("DragFloat4 (0 -> 1)", s.drag_f4, 0.005, 0.0, 1.0, "%.3f", flags) -- Multi-component item, mostly here to document the effect of ImGuiSliderFlags_ColorMarkers.
-- Sliders
local flags_for_sliders = bit32.band(flags, bit32.bnot(256 --[[ImGuiSliderFlags.WrapAround]]))
ImGui.Text("Underlying float value: %f", s.slider_f)
s.slider_f = ImGui.SliderFloat("SliderFloat (0 -> 1)", s.slider_f, 0.0, 1.0, "%.3f", flags_for_sliders)
s.slider_i = ImGui.SliderInt("SliderInt (0 -> 100)", s.slider_i, 0, 100, "%d", flags_for_sliders)
ImGui.SliderFloat4("SliderFloat4 (0 -> 1)", s.slider_f4, 0.0, 1.0, "%.3f", flags_for_sliders) -- Multi-component item, mostly here to document the effect of ImGuiSliderFlags_ColorMarkers. -- [Port] flags_for_sliders: upstream passes `flags`, which trips SliderScalar's WrapAround assert
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsFonts()
-------------------------------------------------------------------------------
function DemoWindowWidgetsFonts()
if ImGui.TreeNode("Fonts") then
IMGUI_DEMO_MARKER("Widgets/Fonts")
local atlas = ImGui.GetIO().Fonts
ImGui.ShowFontAtlas(atlas)
-- FIXME-NEWATLAS: Provide a demo to add/create a procedural font?
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsImages()
-------------------------------------------------------------------------------
S.DemoWindowWidgetsImages = {
image_viewer = ExampleImageViewerData(),
pressed_count = 0,
}
function DemoWindowWidgetsImages()
local s = S.DemoWindowWidgetsImages
if ImGui.TreeNode("Images") then
IMGUI_DEMO_MARKER("Widgets/Images")
local io = ImGui.GetIO()
ImGui.TextWrapped(
"Below we are displaying the font texture (which is the only texture we have access to in this demo). " ..
"Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. " ..
"Hover the texture for a zoomed view!")
-- Below we are displaying the font texture because it is the only texture we have access to inside the demo!
-- Read description about ImTextureID/ImTextureRef and FAQ for details about texture identifiers.
-- If you use one of the default imgui_impl_XXXX.cpp rendering backend, they all have comments at the top
-- of their respective source file to specify what they are using as texture identifier, for example:
-- - The imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer.
-- - The imgui_impl_opengl3.cpp renderer expect a GLuint OpenGL texture identifier, etc.
-- So with the DirectX11 backend, you call ImGui.Image() with a 'ID3D11ShaderResourceView*' cast to ImTextureID.
-- - If you decided that ImTextureID = MyEngineTexture*, then you can pass your MyEngineTexture* pointers
-- to ImGui.Image(), and gather width/height through your own functions, etc.
-- - You can use ShowMetricsWindow() to inspect the draw data that are being passed to your renderer,
-- it will help you debug issues if you are confused about it.
-- - Consider using the lower-level ImDrawList::AddImage() API, via ImGui.GetWindowDrawList():AddImage().
-- - Read https://github.com/ocornut/imgui/blob/master/docs/FAQ.md
-- - Read https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
-- Grab the current texture identifier used by the font atlas.
local atlas = io.Fonts
local my_tex_id = atlas.TexRef
local my_tex_w = atlas.TexData.Width -- Regular user code should never have to care about TexData-> fields, but since we want to display the entire texture here, we pull Width/Height from it.
local my_tex_h = atlas.TexData.Height
ImGui.Text("%.0fx%.0f", my_tex_w, my_tex_h)
-- Basic drawing
ImGui.SeparatorText("Image()/ImageWithBg() function")
local uv_min = ImVec2(0.0, 0.0) -- Top-left
local uv_max = ImVec2(1.0, 1.0) -- Lower-right
ImGui.PushStyleVar(24 --[[ImGuiStyleVar.ImageBorderSize]], ImMax(1.0, ImGui.GetStyle().ImageBorderSize))
ImGui.ImageWithBg(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, ImVec4(0.0, 0.0, 0.0, 1.0))
ImGui.PopStyleVar()
-- Fancy widget
ImGui.SeparatorText("Interactive Image Viewer")
local canvas_size = ImVec2(ImGui.GetContentRegionAvail().x, my_tex_h * 2.0)
ExampleImageViewer_DrawOptions(s.image_viewer)
ExampleImageViewer_DrawCanvas(s.image_viewer, canvas_size, my_tex_id, math.floor(my_tex_w), math.floor(my_tex_h))
IMGUI_DEMO_MARKER("Widgets/Images/Textured buttons")
ImGui.SeparatorText("Textured Buttons")
ImGui.TextWrapped("And now some textured buttons..")
for i = 0, 7 do
-- UV coordinates are often (0.0, 0.0) and (1.0, 1.0) to display an entire textures.
-- Here are trying to display only a 32x32 pixels area of the texture, hence the UV computation.
-- Read about UV coordinates here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
ImGui.PushID(i)
if i > 0 then
ImGui.PushStyleVar(11 --[[ImGuiStyleVar.FramePadding]], ImVec2(i - 1.0, i - 1.0))
end
local size = ImVec2(32.0, 32.0) -- Size of the image we want to make visible
local uv0 = ImVec2(0.0, 0.0) -- UV coordinates for lower-left
local uv1 = ImVec2(32.0 / my_tex_w, 32.0 / my_tex_h) -- UV coordinates for (32,32) in our texture
local bg_col = ImVec4(0.0, 0.0, 0.0, 1.0) -- Black background
local tint_col = ImVec4(1.0, 1.0, 1.0, 1.0) -- No tint
if ImGui.ImageButton("", my_tex_id, size, uv0, uv1, bg_col, tint_col) then
s.pressed_count = s.pressed_count + 1
end
if i > 0 then
ImGui.PopStyleVar()
end
ImGui.PopID()
ImGui.SameLine()
end
ImGui.NewLine()
ImGui.Text("Pressed %d times.", s.pressed_count)
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsListBoxes()
-------------------------------------------------------------------------------
S.DemoWindowWidgetsListBoxes = {
item_selected_idx = 0, -- Here we store our selected data as an index.
item_highlight = false,
}
function DemoWindowWidgetsListBoxes()
local s = S.DemoWindowWidgetsListBoxes
if ImGui.TreeNode("List Boxes") then
IMGUI_DEMO_MARKER("Widgets/List Boxes")
-- BeginListBox() is essentially a thin wrapper to using BeginChild()/EndChild()
-- using the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.
-- You may be tempted to simply use BeginChild() directly. However note that BeginChild() requires EndChild()
-- to always be called (inconsistent with BeginListBox()/EndListBox()).
-- Using the generic BeginListBox() API, you have full control over how to display the combo contents.
-- (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively
-- stored in the object itself, etc.)
local items = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }
-- static int item_selected_idx = 0; // Here we store our selected data as an index.
local item_highlighted_idx = -1 -- Here we store our highlighted data as an index.
_, s.item_highlight = ImGui.Checkbox("Highlight hovered item in second listbox", s.item_highlight)
if ImGui.BeginListBox("listbox 1") then
for n = 0, #items - 1 do
local is_selected = (s.item_selected_idx == n)
if ImGui.Selectable(items[n + 1], is_selected) then
s.item_selected_idx = n
end
if s.item_highlight and ImGui.IsItemHovered() then
item_highlighted_idx = n
end
-- Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
if is_selected then
ImGui.SetItemDefaultFocus()
end
end
ImGui.EndListBox()
end
ImGui.SameLine(); HelpMarker("Here we are sharing selection state between both boxes.")
-- Custom size: use all width, 5 items tall
ImGui.Text("Full-width:")
if ImGui.BeginListBox("##listbox 2", ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], 5 * ImGui.GetTextLineHeightWithSpacing())) then
for n = 0, #items - 1 do
local is_selected = (s.item_selected_idx == n)
local flags = 0
if item_highlighted_idx == n then flags = 32 --[[ImGuiSelectableFlags.Highlight]] end
if ImGui.Selectable(items[n + 1], is_selected, flags) then
s.item_selected_idx = n
end
-- Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
if is_selected then
ImGui.SetItemDefaultFocus()
end
end
ImGui.EndListBox()
end
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsLiveEdit()
-------------------------------------------------------------------------------
S.DemoWindowWidgetsLiveEdit = {
str = ImGui.NewTextBuffer("", 32),
i = 0,
f = 0.0,
}
function DemoWindowWidgetsLiveEdit(demo_data)
local s = S.DemoWindowWidgetsLiveEdit
if ImGui.TreeNode("Live Edit Flags") then
IMGUI_DEMO_MARKER("Widgets/Live Edit Flags")
ImGui.TextWrapped("Select whether to apply keyboard edits to backing variables _while_ typing.")
_, demo_data.LiveEditOverride = ImGui.Checkbox("Override Live Edit Flags in Demo Window", demo_data.LiveEditOverride)
if not demo_data.LiveEditOverride then
demo_data.LiveEditFlags = ImGui.GetItemFlags()
end
ImGui.BeginDisabled(demo_data.LiveEditOverride == false)
ImGui.Indent()
_, demo_data.LiveEditFlags = ImGui.CheckboxFlags("ImGuiItemFlags_LiveEditOnInputText", demo_data.LiveEditFlags, 128 --[[ImGuiItemFlags.LiveEditOnInputText]])
_, demo_data.LiveEditFlags = ImGui.CheckboxFlags("ImGuiItemFlags_LiveEditOnInputScalar", demo_data.LiveEditFlags, 256 --[[ImGuiItemFlags.LiveEditOnInputScalar]])
ImGui.Unindent()
ImGui.EndDisabled()
ImGui.Text("Try typing '123' and seeing effect on backing value:")
ImGui.InputText("str", s.str, 32)
ImGui.Text("Backing value: \"%s\"", ImGui.TextBufferToString(s.str))
s.i = ImGui.InputInt("int", s.i, 0, 0)
ImGui.Text("Backing value: %d", s.i)
s.f = ImGui.SliderFloat("float", s.f, 0.0, 100.0)
ImGui.Text("Backing value: %f", s.f)
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsMixedValues()
-------------------------------------------------------------------------------
S.DemoWindowWidgetsMixedValues = {
use_liveedit = false,
items = { 12.0, 0.0, 0.0 },
}
function DemoWindowWidgetsMixedValues()
local s = S.DemoWindowWidgetsMixedValues
if ImGui.TreeNode("Mixed Values") then
-- This is designed for advanced property editors which are generally reusable and data-driven.
HelpMarker("Using ImGuiItemFlags_MixedValue.")
local items = s.items
local item_ref = 1 -- [Port] index of the reference item (C++: float* item_ref = &items[0])
_, s.use_liveedit = ImGui.Checkbox("ImGuiItemFlags_LiveEditOnInput", s.use_liveedit)
ImGui.SeparatorText("Scalar/Text Widgets")
local is_mixed = items[1] ~= items[2] or items[1] ~= items[3]
-- Demonstrate Drags, Sliders, Inputs
ImGui.PushItemFlag(384 --[[ImGuiItemFlags.LiveEditOnInput]], s.use_liveedit)
ImGui.PushItemFlag(4096 --[[ImGuiItemFlags.MixedValue]], is_mixed)
local edited = false
local changed
items[item_ref], changed = ImGui.DragFloat("DragFloat", items[item_ref]); edited = edited or changed
items[item_ref], changed = ImGui.SliderFloat("SliderFloat", items[item_ref], 0.0, 100.0); edited = edited or changed
items[item_ref], changed = ImGui.InputFloat("InputFloat", items[item_ref], 1.0); edited = edited or changed
if edited then
for n = 1, #items do
if n ~= item_ref then
items[n] = items[item_ref]
end
end
end
ImGui.PopItemFlag()
ImGui.Text("Underlying data:")
items[1] = ImGui.InputFloat("item 0 (ref)", items[1])
items[2] = ImGui.InputFloat("item 1", items[2])
items[3] = ImGui.InputFloat("item 2", items[3])
ImGui.PopItemFlag()
-- Demonstrate Checkbox(), RadioButton(), Combo(), ColorEdit4()
ImGui.SeparatorText("Others Widgets")
ImGui.Text("(note: edits are not applied in this demo)") -- <-- Would need more state tracking.
local b_on, b_off = true, false
_, b_on = ImGui.Checkbox("Checkbox On", b_on)
_, b_off = ImGui.Checkbox("Checkbox Off", b_off)
ImGui.PushItemFlag(4096 --[[ImGuiItemFlags.MixedValue]], true)
_, b_off = ImGui.Checkbox("Checkbox Mixed", b_off)
ImGui.RadioButtonEx("RadioButton Mixed", true) -- [Port] RadioButton(label, bool active) overload is RadioButtonEx
ImGui.SameLine()
ImGui.RadioButtonEx("RadioButton Mixed##2", true) -- Showing 2 radio buttons makes the example more clear
local combo_idx = 0
combo_idx = ImGui.Combo("Combo", combo_idx, "One\0Two\0Three\0")
local color = ImVec4(0.5, 0.5, 0.5, 0.5)
ImGui.ColorEdit4("ColorEdit4", color)
ImGui.PopItemFlag()
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsMultiComponents()
-------------------------------------------------------------------------------
S.DemoWindowWidgetsMultiComponents = {
vec4f = { 0.10, 0.20, 0.30, 0.44 },
vec4i = { 1, 5, 100, 255 },
flags = 0,
begin = 10, ["end"] = 90,
begin_i = 100, end_i = 1000,
}
function DemoWindowWidgetsMultiComponents()
local s = S.DemoWindowWidgetsMultiComponents
if ImGui.TreeNode("Multi-component Widgets") then
IMGUI_DEMO_MARKER("Widgets/Multi-component Widgets")
local vec4f = s.vec4f
local vec4i = s.vec4i
_, s.flags = ImGui.CheckboxFlags("ImGuiSliderFlags_ColorMarkers", s.flags, 4096 --[[ImGuiSliderFlags.ColorMarkers]]) -- Only passing this to Drag/Sliders
local flags = s.flags
ImGui.SeparatorText("2-wide")
ImGui.InputFloat2("input float2", vec4f)
ImGui.InputInt2("input int2", vec4i)
ImGui.DragFloat2("drag float2", vec4f, 0.01, 0.0, 1.0, nil, flags)
ImGui.DragInt2("drag int2", vec4i, 1, 0, 255, nil, flags)
ImGui.SliderFloat2("slider float2", vec4f, 0.0, 1.0, nil, flags)
ImGui.SliderInt2("slider int2", vec4i, 0, 255, nil, flags)
ImGui.SeparatorText("3-wide")
ImGui.InputFloat3("input float3", vec4f)
ImGui.InputInt3("input int3", vec4i)
ImGui.DragFloat3("drag float3", vec4f, 0.01, 0.0, 1.0, nil, flags)
ImGui.DragInt3("drag int3", vec4i, 1, 0, 255, nil, flags)
ImGui.SliderFloat3("slider float3", vec4f, 0.0, 1.0, nil, flags)
ImGui.SliderInt3("slider int3", vec4i, 0, 255, nil, flags)
ImGui.SeparatorText("4-wide")
ImGui.InputFloat4("input float4", vec4f)
ImGui.InputInt4("input int4", vec4i)
ImGui.DragFloat4("drag float4", vec4f, 0.01, 0.0, 1.0, nil, flags)
ImGui.DragInt4("drag int4", vec4i, 1, 0, 255, nil, flags)
ImGui.SliderFloat4("slider float4", vec4f, 0.0, 1.0, nil, flags)
ImGui.SliderInt4("slider int4", vec4i, 0, 255, nil, flags)
ImGui.SeparatorText("Ranges")
s.begin, s["end"] = ImGui.DragFloatRange2("range float", s.begin, s["end"], 0.25, 0.0, 100.0, "Min: %.1f %%", "Max: %.1f %%", 1536 --[[ImGuiSliderFlags.AlwaysClamp]])
s.begin_i, s.end_i = ImGui.DragIntRange2("range int", s.begin_i, s.end_i, 5, 0, 1000, "Min: %d units", "Max: %d units")
s.begin_i, s.end_i = ImGui.DragIntRange2("range int (no bounds)", s.begin_i, s.end_i, 5, 0, 0, "Min: %d units", "Max: %d units")
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsPlotting()
-------------------------------------------------------------------------------
S.DemoWindowWidgetsPlotting = {
animate = true,
arr = { 0.6, 0.1, 1.0, 0.5, 0.92, 0.1, 0.2 },
values = {}, -- 90 zeros, filled below
values_offset = 0,
refresh_time = 0.0,
phase = 0.0,
func_type = 0, display_count = 70,
}
for n = 1, 90 do S.DemoWindowWidgetsPlotting.values[n] = 0.0 end
-- Use functions to generate output
-- [Port] struct Funcs hoisted out of the function to avoid per-frame closures.
-- Plot getters receive a 1-based index in this port: convert back to upstream's 0-based i.
local DemoPlottingFuncs = {
Sin = function(_, idx) local i = idx - 1; return math.sin(i * 0.1) end,
Saw = function(_, idx) local i = idx - 1; if bit32.band(i, 1) ~= 0 then return 1.0 end return -1.0 end,
}
function DemoWindowWidgetsPlotting()
local s = S.DemoWindowWidgetsPlotting
-- Plot/Graph widgets are not very good.
-- Consider using a third-party library such as ImPlot: https://github.com/epezent/implot
-- (see others https://github.com/ocornut/imgui/wiki/Useful-Extensions)
if ImGui.TreeNode("Plotting") then
IMGUI_DEMO_MARKER("Widgets/Plotting")
ImGui.Text("Need better plotting and graphing? Consider using ImPlot:")
ImGui.TextLinkOpenURL("https://github.com/epezent/implot")
ImGui.Separator()
_, s.animate = ImGui.Checkbox("Animate", s.animate)
-- Plot as lines and plot as histogram
local arr = s.arr
ImGui.PlotLines("Frame Times", arr, nil, #arr)
ImGui.PlotHistogram("Histogram", arr, nil, #arr, 0, nil, 0.0, 1.0, ImVec2(0, 80.0))
-- ImGui.SameLine(); HelpMarker("Consider using ImPlot instead!")
-- Fill an array of contiguous float values to plot
-- Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float
-- and the sizeof() of your structure in the "stride" parameter.
local values = s.values
if not s.animate or s.refresh_time == 0.0 then
s.refresh_time = ImGui.GetTime()
end
while s.refresh_time < ImGui.GetTime() do -- Create data at fixed 60 Hz rate for the demo
values[s.values_offset + 1] = math.cos(s.phase)
s.values_offset = (s.values_offset + 1) % #values
s.phase = s.phase + 0.10 * s.values_offset
s.refresh_time = s.refresh_time + 1.0 / 60.0
end
-- Plots can display overlay texts
-- (in this example, we will display an average value)
do
local average = 0.0
for n = 0, #values - 1 do
average = average + values[n + 1]
end
average = average / #values
local overlay = string.format("avg %f", average)
ImGui.PlotLines("Lines", values, nil, #values, s.values_offset, overlay, -1.0, 1.0, ImVec2(0, 80.0))
end
-- Use functions to generate output
-- FIXME: This is actually VERY awkward because current plot API only pass in indices.
-- We probably want an API passing floats and user provide sample rate/count.
local Funcs = DemoPlottingFuncs
ImGui.SeparatorText("Functions")
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 8)
s.func_type = ImGui.Combo("func", s.func_type, "Sin\0Saw\0")
ImGui.SameLine()
s.display_count = ImGui.SliderInt("Sample count", s.display_count, 1, 400)
local func = Funcs.Saw
if s.func_type == 0 then func = Funcs.Sin end
ImGui.PlotLines("Lines##2", func, nil, s.display_count, 0, nil, -1.0, 1.0, ImVec2(0, 80))
ImGui.PlotHistogram("Histogram##2", func, nil, s.display_count, 0, nil, -1.0, 1.0, ImVec2(0, 80))
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsProgressBars()
-------------------------------------------------------------------------------
S.DemoWindowWidgetsProgressBars = {
progress_accum = 0.0, progress_dir = 1.0,
}
function DemoWindowWidgetsProgressBars()
local s = S.DemoWindowWidgetsProgressBars
if ImGui.TreeNode("Progress Bars") then
IMGUI_DEMO_MARKER("Widgets/Progress Bars")
-- Animate a simple progress bar
s.progress_accum = s.progress_accum + s.progress_dir * 0.4 * ImGui.GetIO().DeltaTime
if s.progress_accum >= 1.1 then s.progress_accum = 1.1; s.progress_dir = s.progress_dir * -1.0 end
if s.progress_accum <= -0.1 then s.progress_accum = -0.1; s.progress_dir = s.progress_dir * -1.0 end
local progress = ImClamp(s.progress_accum, 0.0, 1.0)
-- Typically we would use ImVec2(-1.0,0.0) or ImVec2(-FLT_MIN,0.0) to use all available width,
-- or ImVec2(width,0.0) for a specified width. ImVec2(0.0,0.0) uses ItemWidth.
ImGui.ProgressBar(progress, ImVec2(0.0, 0.0))
ImGui.SameLine(0.0, ImGui.GetStyle().ItemInnerSpacing.x)
ImGui.Text("Progress Bar")
local buf = string.format("%d/%d", math.floor(progress * 1753), 1753)
ImGui.ProgressBar(progress, ImVec2(0.0, 0.0), buf)
-- Pass an animated negative value, e.g. -1.0 * ImGui.GetTime() is the recommended value.
-- Adjust the factor if you want to adjust the animation speed.
ImGui.ProgressBar(-1.0 * ImGui.GetTime(), ImVec2(0.0, 0.0), "Searching..")
ImGui.SameLine(0.0, ImGui.GetStyle().ItemInnerSpacing.x)
ImGui.Text("Indeterminate")
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsQueryingStatuses()
-------------------------------------------------------------------------------
-- [Port] C++ passes bools to "%d"; string.format needs numbers.
local function DemoBoolToInt(b) if b then return 1 end return 0 end
S.DemoWindowWidgetsQueryingStatuses = {
item_type = 4,
item_disabled = false,
item_mixedvalue = false,
liveedit_flags_override = false,
liveedit_flags = 0,
b = false,
col4f = { 1.0, 0.5, 0.0, 1.0 },
str = ImGui.NewTextBuffer("", 16),
current = 1, -- item_type == 14
current_2 = 1, -- item_type == 15
embed_all_inside_a_child_window = false,
test_window = false,
}
function DemoWindowWidgetsQueryingStatuses()
local s = S.DemoWindowWidgetsQueryingStatuses
local B = DemoBoolToInt
if ImGui.TreeNode("Querying Item Status (Edited/Active/Hovered etc.)") then
IMGUI_DEMO_MARKER("Widgets/Querying Item Status (Edited,Active,Hovered etc.)")
-- Select an item type
local item_names =
{
"Text", "Button", "Button (w/ repeat)", "Checkbox", "SliderFloat", "InputText", "InputTextMultiline", "InputFloat",
"InputFloat3", "ColorEdit4", "Selectable", "MenuItem", "TreeNode", "TreeNode (w/ double-click)", "Combo", "ListBox"
}
s.item_type = ImGui.Combo("Item Type", s.item_type, item_names, #item_names, #item_names)
ImGui.SameLine()
HelpMarker("Testing how various types of items are interacting with the IsItemXXX functions. Note that the bool return value of most ImGui function is generally equivalent to calling ImGui::IsItemHovered().")
_, s.item_disabled = ImGui.Checkbox("Item Disabled", s.item_disabled)
_, s.item_mixedvalue = ImGui.Checkbox("Item MixedValue", s.item_mixedvalue)
_, s.liveedit_flags_override = ImGui.Checkbox("Override LiveEdit:", s.liveedit_flags_override)
ImGui.SameLine()
if not s.liveedit_flags_override then
s.liveedit_flags = ImGui.GetItemFlags()
end
ImGui.BeginDisabled(s.liveedit_flags_override == false)
_, s.liveedit_flags = ImGui.CheckboxFlags("_LiveEditOnInput", s.liveedit_flags, 384 --[[ImGuiItemFlags.LiveEditOnInput]])
ImGui.SameLine()
_, s.liveedit_flags = ImGui.CheckboxFlags("_LiveEditOnInputText", s.liveedit_flags, 128 --[[ImGuiItemFlags.LiveEditOnInputText]])
ImGui.SameLine()
_, s.liveedit_flags = ImGui.CheckboxFlags("_LiveEditOnInputScalar", s.liveedit_flags, 256 --[[ImGuiItemFlags.LiveEditOnInputScalar]])
ImGui.EndDisabled()
if s.liveedit_flags_override then
ImGui.PushItemFlag(128 --[[ImGuiItemFlags.LiveEditOnInputText]], bit32.band(s.liveedit_flags, 128 --[[ImGuiItemFlags.LiveEditOnInputText]]) ~= 0)
ImGui.PushItemFlag(256 --[[ImGuiItemFlags.LiveEditOnInputScalar]], bit32.band(s.liveedit_flags, 256 --[[ImGuiItemFlags.LiveEditOnInputScalar]]) ~= 0)
end
-- Submit selected items so we can query their status in the code following it.
local ret = false
local item_type = s.item_type
local col4f = s.col4f
if s.item_disabled then
ImGui.BeginDisabled(true)
end
if s.item_mixedvalue then
ImGui.PushItemFlag(4096 --[[ImGuiItemFlags.MixedValue]], true)
end
if item_type == 0 then ImGui.Text("ITEM: Text") end -- Testing text items with no identifier/interaction
if item_type == 1 then ret = ImGui.Button("ITEM: Button") end -- Testing button
if item_type == 2 then ImGui.PushItemFlag(8 --[[ImGuiItemFlags.ButtonRepeat]], true); ret = ImGui.Button("ITEM: Button"); ImGui.PopItemFlag() end -- Testing button (with repeater)
if item_type == 3 then ret, s.b = ImGui.Checkbox("ITEM: Checkbox", s.b) end -- Testing checkbox
if item_type == 4 then col4f[1], ret = ImGui.SliderFloat("ITEM: SliderFloat", col4f[1], 0.0, 1.0) end -- Testing basic item
if item_type == 5 then ret = ImGui.InputText("ITEM: InputText", s.str, 16) end -- Testing input text (which handles tabbing)
if item_type == 6 then ret = ImGui.InputTextMultiline("ITEM: InputTextMultiline", s.str, 16) end -- Testing input text (which uses a child window)
if item_type == 7 then col4f[1], ret = ImGui.InputFloat("ITEM: InputFloat", col4f[1], 1.0) end -- Testing +/- buttons on scalar input
if item_type == 8 then ret = ImGui.InputFloat3("ITEM: InputFloat3", col4f) end -- Testing multi-component items (IsItemXXX flags are reported merged)
if item_type == 9 then ret = ImGui.ColorEdit4("ITEM: ColorEdit4", col4f) end -- Testing multi-component items (IsItemXXX flags are reported merged)
if item_type == 10 then ret = ImGui.Selectable("ITEM: Selectable") end -- Testing selectable item
if item_type == 11 then ret = ImGui.MenuItem("ITEM: MenuItem") end -- Testing menu item (they use ImGuiButtonFlags_PressedOnRelease button policy)
if item_type == 12 then ret = ImGui.TreeNode("ITEM: TreeNode"); if ret then ImGui.TreePop() end end -- Testing tree node
if item_type == 13 then ret = ImGui.TreeNodeEx("ITEM: TreeNode w/ ImGuiTreeNodeFlags_OpenOnDoubleClick", bit32.bor(64 --[[ImGuiTreeNodeFlags.OpenOnDoubleClick]], 8 --[[ImGuiTreeNodeFlags.NoTreePushOnOpen]])) end -- Testing tree node with ImGuiButtonFlags_PressedOnDoubleClick button policy.
if item_type == 14 then local items = { "Apple", "Banana", "Cherry", "Kiwi" }; s.current, ret = ImGui.Combo("ITEM: Combo", s.current, items, #items) end
if item_type == 15 then local items = { "Apple", "Banana", "Cherry", "Kiwi" }; s.current_2, ret = ImGui.ListBox("ITEM: ListBox", s.current_2, items, #items, #items) end
local hovered_delay_none = ImGui.IsItemHovered()
local hovered_delay_stationary = ImGui.IsItemHovered(8192 --[[ImGuiHoveredFlags.Stationary]])
local hovered_delay_short = ImGui.IsItemHovered(32768 --[[ImGuiHoveredFlags.DelayShort]])
local hovered_delay_normal = ImGui.IsItemHovered(65536 --[[ImGuiHoveredFlags.DelayNormal]])
local hovered_delay_tooltip = ImGui.IsItemHovered(4096 --[[ImGuiHoveredFlags.ForTooltip]]) -- = Normal + Stationary
-- Display the values of IsItemHovered() and other common item state functions.
-- Note that the ImGuiHoveredFlags_XXX flags can be combined.
-- Because BulletText is an item itself and that would affect the output of IsItemXXX functions,
-- we query every state in a single call to avoid storing them and to simplify the code.
ImGui.BulletText(
"Return value = %d\n" ..
"IsItemFocused() = %d\n" ..
"IsItemHovered() = %d\n" ..
"IsItemHovered(_AllowWhenBlockedByPopup) = %d\n" ..
"IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n" ..
"IsItemHovered(_AllowWhenOverlappedByItem) = %d\n" ..
"IsItemHovered(_AllowWhenOverlappedByWindow) = %d\n" ..
"IsItemHovered(_AllowWhenDisabled) = %d\n" ..
"IsItemHovered(_RectOnly) = %d\n" ..
"IsItemActive() = %d\n" ..
"IsItemEdited() = %d\n" ..
"IsItemActivated() = %d\n" ..
"IsItemDeactivated() = %d\n" ..
"IsItemDeactivatedAfterEdit() = %d\n" ..
"IsItemVisible() = %d\n" ..
"IsItemClicked() = %d\n" ..
"IsItemToggledOpen() = %d\n" ..
"GetItemRectMin() = (%.1f, %.1f)\n" ..
"GetItemRectMax() = (%.1f, %.1f)\n" ..
"GetItemRectSize() = (%.1f, %.1f)",
B(ret),
B(ImGui.IsItemFocused()),
B(ImGui.IsItemHovered()),
B(ImGui.IsItemHovered(32 --[[ImGuiHoveredFlags.AllowWhenBlockedByPopup]])),
B(ImGui.IsItemHovered(128 --[[ImGuiHoveredFlags.AllowWhenBlockedByActiveItem]])),
B(ImGui.IsItemHovered(256 --[[ImGuiHoveredFlags.AllowWhenOverlappedByItem]])),
B(ImGui.IsItemHovered(512 --[[ImGuiHoveredFlags.AllowWhenOverlappedByWindow]])),
B(ImGui.IsItemHovered(1024 --[[ImGuiHoveredFlags.AllowWhenDisabled]])),
B(ImGui.IsItemHovered(928 --[[ImGuiHoveredFlags.RectOnly]])),
B(ImGui.IsItemActive()),
B(ImGui.IsItemEdited()),
B(ImGui.IsItemActivated()),
B(ImGui.IsItemDeactivated()),
B(ImGui.IsItemDeactivatedAfterEdit()),
B(ImGui.IsItemVisible()),
B(ImGui.IsItemClicked()),
B(ImGui.IsItemToggledOpen()),
ImGui.GetItemRectMin().x, ImGui.GetItemRectMin().y,
ImGui.GetItemRectMax().x, ImGui.GetItemRectMax().y,
ImGui.GetItemRectSize().x, ImGui.GetItemRectSize().y
)
ImGui.BulletText(
"with Hovering Delay or Stationary test:\n" ..
"IsItemHovered() = %d\n" ..
"IsItemHovered(_Stationary) = %d\n" ..
"IsItemHovered(_DelayShort) = %d\n" ..
"IsItemHovered(_DelayNormal) = %d\n" ..
"IsItemHovered(_Tooltip) = %d",
B(hovered_delay_none), B(hovered_delay_stationary), B(hovered_delay_short), B(hovered_delay_normal), B(hovered_delay_tooltip))
if s.liveedit_flags_override then
ImGui.PopItemFlag()
ImGui.PopItemFlag()
end
if s.item_mixedvalue then
ImGui.PopItemFlag()
end
if s.item_disabled then
ImGui.EndDisabled()
end
local buf = ImGui.NewTextBuffer("", 1)
ImGui.InputText("unused", buf, 1, 512 --[[ImGuiInputTextFlags.ReadOnly]])
ImGui.SameLine()
HelpMarker("This widget is only here to be able to tab-out of the widgets above and see e.g. Deactivated() status.")
ImGui.TreePop()
end
if ImGui.TreeNode("Querying Window Status (Focused/Hovered etc.)") then
IMGUI_DEMO_MARKER("Widgets/Querying Window Status (Focused,Hovered etc.)")
_, s.embed_all_inside_a_child_window = ImGui.Checkbox("Embed everything inside a child window for testing _RootWindow flag.", s.embed_all_inside_a_child_window)
if s.embed_all_inside_a_child_window then
ImGui.BeginChild("outer_child", ImVec2(0, ImGui.GetFontSize() * 20.0), 1 --[[ImGuiChildFlags.Borders]])
end
-- Testing IsWindowFocused() function with its various flags.
ImGui.BulletText(
"IsWindowFocused() = %d\n" ..
"IsWindowFocused(_ChildWindows) = %d\n" ..
"IsWindowFocused(_ChildWindows|_NoPopupHierarchy) = %d\n" ..
"IsWindowFocused(_ChildWindows|_DockHierarchy) = %d\n" ..
"IsWindowFocused(_ChildWindows|_RootWindow) = %d\n" ..
"IsWindowFocused(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" ..
"IsWindowFocused(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n" ..
"IsWindowFocused(_RootWindow) = %d\n" ..
"IsWindowFocused(_RootWindow|_NoPopupHierarchy) = %d\n" ..
"IsWindowFocused(_RootWindow|_DockHierarchy) = %d\n" ..
"IsWindowFocused(_AnyWindow) = %d\n",
B(ImGui.IsWindowFocused()),
B(ImGui.IsWindowFocused(1 --[[ImGuiFocusedFlags.ChildWindows]])),
B(ImGui.IsWindowFocused(bit32.bor(1 --[[ImGuiFocusedFlags.ChildWindows]], 8 --[[ImGuiFocusedFlags.NoPopupHierarchy]]))),
B(ImGui.IsWindowFocused(bit32.bor(1 --[[ImGuiFocusedFlags.ChildWindows]], 16 --[[ImGuiFocusedFlags.DockHierarchy]]))),
B(ImGui.IsWindowFocused(bit32.bor(1 --[[ImGuiFocusedFlags.ChildWindows]], 2 --[[ImGuiFocusedFlags.RootWindow]]))),
B(ImGui.IsWindowFocused(bit32.bor(1 --[[ImGuiFocusedFlags.ChildWindows]], 2 --[[ImGuiFocusedFlags.RootWindow]], 8 --[[ImGuiFocusedFlags.NoPopupHierarchy]]))),
B(ImGui.IsWindowFocused(bit32.bor(1 --[[ImGuiFocusedFlags.ChildWindows]], 2 --[[ImGuiFocusedFlags.RootWindow]], 16 --[[ImGuiFocusedFlags.DockHierarchy]]))),
B(ImGui.IsWindowFocused(2 --[[ImGuiFocusedFlags.RootWindow]])),
B(ImGui.IsWindowFocused(bit32.bor(2 --[[ImGuiFocusedFlags.RootWindow]], 8 --[[ImGuiFocusedFlags.NoPopupHierarchy]]))),
B(ImGui.IsWindowFocused(bit32.bor(2 --[[ImGuiFocusedFlags.RootWindow]], 16 --[[ImGuiFocusedFlags.DockHierarchy]]))),
B(ImGui.IsWindowFocused(4 --[[ImGuiFocusedFlags.AnyWindow]])))
-- Testing IsWindowHovered() function with its various flags.
ImGui.BulletText(
"IsWindowHovered() = %d\n" ..
"IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n" ..
"IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n" ..
"IsWindowHovered(_ChildWindows) = %d\n" ..
"IsWindowHovered(_ChildWindows|_NoPopupHierarchy) = %d\n" ..
"IsWindowHovered(_ChildWindows|_DockHierarchy) = %d\n" ..
"IsWindowHovered(_ChildWindows|_RootWindow) = %d\n" ..
"IsWindowHovered(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" ..
"IsWindowHovered(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n" ..
"IsWindowHovered(_RootWindow) = %d\n" ..
"IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n" ..
"IsWindowHovered(_RootWindow|_DockHierarchy) = %d\n" ..
"IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n" ..
"IsWindowHovered(_AnyWindow) = %d\n" ..
"IsWindowHovered(_Stationary) = %d\n",
B(ImGui.IsWindowHovered()),
B(ImGui.IsWindowHovered(32 --[[ImGuiHoveredFlags.AllowWhenBlockedByPopup]])),
B(ImGui.IsWindowHovered(128 --[[ImGuiHoveredFlags.AllowWhenBlockedByActiveItem]])),
B(ImGui.IsWindowHovered(1 --[[ImGuiHoveredFlags.ChildWindows]])),
B(ImGui.IsWindowHovered(bit32.bor(1 --[[ImGuiHoveredFlags.ChildWindows]], 8 --[[ImGuiHoveredFlags.NoPopupHierarchy]]))),
B(ImGui.IsWindowHovered(bit32.bor(1 --[[ImGuiHoveredFlags.ChildWindows]], 16 --[[ImGuiHoveredFlags.DockHierarchy]]))),
B(ImGui.IsWindowHovered(bit32.bor(1 --[[ImGuiHoveredFlags.ChildWindows]], 2 --[[ImGuiHoveredFlags.RootWindow]]))),
B(ImGui.IsWindowHovered(bit32.bor(1 --[[ImGuiHoveredFlags.ChildWindows]], 2 --[[ImGuiHoveredFlags.RootWindow]], 8 --[[ImGuiHoveredFlags.NoPopupHierarchy]]))),
B(ImGui.IsWindowHovered(bit32.bor(1 --[[ImGuiHoveredFlags.ChildWindows]], 2 --[[ImGuiHoveredFlags.RootWindow]], 16 --[[ImGuiHoveredFlags.DockHierarchy]]))),
B(ImGui.IsWindowHovered(2 --[[ImGuiHoveredFlags.RootWindow]])),
B(ImGui.IsWindowHovered(bit32.bor(2 --[[ImGuiHoveredFlags.RootWindow]], 8 --[[ImGuiHoveredFlags.NoPopupHierarchy]]))),
B(ImGui.IsWindowHovered(bit32.bor(2 --[[ImGuiHoveredFlags.RootWindow]], 16 --[[ImGuiHoveredFlags.DockHierarchy]]))),
B(ImGui.IsWindowHovered(bit32.bor(1 --[[ImGuiHoveredFlags.ChildWindows]], 32 --[[ImGuiHoveredFlags.AllowWhenBlockedByPopup]]))),
B(ImGui.IsWindowHovered(4 --[[ImGuiHoveredFlags.AnyWindow]])),
B(ImGui.IsWindowHovered(8192 --[[ImGuiHoveredFlags.Stationary]])))
ImGui.BeginChild("child", ImVec2(0, 50), 1 --[[ImGuiChildFlags.Borders]])
ImGui.Text("This is another child window for testing the _ChildWindows flag.")
ImGui.EndChild()
if s.embed_all_inside_a_child_window then
ImGui.EndChild()
end
-- Calling IsItemHovered() after begin returns the hovered status of the title bar.
-- This is useful in particular if you want to create a context menu associated to the title bar of a window.
-- This will also work when docked into a Tab (the Tab replace the Title Bar and guarantee the same properties).
_, s.test_window = ImGui.Checkbox("Hovered/Active tests after Begin() for title bar testing", s.test_window)
if s.test_window then
-- FIXME-DOCK: This window cannot be docked within the ImGui Demo window, this will cause a feedback loop and get them stuck.
-- Could we fix this through an ImGuiWindowClass feature? Or an API call to tag our parent as "don't skip items"?
s.test_window = ImGui.Begin("Title bar Hovered/Active tests", s.test_window)
if ImGui.BeginPopupContextItem() then -- <-- This is using IsItemHovered()
if ImGui.MenuItem("Close") then s.test_window = false end
ImGui.EndPopup()
end
ImGui.Text(
"IsItemHovered() after begin = %d (== is title bar hovered)\n" ..
"IsItemActive() after begin = %d (== is window being clicked/moved)\n",
B(ImGui.IsItemHovered()), B(ImGui.IsItemActive()))
ImGui.End()
end
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsSelectables()
-------------------------------------------------------------------------------
S.DemoWindowWidgetsSelectables = {
selection = { false, true, false, false, false }, -- Basic
selected_multi = { false, false, false }, -- Multiple items on the same line (1)
checked = { false, false, false, false, false }, -- Multiple items on the same line (2)
selected_n = 0,
selected_tables = { false, false, false, false, false, false, false, false, false, false }, -- In Tables
selected_grid = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }, -- Grid
selected_align = { true, false, true, false, true, false, true, false, true }, -- Alignment
}
-- [Port] The Selectable(label, bool* p_selected) overload doesn't exist in this port (Selectable returns pressed, selected
-- without toggling): written as `if ImGui.Selectable(label, sel) then sel = not sel end`.
function DemoWindowWidgetsSelectables()
local s = S.DemoWindowWidgetsSelectables
-- ImGui.SetNextItemOpen(true, ImGuiCond.Once)
if ImGui.TreeNode("Selectables") then
IMGUI_DEMO_MARKER("Widgets/Selectables")
-- Selectable() has 2 overloads:
-- - The one taking "bool selected" as a read-only selection information.
-- When Selectable() has been clicked it returns true and you can alter selection state accordingly.
-- - The one taking "bool* p_selected" as a read-write selection information (convenient in some cases)
-- The earlier is more flexible, as in real application your selection may be stored in many different ways
-- and not necessarily inside a bool value (e.g. in flags within objects, as an external list, etc).
IMGUI_DEMO_MARKER("Widgets/Selectables/Basic")
if ImGui.TreeNode("Basic") then
local selection = s.selection
if ImGui.Selectable("1. I am selectable", selection[1]) then selection[1] = not selection[1] end
if ImGui.Selectable("2. I am selectable", selection[2]) then selection[2] = not selection[2] end
if ImGui.Selectable("3. I am selectable", selection[3]) then selection[3] = not selection[3] end
if ImGui.Selectable("4. I am double clickable", selection[4], 4 --[[ImGuiSelectableFlags.AllowDoubleClick]]) then
if ImGui.IsMouseDoubleClicked(0) then
selection[4] = not selection[4]
end
end
ImGui.TreePop()
end
IMGUI_DEMO_MARKER("Widgets/Selectables/Rendering more items on the same line")
if ImGui.TreeNode("Multiple items on the same line") then
IMGUI_DEMO_MARKER("Widgets/Selectables/Multiple items on the same line")
-- - Using SetNextItemAllowOverlap()
-- - Using the Selectable() override that takes "bool* p_selected" parameter, the bool value is toggled automatically.
do
local selected = s.selected_multi
ImGui.SetNextItemAllowOverlap(); if ImGui.Selectable("main.c", selected[1]) then selected[1] = not selected[1] end; ImGui.SameLine(); ImGui.SmallButton("Link 1")
ImGui.SetNextItemAllowOverlap(); if ImGui.Selectable("hello.cpp", selected[2]) then selected[2] = not selected[2] end; ImGui.SameLine(); ImGui.SmallButton("Link 2")
ImGui.SetNextItemAllowOverlap(); if ImGui.Selectable("hello.h", selected[3]) then selected[3] = not selected[3] end; ImGui.SameLine(); ImGui.SmallButton("Link 3")
end
-- (2)
-- - Using ImGuiSelectableFlags_AllowOverlap is a shortcut for calling SetNextItemAllowOverlap()
-- - No visible label, display contents inside the selectable bounds.
-- - We don't maintain actual selection in this example to keep things simple.
ImGui.Spacing()
do
local checked = s.checked
local color_marker_w = ImGui.CalcTextSize("x").x
for n = 0, 4 do
ImGui.PushID(n)
ImGui.AlignTextToFramePadding()
if ImGui.Selectable("##selectable", s.selected_n == n, 16 --[[ImGuiSelectableFlags.AllowOverlap]]) then
s.selected_n = n
end
ImGui.SameLine(0, 0)
_, checked[n + 1] = ImGui.Checkbox("##check", checked[n + 1])
ImGui.SameLine()
local color = ImVec4(bit32.band(n, 1) ~= 0 and 1.0 or 0.2, bit32.band(n, 2) ~= 0 and 1.0 or 0.2, 0.2, 1.0)
ImGui.ColorButton("##color", color, 64 --[[ImGuiColorEditFlags.NoTooltip]], ImVec2(color_marker_w, 0))
ImGui.SameLine()
ImGui.Text("Some label")
ImGui.PopID()
end
end
ImGui.TreePop()
end
if ImGui.TreeNode("In Tables") then
IMGUI_DEMO_MARKER("Widgets/Selectables/In Tables")
local selected = s.selected_tables
if ImGui.BeginTable("split1", 3, bit32.bor(1 --[[ImGuiTableFlags.Resizable]], 16 --[[ImGuiTableFlags.NoSavedSettings]], 1920 --[[ImGuiTableFlags.Borders]])) then
for i = 0, 9 do
local label = string.format("Item %d", i)
ImGui.TableNextColumn()
if ImGui.Selectable(label, selected[i + 1]) then selected[i + 1] = not selected[i + 1] end -- FIXME-TABLE: Selection overlap
end
ImGui.EndTable()
end
ImGui.Spacing()
if ImGui.BeginTable("split2", 3, bit32.bor(1 --[[ImGuiTableFlags.Resizable]], 16 --[[ImGuiTableFlags.NoSavedSettings]], 1920 --[[ImGuiTableFlags.Borders]])) then
for i = 0, 9 do
local label = string.format("Item %d", i)
ImGui.TableNextRow()
ImGui.TableNextColumn()
if ImGui.Selectable(label, selected[i + 1], 2 --[[ImGuiSelectableFlags.SpanAllColumns]]) then selected[i + 1] = not selected[i + 1] end
ImGui.TableNextColumn()
ImGui.Text("Some other contents")
ImGui.TableNextColumn()
ImGui.Text("123456")
end
ImGui.EndTable()
end
ImGui.TreePop()
end
if ImGui.TreeNode("Grid") then
IMGUI_DEMO_MARKER("Widgets/Selectables/Grid")
local selected = s.selected_grid
-- Add in a bit of silly fun...
local time = ImGui.GetTime()
local winning_state = true -- If all cells are selected...
for y = 1, 4 do for x = 1, 4 do if selected[y][x] == 0 then winning_state = false end end end -- [Port] memchr(selected, 0, sizeof(selected)) == NULL
if winning_state then
ImGui.PushStyleVar(39 --[[ImGuiStyleVar.SelectableTextAlign]], ImVec2(0.5 + 0.5 * math.cos(time * 2.0), 0.5 + 0.5 * math.sin(time * 3.0)))
end
local size = ImGui.CalcTextSize("Sailor").x
for y = 0, 3 do
for x = 0, 3 do
if x > 0 then
ImGui.SameLine()
end
ImGui.PushID(y * 4 + x)
if ImGui.Selectable("Sailor", selected[y + 1][x + 1] ~= 0, 0, ImVec2(size, size)) then
-- Toggle clicked cell + toggle neighbors
selected[y + 1][x + 1] = bit32.bxor(selected[y + 1][x + 1], 1)
if x > 0 then selected[y + 1][x] = bit32.bxor(selected[y + 1][x], 1) end
if x < 3 then selected[y + 1][x + 2] = bit32.bxor(selected[y + 1][x + 2], 1) end
if y > 0 then selected[y][x + 1] = bit32.bxor(selected[y][x + 1], 1) end
if y < 3 then selected[y + 2][x + 1] = bit32.bxor(selected[y + 2][x + 1], 1) end
end
ImGui.PopID()
end
end
if winning_state then
ImGui.PopStyleVar()
end
ImGui.TreePop()
end
if ImGui.TreeNode("Alignment") then
IMGUI_DEMO_MARKER("Widgets/Selectables/Alignment")
HelpMarker(
"By default, Selectables uses style.SelectableTextAlign but it can be overridden on a per-item " ..
"basis using PushStyleVar(). You'll probably want to always keep your default situation to " ..
"left-align otherwise it becomes difficult to layout multiple items on a same line")
local selected = s.selected_align
local size = ImGui.CalcTextSize("(1.0,1.0)").x
for y = 0, 2 do
for x = 0, 2 do
local alignment = ImVec2(x / 2.0, y / 2.0)
local name = string.format("(%.1f,%.1f)", alignment.x, alignment.y)
if x > 0 then ImGui.SameLine() end
ImGui.PushStyleVar(39 --[[ImGuiStyleVar.SelectableTextAlign]], alignment)
local idx = 3 * y + x + 1
if ImGui.Selectable(name, selected[idx], 0 --[[ImGuiSelectableFlags.None]], ImVec2(size, size)) then selected[idx] = not selected[idx] end
ImGui.PopStyleVar()
end
end
ImGui.TreePop()
end
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsSelectionAndMultiSelect()
-------------------------------------------------------------------------------
-- Multi-selection demos
-- Also read: https://github.com/ocornut/imgui/wiki/Multi-Select
-------------------------------------------------------------------------------
ExampleNames =
{
"Artichoke", "Arugula", "Asparagus", "Avocado", "Bamboo Shoots", "Bean Sprouts", "Beans", "Beet", "Belgian Endive", "Bell Pepper",
"Bitter Gourd", "Bok Choy", "Broccoli", "Brussels Sprouts", "Burdock Root", "Cabbage", "Calabash", "Capers", "Carrot", "Cassava",
"Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber"
}
-- Extra functions to add deletion support to ImGuiSelectionBasicStorage
-- [Port] "struct ExampleSelectionWithDeletion : ImGuiSelectionBasicStorage": an ImGuiSelectionBasicStorage() object whose
-- metatable chains to the library's ImGuiSelectionBasicStorage methods.
ExampleSelectionWithDeletionMT = {}
ExampleSelectionWithDeletionMT.__index = ExampleSelectionWithDeletionMT
function ExampleSelectionWithDeletion()
local self = ImGuiSelectionBasicStorage()
if getmetatable(ExampleSelectionWithDeletionMT) == nil then
setmetatable(ExampleSelectionWithDeletionMT, { __index = getmetatable(self) }) -- [Port] inherit ImGuiSelectionBasicStorage methods
end
return setmetatable(self, ExampleSelectionWithDeletionMT)
end
-- Find which item should be Focused after deletion.
-- Call _before_ item submission. Return an index in the before-deletion item list, your item loop should call SetKeyboardFocusHere() on it.
-- The subsequent ApplyDeletionPostLoop() code will use it to apply Selection.
-- - We cannot provide this logic in core Dear ImGui because we don't have access to selection data.
-- - We don't actually manipulate the ImVector<> here, only in ApplyDeletionPostLoop(), but using similar API for consistency and flexibility.
-- - Important: Deletion only works if the underlying ImGuiID for your items are stable: aka not depend on their index, but on e.g. item id/ptr.
-- FIXME-MULTISELECT: Doesn't take account of the possibility focus target will be moved during deletion. Need refocus or scroll offset.
function ExampleSelectionWithDeletionMT:ApplyDeletionPreLoop(ms_io, items_count)
if self.Size == 0 then
return -1
end
-- If focused item is not selected...
local focused_idx = ms_io.NavIdItem -- Index of currently focused item
if ms_io.NavIdSelected == false then -- This is merely a shortcut, == Contains(adapter->IndexToStorage(items, focused_idx))
ms_io.RangeSrcReset = true -- Request to recover RangeSrc from NavId next frame. Would be ok to reset even when NavIdSelected==true, but it would take an extra frame to recover RangeSrc when deleting a selected item.
return focused_idx -- Request to focus same item after deletion.
end
-- If focused item is selected: land on first unselected item after focused item.
for idx = focused_idx + 1, items_count - 1 do
if not self:Contains(self:GetStorageIdFromIndex(idx)) then
return idx
end
end
-- If focused item is selected: otherwise return last unselected item before focused item.
for idx = ImMin(focused_idx, items_count) - 1, 0, -1 do
if not self:Contains(self:GetStorageIdFromIndex(idx)) then
return idx
end
end
return -1
end
-- Rewrite item list (delete items) + update selection.
-- - Call after EndMultiSelect()
-- - We cannot provide this logic in core Dear ImGui because we don't have access to your items, nor to selection data.
-- [Port] items: ImVector (1-based .Data), swapped in place with the rebuilt list.
function ExampleSelectionWithDeletionMT:ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_select)
-- Rewrite item list (delete items) + convert old selection index (before deletion) to new selection index (after selection).
-- If NavId was not part of selection, we will stay on same item.
local new_items = ImVector()
new_items:reserve(items.Size - self.Size)
local item_next_idx_to_select = -1
for idx = 0, items.Size - 1 do
if not self:Contains(self:GetStorageIdFromIndex(idx)) then
new_items:push_back(items.Data[idx + 1])
end
if item_curr_idx_to_select == idx then
item_next_idx_to_select = new_items.Size - 1
end
end
items:swap(new_items)
-- Update selection
self:Clear()
if item_next_idx_to_select ~= -1 and ms_io.NavIdSelected then
self:SetItemSelected(self:GetStorageIdFromIndex(item_next_idx_to_select), true)
end
end
-- Example: Implement dual list box storage and interface
ExampleDualListBoxMT = {}
ExampleDualListBoxMT.__index = ExampleDualListBoxMT
function ExampleDualListBox()
local self = setmetatable({}, ExampleDualListBoxMT)
self.Items = { ImVector(), ImVector() } -- ID is index into ExampleName[] -- [Port] 1-based: Items[side + 1]
self.Selections = { ImGuiSelectionBasicStorage(), ImGuiSelectionBasicStorage() } -- Store ExampleItemId into selection -- [Port] 1-based: Selections[side + 1]
self.OptKeepSorted = true
return self
end
function ExampleDualListBoxMT:MoveAll(src, dst)
assert((src == 0 and dst == 1) or (src == 1 and dst == 0))
for _, item_id in self.Items[src + 1]:iter() do
self.Items[dst + 1]:push_back(item_id)
end
self.Items[src + 1]:clear()
self:SortItems(dst)
self.Selections[src + 1]:Swap(self.Selections[dst + 1])
self.Selections[src + 1]:Clear()
end
function ExampleDualListBoxMT:MoveSelected(src, dst)
local src_n = 0 -- [Port] while loop: the C++ for loop decrements src_n
while src_n < self.Items[src + 1].Size do
repeat
local item_id = self.Items[src + 1].Data[src_n + 1]
if not self.Selections[src + 1]:Contains(item_id) then
break -- continue
end
self.Items[src + 1]:erase(src_n + 1) -- FIXME-OPT: Could be implemented more optimally (rebuild src items and swap)
self.Items[dst + 1]:push_back(item_id)
src_n = src_n - 1
until true
src_n = src_n + 1
end
if self.OptKeepSorted then
self:SortItems(dst)
end
self.Selections[src + 1]:Swap(self.Selections[dst + 1])
self.Selections[src + 1]:Clear()
end
function ExampleDualListBoxMT:ApplySelectionRequests(ms_io, side)
-- In this example we store item id in selection (instead of item index)
self.Selections[side + 1].UserData = self.Items[side + 1].Data
self.Selections[side + 1].AdapterIndexToStorageId = function(self_, idx) local items = self_.UserData; return items[idx + 1] end
self.Selections[side + 1]:ApplyRequests(ms_io)
end
-- [Port] table.sort() comparator: returns 'lhs < rhs' instead of '*a - *b'
function ExampleDualListBoxMT.CompareItemsByValue(lhs, rhs)
return lhs < rhs
end
function ExampleDualListBoxMT:SortItems(n)
if self.Items[n + 1].Size > 0 then -- [Port] .Data is nil for an empty ImVector
table.sort(self.Items[n + 1].Data, ExampleDualListBoxMT.CompareItemsByValue) -- [Port] .Data holds exactly .Size items here (only push_back/erase/clear are used)
end
end
function ExampleDualListBoxMT:Show()
--if (ImGui::Checkbox("Sorted", &OptKeepSorted) && OptKeepSorted) { SortItems(0); SortItems(1); }
if ImGui.BeginTable("split", 3, 0 --[[ImGuiTableFlags.None]]) then
ImGui.TableSetupColumn("", 8 --[[ImGuiTableColumnFlags.WidthStretch]]) -- Left side
ImGui.TableSetupColumn("", 16 --[[ImGuiTableColumnFlags.WidthFixed]]) -- Buttons
ImGui.TableSetupColumn("", 8 --[[ImGuiTableColumnFlags.WidthStretch]]) -- Right side
ImGui.TableNextRow()
local request_move_selected = -1
local request_move_all = -1
local child_height_0 = 0.0
for side = 0, 1 do
-- FIXME-MULTISELECT: Dual List Box: Add context menus
-- FIXME-NAV: Using ImGuiWindowFlags_NavFlattened exhibit many issues.
local items = self.Items[side + 1]
local selection = self.Selections[side + 1]
ImGui.TableSetColumnIndex((side == 0) and 0 or 2)
ImGui.Text("%s (%d)", (side == 0) and "Available" or "Basket", items.Size)
-- Submit scrolling range to avoid glitches on moving/deletion
local items_height = ImGui.GetTextLineHeightWithSpacing()
ImGui.SetNextWindowContentSize(ImVec2(0.0, items.Size * items_height))
local child_visible
if side == 0 then
-- Left child is resizable
ImGui.SetNextWindowSizeConstraints(ImVec2(0.0, ImGui.GetFrameHeightWithSpacing() * 4), ImVec2(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]]))
child_visible = ImGui.BeginChild("0", ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], ImGui.GetFontSize() * 20), bit32.bor(128 --[[ImGuiChildFlags.FrameStyle]], 8 --[[ImGuiChildFlags.ResizeY]]))
child_height_0 = ImGui.GetWindowSize().y
else
-- Right child use same height as left one
child_visible = ImGui.BeginChild("1", ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], child_height_0), 128 --[[ImGuiChildFlags.FrameStyle]])
end
if child_visible then
local flags = 64 --[[ImGuiMultiSelectFlags.BoxSelect1d]]
local ms_io = ImGui.BeginMultiSelect(flags, selection.Size, items.Size)
self:ApplySelectionRequests(ms_io, side)
for item_n = 0, items.Size - 1 do
local item_id = items.Data[item_n + 1]
local item_is_selected = selection:Contains(item_id)
ImGui.SetNextItemSelectionUserData(item_n)
ImGui.Selectable(ExampleNames[item_id + 1], item_is_selected, 4 --[[ImGuiSelectableFlags.AllowDoubleClick]])
if ImGui.IsItemFocused() then
-- FIXME-MULTISELECT: Dual List Box: Transfer focus
if ImGui.IsKeyPressed(525 --[[ImGuiKey.Enter]]) or ImGui.IsKeyPressed(627 --[[ImGuiKey.KeypadEnter]]) then
request_move_selected = side
end
if ImGui.IsMouseDoubleClicked(0) then -- FIXME-MULTISELECT: Double-click on multi-selection?
request_move_selected = side
end
end
end
ms_io = ImGui.EndMultiSelect()
self:ApplySelectionRequests(ms_io, side)
end
ImGui.EndChild()
end
-- Buttons columns
ImGui.TableSetColumnIndex(1)
ImGui.NewLine()
--ImVec2 button_sz = { ImGui::CalcTextSize(">>").x + ImGui::GetStyle().FramePadding.x * 2.0f, ImGui::GetFrameHeight() + padding.y * 2.0f };
local button_sz = ImVec2(ImGui.GetFrameHeight(), ImGui.GetFrameHeight())
-- (Using BeginDisabled()/EndDisabled() works but feels distracting given how it is currently visualized)
if ImGui.Button(">>", button_sz) then
request_move_all = 0
end
if ImGui.Button(">", button_sz) then
request_move_selected = 0
end
if ImGui.Button("<", button_sz) then
request_move_selected = 1
end
if ImGui.Button("<<", button_sz) then
request_move_all = 1
end
-- Process requests
if request_move_all ~= -1 then
self:MoveAll(request_move_all, bit32.bxor(request_move_all, 1))
end
if request_move_selected ~= -1 then
self:MoveSelected(request_move_selected, bit32.bxor(request_move_selected, 1))
end
-- FIXME-MULTISELECT: Support action from outside
--[[
if (OptKeepSorted == false)
{
ImGui::NewLine();
if (ImGui::ArrowButton("MoveUp", ImGuiDir_Up)) {}
if (ImGui::ArrowButton("MoveDown", ImGuiDir_Down)) {}
}
]]
ImGui.EndTable()
end
end
-- [Port] struct ExampleTreeFuncs is local to DemoWindowWidgetsSelectionAndMultiSelect() upstream ("Multi-Select (trees)"):
-- hoisted to file level (global table) so its functions are not re-created every frame.
ExampleTreeFuncs = {}
function ExampleTreeFuncs.DrawNode(node, selection)
local tree_node_flags = bit32.bor(2048 --[[ImGuiTreeNodeFlags.SpanAvailWidth]], 128 --[[ImGuiTreeNodeFlags.OpenOnArrow]], 64 --[[ImGuiTreeNodeFlags.OpenOnDoubleClick]])
tree_node_flags = bit32.bor(tree_node_flags, 131072 --[[ImGuiTreeNodeFlags.NavLeftJumpsToParent]]) -- Enable pressing left to jump to parent
if #node.Childs == 0 then
tree_node_flags = bit32.bor(tree_node_flags, 512 --[[ImGuiTreeNodeFlags.Bullet]], 256 --[[ImGuiTreeNodeFlags.Leaf]])
end
if selection:Contains(node.UID) then
tree_node_flags = bit32.bor(tree_node_flags, 1 --[[ImGuiTreeNodeFlags.Selected]])
end
-- Using SetNextItemStorageID() to specify storage id, so we can easily peek into
-- the storage holding open/close stage, using our TreeNodeGetOpen/TreeNodeSetOpen() functions.
ImGui.SetNextItemSelectionUserData(node) -- [Port] the node table itself is the selection user data (C++ casts the pointer)
ImGui.SetNextItemStorageID(node.UID)
if ImGui.TreeNodeEx(node.Name, tree_node_flags) then
for _, child in ipairs(node.Childs) do
ExampleTreeFuncs.DrawNode(child, selection)
end
ImGui.TreePop()
elseif ImGui.IsItemToggledOpen() then
ExampleTreeFuncs.TreeCloseAndUnselectChildNodes(node, selection)
end
end
-- When closing a node: 1) close and unselect all child nodes, 2) select parent if any child was selected.
-- FIXME: This is currently handled by user logic but I'm hoping to eventually provide tree node
-- features to do this automatically, e.g. a ImGuiTreeNodeFlags_AutoCloseChildNodes etc.
function ExampleTreeFuncs.TreeCloseAndUnselectChildNodes(node, selection, depth)
if depth == nil then depth = 0 end
-- Recursive close (the test for depth == 0 is because we call this on a node that was just closed!)
local unselected_count = selection:Contains(node.UID) and 1 or 0
if depth == 0 or ImGui.TreeNodeGetOpen(node.UID) then
for _, child in ipairs(node.Childs) do
unselected_count = unselected_count + ExampleTreeFuncs.TreeCloseAndUnselectChildNodes(child, selection, depth + 1)
end
ImGui.TreeNodeSetOpen(node.UID, false)
end
-- Select root node if any of its child was selected, otherwise unselect
selection:SetItemSelected(node.UID, (depth == 0 and unselected_count > 0))
return unselected_count
end
-- Apply multi-selection requests
function ExampleTreeFuncs.ApplySelectionRequests(ms_io, tree, selection)
for _, req in ms_io.Requests:iter() do
if req.Type == 1 --[[ImGuiSelectionRequestType.SetAll]] then
if req.Selected then
ExampleTreeFuncs.TreeSetAllInOpenNodes(tree, selection, req.Selected)
else
selection:Clear()
end
elseif req.Type == 2 --[[ImGuiSelectionRequestType.SetRange]] then
local first_node = req.RangeFirstItem
local last_node = req.RangeLastItem
local node = first_node
while node ~= nil do
selection:SetItemSelected(node.UID, req.Selected)
node = ExampleTreeFuncs.TreeGetNextNodeInVisibleOrder(node, last_node)
end
end
end
end
function ExampleTreeFuncs.TreeSetAllInOpenNodes(node, selection, selected)
if node.Parent ~= nil then -- Root node isn't visible nor selectable in our scheme
selection:SetItemSelected(node.UID, selected)
end
if node.Parent == nil or ImGui.TreeNodeGetOpen(node.UID) then
for _, child in ipairs(node.Childs) do
ExampleTreeFuncs.TreeSetAllInOpenNodes(child, selection, selected)
end
end
end
-- Interpolate in *user-visible order* AND only *over opened nodes*.
-- If you have a sequential mapping tables (e.g. generated after a filter/search pass) this would be simpler.
-- Here the tricks are that:
-- - we store/maintain ExampleTreeNode::IndexInParent which allows implementing a linear iterator easily, without searches, without recursion.
-- this could be replaced by a search in parent, aka 'int index_in_parent = curr_node->Parent->Childs.find_index(curr_node)'
-- which would only be called when crossing from child to a parent, aka not too much.
-- - we call SetNextItemStorageID() before our TreeNode() calls with an ID which doesn't relate to UI stack,
-- making it easier to call TreeNodeGetOpen()/TreeNodeSetOpen() from any location.
function ExampleTreeFuncs.TreeGetNextNodeInVisibleOrder(curr_node, last_node)
-- Reached last node
if curr_node == last_node then
return nil
end
-- Recurse into childs. Query storage to tell if the node is open.
if #curr_node.Childs > 0 and ImGui.TreeNodeGetOpen(curr_node.UID) then
return curr_node.Childs[1]
end
-- Next sibling, then into our own parent
while curr_node.Parent ~= nil do
if curr_node.IndexInParent + 1 < #curr_node.Parent.Childs then
return curr_node.Parent.Childs[curr_node.IndexInParent + 1 + 1] -- [Port] Childs is 1-based, IndexInParent 0-based
end
curr_node = curr_node.Parent
end
return nil
end
S.DemoWindowWidgetsSelectionAndMultiSelect = {
single_select = { selected = -1 },
manual = { selection = { false, false, false, false, false } },
multi_select = { selection = ImGuiSelectionBasicStorage() },
with_clipper = { selection = ImGuiSelectionBasicStorage() },
with_deletion = { items = ImVector(), selection = ExampleSelectionWithDeletion(), items_next_id = 0 },
dual_list_box = { dlb = ExampleDualListBox() },
in_a_table = { selection = ImGuiSelectionBasicStorage() },
checkboxes = {
items = { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false },
flags = bit32.bor(ImGuiMultiSelectFlags.NoAutoSelect, ImGuiMultiSelectFlags.NoAutoClear, ImGuiMultiSelectFlags.ClearOnEscape),
},
multiple_scopes = {
selections_data = { ImGuiSelectionBasicStorage(), ImGuiSelectionBasicStorage(), ImGuiSelectionBasicStorage() }, -- [Port] 1-based
flags = bit32.bor(ImGuiMultiSelectFlags.ScopeRect, ImGuiMultiSelectFlags.ClearOnEscape), -- | ImGuiMultiSelectFlags_ClearOnClickVoid;
},
trees = { selection = ImGuiSelectionBasicStorage() },
advanced = {
use_clipper = true,
use_deletion = true,
use_drag_drop = true,
show_in_table = false,
show_color_button = true,
flags = bit32.bor(ImGuiMultiSelectFlags.ClearOnEscape, ImGuiMultiSelectFlags.BoxSelect1d),
widget_type = 0, -- WidgetType_Selectable
items = ImVector(),
items_next_id = 0,
selection = ExampleSelectionWithDeletion(),
request_deletion_from_menu = false, -- Queue deletion triggered from context menu
item_category_bufs = {}, -- [Port] read-only InputText() byte buffers, per ExampleNames[] entry
},
}
function DemoWindowWidgetsSelectionAndMultiSelect(demo_data)
local s = S.DemoWindowWidgetsSelectionAndMultiSelect
if ImGui.TreeNode("Selection State & Multi-Select") then
IMGUI_DEMO_MARKER("Widgets/Selection State & Multi-Select")
HelpMarker("Selections can be built using Selectable(), TreeNode() or other widgets. Selection state is owned by application code/data.")
ImGui.BulletText("Wiki page:")
ImGui.SameLine()
ImGui.TextLinkOpenURL("imgui/wiki/Multi-Select", "https://github.com/ocornut/imgui/wiki/Multi-Select")
-- Without any fancy API: manage single-selection yourself.
if ImGui.TreeNode("Single-Select") then
IMGUI_DEMO_MARKER("Widgets/Selection State/Single-Select")
local st = s.single_select
for n = 0, 4 do
local buf = string.format("Object %d", n)
if ImGui.Selectable(buf, st.selected == n) then
st.selected = n
end
end
ImGui.TreePop()
end
-- Demonstrate implementation a most-basic form of multi-selection manually
-- This doesn't support the Shift modifier which requires BeginMultiSelect()!
if ImGui.TreeNode("Multi-Select (manual/simplified, without BeginMultiSelect)") then
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (manual/simplified, without BeginMultiSelect)")
HelpMarker("Hold Ctrl and Click to select multiple items.")
local selection = s.manual.selection
for n = 0, 4 do
local buf = string.format("Object %d", n)
if ImGui.Selectable(buf, selection[n + 1]) then
if not ImGui.GetIO().KeyCtrl then -- Clear selection when Ctrl is not held
for i = 1, 5 do selection[i] = false end
end
selection[n + 1] = not selection[n + 1] -- Toggle current item
end
end
ImGui.TreePop()
end
-- Demonstrate handling proper multi-selection using the BeginMultiSelect/EndMultiSelect API.
-- Shift+Click w/ Ctrl and other standard features are supported.
-- We use the ImGuiSelectionBasicStorage helper which you may freely reimplement.
if ImGui.TreeNode("Multi-Select") then
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select")
ImGui.Text("Supported features:")
ImGui.BulletText("Keyboard navigation (arrows, page up/down, home/end, space).")
ImGui.BulletText("Ctrl modifier to preserve and toggle selection.")
ImGui.BulletText("Shift modifier for range selection.")
ImGui.BulletText("Ctrl+A to select all.")
ImGui.BulletText("Escape to clear selection.")
ImGui.BulletText("Click and drag to box-select.")
ImGui.Text("Tip: Use 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen.")
-- Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection
local ITEMS_COUNT = 50
local selection = s.multi_select.selection
ImGui.Text("Selection: %d/%d", selection.Size, ITEMS_COUNT)
-- The BeginChild() has no purpose for selection logic, other that offering a scrolling region.
if ImGui.BeginChild("##Basket", ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], ImGui.GetFontSize() * 20), bit32.bor(128 --[[ImGuiChildFlags.FrameStyle]], 8 --[[ImGuiChildFlags.ResizeY]])) then
local flags = bit32.bor(512 --[[ImGuiMultiSelectFlags.ClearOnEscape]], 64 --[[ImGuiMultiSelectFlags.BoxSelect1d]])
local ms_io = ImGui.BeginMultiSelect(flags, selection.Size, ITEMS_COUNT)
selection:ApplyRequests(ms_io)
for n = 0, ITEMS_COUNT - 1 do
local label = string.format("Object %05d: %s", n, ExampleNames[n % #ExampleNames + 1])
local item_is_selected = selection:Contains(n)
ImGui.SetNextItemSelectionUserData(n)
ImGui.Selectable(label, item_is_selected)
end
ms_io = ImGui.EndMultiSelect()
selection:ApplyRequests(ms_io)
end
ImGui.EndChild()
ImGui.TreePop()
end
-- Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect()
if ImGui.TreeNode("Multi-Select (with clipper)") then
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with clipper)")
-- Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection
local selection = s.with_clipper.selection
ImGui.Text("Added features:")
ImGui.BulletText("Using ImGuiListClipper.")
local ITEMS_COUNT = 10000
ImGui.Text("Selection: %d/%d", selection.Size, ITEMS_COUNT)
if ImGui.BeginChild("##Basket", ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], ImGui.GetFontSize() * 20), bit32.bor(128 --[[ImGuiChildFlags.FrameStyle]], 8 --[[ImGuiChildFlags.ResizeY]])) then
local flags = bit32.bor(512 --[[ImGuiMultiSelectFlags.ClearOnEscape]], 64 --[[ImGuiMultiSelectFlags.BoxSelect1d]])
local ms_io = ImGui.BeginMultiSelect(flags, selection.Size, ITEMS_COUNT)
selection:ApplyRequests(ms_io)
local clipper = ImGuiListClipper()
clipper:Begin(ITEMS_COUNT)
if ms_io.RangeSrcItem ~= -1 then
clipper:IncludeItemByIndex(ms_io.RangeSrcItem) -- Ensure RangeSrc item is not clipped.
end
while clipper:Step() do
for n = clipper.DisplayStart, clipper.DisplayEnd - 1 do
local label = string.format("Object %05d: %s", n, ExampleNames[n % #ExampleNames + 1])
local item_is_selected = selection:Contains(n)
ImGui.SetNextItemSelectionUserData(n)
ImGui.Selectable(label, item_is_selected)
end
end
ms_io = ImGui.EndMultiSelect()
selection:ApplyRequests(ms_io)
end
ImGui.EndChild()
ImGui.TreePop()
end
-- Demonstrate dynamic item list + deletion support using the BeginMultiSelect/EndMultiSelect API.
-- In order to support Deletion without any glitches you need to:
-- - (1) If items are submitted in their own scrolling area, submit contents size SetNextWindowContentSize() ahead of time to prevent one-frame readjustment of scrolling.
-- - (2) Items needs to have persistent ID Stack identifier = ID needs to not depends on their index. PushID(index) = KO. PushID(item_id) = OK. This is in order to focus items reliably after a selection.
-- - (3) BeginXXXX process
-- - (4) Focus process
-- - (5) EndXXXX process
if ImGui.TreeNode("Multi-Select (with deletion)") then
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with deletion)")
-- Storing items data separately from selection data.
-- (you may decide to store selection data inside your item (aka intrusive storage) if you don't need multiple views over same items)
-- Use a custom selection.Adapter: store item identifier in Selection (instead of index)
local st = s.with_deletion
local items = st.items
local selection = st.selection
selection.UserData = items
selection.AdapterIndexToStorageId = function(self, idx) local p_items = self.UserData; return p_items.Data[idx + 1] end -- Index -> ID
ImGui.Text("Added features:")
ImGui.BulletText("Dynamic list with Delete key support.")
ImGui.Text("Selection size: %d/%d", selection.Size, items.Size)
-- Initialize default list with 50 items + button to add/remove items.
if st.items_next_id == 0 then
for _ = 0, 49 do
items:push_back(st.items_next_id); st.items_next_id = st.items_next_id + 1
end
end
if ImGui.SmallButton("Add 20 items") then for _ = 0, 19 do items:push_back(st.items_next_id); st.items_next_id = st.items_next_id + 1 end end
ImGui.SameLine()
if ImGui.SmallButton("Remove 20 items") then for _ = ImMin(20, items.Size), 1, -1 do selection:SetItemSelected(items:back(), false); items:pop_back() end end
-- (1) Extra to support deletion: Submit scrolling range to avoid glitches on deletion
local items_height = ImGui.GetTextLineHeightWithSpacing()
ImGui.SetNextWindowContentSize(ImVec2(0.0, items.Size * items_height))
if ImGui.BeginChild("##Basket", ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], ImGui.GetFontSize() * 20), bit32.bor(128 --[[ImGuiChildFlags.FrameStyle]], 8 --[[ImGuiChildFlags.ResizeY]])) then
local flags = bit32.bor(512 --[[ImGuiMultiSelectFlags.ClearOnEscape]], 64 --[[ImGuiMultiSelectFlags.BoxSelect1d]])
local ms_io = ImGui.BeginMultiSelect(flags, selection.Size, items.Size)
selection:ApplyRequests(ms_io)
local want_delete = ImGui.Shortcut(522 --[[ImGuiKey.Delete]], 1 --[[ImGuiInputFlags.Repeat]]) and (selection.Size > 0)
local item_curr_idx_to_focus = -1
if want_delete then item_curr_idx_to_focus = selection:ApplyDeletionPreLoop(ms_io, items.Size) end
for n = 0, items.Size - 1 do
local item_id = items.Data[n + 1]
local label = string.format("Object %05d: %s", item_id, ExampleNames[item_id % #ExampleNames + 1]) -- [Port] %05u -> %05d
local item_is_selected = selection:Contains(item_id)
ImGui.SetNextItemSelectionUserData(n)
ImGui.Selectable(label, item_is_selected)
if item_curr_idx_to_focus == n then
ImGui.SetKeyboardFocusHere(-1)
end
end
-- Apply multi-select requests
ms_io = ImGui.EndMultiSelect()
selection:ApplyRequests(ms_io)
if want_delete then
selection:ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_focus)
end
end
ImGui.EndChild()
ImGui.TreePop()
end
-- Implement a Dual List Box (#6648)
if ImGui.TreeNode("Multi-Select (dual list box)") then
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (dual list box)")
-- Init default state
local dlb = s.dual_list_box.dlb
if dlb.Items[1].Size == 0 and dlb.Items[2].Size == 0 then
for item_id = 0, #ExampleNames - 1 do
dlb.Items[1]:push_back(item_id)
end
end
-- Show
dlb:Show()
ImGui.TreePop()
end
-- Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect()
if ImGui.TreeNode("Multi-Select (in a table)") then
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (in a table)")
local selection = s.in_a_table.selection
local ITEMS_COUNT = 10000
ImGui.Text("Selection: %d/%d", selection.Size, ITEMS_COUNT)
if ImGui.BeginTable("##Basket", 2, bit32.bor(33554432 --[[ImGuiTableFlags.ScrollY]], 64 --[[ImGuiTableFlags.RowBg]], 1280 --[[ImGuiTableFlags.BordersOuter]]), ImVec2(0.0, ImGui.GetFontSize() * 20)) then
ImGui.TableSetupColumn("Object")
ImGui.TableSetupColumn("Action")
ImGui.TableSetupScrollFreeze(0, 1)
ImGui.TableHeadersRow()
local flags = bit32.bor(512 --[[ImGuiMultiSelectFlags.ClearOnEscape]], 64 --[[ImGuiMultiSelectFlags.BoxSelect1d]])
local ms_io = ImGui.BeginMultiSelect(flags, selection.Size, ITEMS_COUNT)
selection:ApplyRequests(ms_io)
local clipper = ImGuiListClipper()
clipper:Begin(ITEMS_COUNT)
if ms_io.RangeSrcItem ~= -1 then
clipper:IncludeItemByIndex(ms_io.RangeSrcItem) -- Ensure RangeSrc item is not clipped.
end
while clipper:Step() do
for n = clipper.DisplayStart, clipper.DisplayEnd - 1 do
ImGui.TableNextRow()
ImGui.TableNextColumn()
ImGui.PushID(n)
local label = string.format("Object %05d: %s", n, ExampleNames[n % #ExampleNames + 1])
local item_is_selected = selection:Contains(n)
ImGui.SetNextItemSelectionUserData(n)
ImGui.Selectable(label, item_is_selected, bit32.bor(2 --[[ImGuiSelectableFlags.SpanAllColumns]], 16 --[[ImGuiSelectableFlags.AllowOverlap]]))
ImGui.TableNextColumn()
ImGui.SmallButton("hello")
ImGui.PopID()
end
end
ms_io = ImGui.EndMultiSelect()
selection:ApplyRequests(ms_io)
ImGui.EndTable()
end
ImGui.TreePop()
end
if ImGui.TreeNode("Multi-Select (checkboxes)") then
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (checkboxes)")
ImGui.Text("In a list of checkboxes (not selectable):")
ImGui.BulletText("Using _NoAutoSelect + _NoAutoClear flags.")
ImGui.BulletText("Shift+Click to check multiple boxes.")
ImGui.BulletText("Shift+Keyboard to copy current value to other boxes.")
-- If you have an array of checkboxes, you may want to use NoAutoSelect + NoAutoClear and the ImGuiSelectionExternalStorage helper.
local st = s.checkboxes
local items = st.items
_, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", st.flags, 8 --[[ImGuiMultiSelectFlags.NoAutoSelect]])
_, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", st.flags, 16 --[[ImGuiMultiSelectFlags.NoAutoClear]])
_, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", st.flags, 128 --[[ImGuiMultiSelectFlags.BoxSelect2d]]) -- Cannot use ImGuiMultiSelectFlags_BoxSelect1d as checkboxes are varying width.
if ImGui.BeginChild("##Basket", ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], ImGui.GetFontSize() * 20), bit32.bor(1 --[[ImGuiChildFlags.Borders]], 8 --[[ImGuiChildFlags.ResizeY]])) then
local ms_io = ImGui.BeginMultiSelect(st.flags, -1, #items)
local storage_wrapper = ImGuiSelectionExternalStorage()
storage_wrapper.UserData = items
storage_wrapper.AdapterSetItemSelected = function(self, n, selected) local array = self.UserData; array[n + 1] = selected end
storage_wrapper:ApplyRequests(ms_io)
for n = 0, 19 do
local label = string.format("Item %d", n)
ImGui.SetNextItemSelectionUserData(n)
_, items[n + 1] = ImGui.Checkbox(label, items[n + 1])
end
ms_io = ImGui.EndMultiSelect()
storage_wrapper:ApplyRequests(ms_io)
end
ImGui.EndChild()
ImGui.TreePop()
end
-- Demonstrate individual selection scopes in same window
if ImGui.TreeNode("Multi-Select (multiple scopes)") then
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (multiple scopes)")
-- Use default select: Pass index to SetNextItemSelectionUserData(), store index in Selection
local SCOPES_COUNT = 3
local ITEMS_COUNT = 8 -- Per scope
local st = s.multiple_scopes
local selections_data = st.selections_data
-- Use ImGuiMultiSelectFlags_ScopeRect to not affect other selections in same window.
local pressed
pressed, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_ScopeWindow", st.flags, 2048 --[[ImGuiMultiSelectFlags.ScopeWindow]])
if pressed and bit32.band(st.flags, 2048 --[[ImGuiMultiSelectFlags.ScopeWindow]]) ~= 0 then
st.flags = bit32.band(st.flags, bit32.bnot(4096 --[[ImGuiMultiSelectFlags.ScopeRect]]))
end
pressed, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_ScopeRect", st.flags, 4096 --[[ImGuiMultiSelectFlags.ScopeRect]])
if pressed and bit32.band(st.flags, 4096 --[[ImGuiMultiSelectFlags.ScopeRect]]) ~= 0 then
st.flags = bit32.band(st.flags, bit32.bnot(2048 --[[ImGuiMultiSelectFlags.ScopeWindow]]))
end
_, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", st.flags, 1024 --[[ImGuiMultiSelectFlags.ClearOnClickVoid]])
_, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect1d", st.flags, 64 --[[ImGuiMultiSelectFlags.BoxSelect1d]])
for selection_scope_n = 0, SCOPES_COUNT - 1 do
ImGui.PushID(selection_scope_n)
local selection = selections_data[selection_scope_n + 1]
local ms_io = ImGui.BeginMultiSelect(st.flags, selection.Size, ITEMS_COUNT)
selection:ApplyRequests(ms_io)
ImGui.SeparatorText("Selection scope")
ImGui.Text("Selection size: %d/%d", selection.Size, ITEMS_COUNT)
for n = 0, ITEMS_COUNT - 1 do
local label = string.format("Object %05d: %s", n, ExampleNames[n % #ExampleNames + 1])
local item_is_selected = selection:Contains(n)
ImGui.SetNextItemSelectionUserData(n)
ImGui.Selectable(label, item_is_selected)
end
-- Apply multi-select requests
ms_io = ImGui.EndMultiSelect()
selection:ApplyRequests(ms_io)
ImGui.PopID()
end
ImGui.TreePop()
end
-- See ShowExampleAppAssetsBrowser()
if ImGui.TreeNode("Multi-Select (tiled assets browser)") then
_, demo_data.ShowAppAssetsBrowser = ImGui.Checkbox("Assets Browser", demo_data.ShowAppAssetsBrowser)
ImGui.Text("(also access from 'Examples->Assets Browser' in menu)")
ImGui.TreePop()
end
-- Demonstrate supporting multiple-selection in a tree.
-- - We don't use linear indices for selection user data, but our ExampleTreeNode* pointer directly!
-- This showcase how SetNextItemSelectionUserData() never assume indices!
-- - The difficulty here is to "interpolate" from RangeSrcItem to RangeDstItem in the SetAll/SetRange request.
-- We want this interpolation to match what the user sees: in visible order, skipping closed nodes.
-- This is implemented by our TreeGetNextNodeInVisibleOrder() user-space helper.
-- - Important: In a real codebase aiming to implement full-featured selectable tree with custom filtering, you
-- are more likely to build an array mapping sequential indices to visible tree nodes, since your
-- filtering/search + clipping process will benefit from it. Having this will make this interpolation much easier.
-- - Consider this a prototype: we are working toward simplifying some of it.
if ImGui.TreeNode("Multi-Select (trees)") then
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (trees)")
HelpMarker(
"This is rather advanced and experimental. If you are getting started with multi-select, " ..
"please don't start by looking at how to use it for a tree!\n\n" ..
"Future versions will try to simplify and formalize some of this.")
-- struct ExampleTreeFuncs { ... }; -- [Port] defined at file level above S.DemoWindowWidgetsSelectionAndMultiSelect
local selection = s.trees.selection
if demo_data.DemoTree == nil then
demo_data.DemoTree = ExampleTree_CreateDemoTree() -- Create tree once
end
ImGui.Text("Selection size: %d", selection.Size)
if ImGui.BeginChild("##Tree", ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], ImGui.GetFontSize() * 20), bit32.bor(128 --[[ImGuiChildFlags.FrameStyle]], 8 --[[ImGuiChildFlags.ResizeY]])) then
local tree = demo_data.DemoTree
local ms_flags = bit32.bor(512 --[[ImGuiMultiSelectFlags.ClearOnEscape]], 128 --[[ImGuiMultiSelectFlags.BoxSelect2d]])
local ms_io = ImGui.BeginMultiSelect(ms_flags, selection.Size, -1)
ExampleTreeFuncs.ApplySelectionRequests(ms_io, tree, selection)
for _, node in ipairs(tree.Childs) do
ExampleTreeFuncs.DrawNode(node, selection)
end
ms_io = ImGui.EndMultiSelect()
ExampleTreeFuncs.ApplySelectionRequests(ms_io, tree, selection)
end
ImGui.EndChild()
ImGui.TreePop()
end
-- Advanced demonstration of BeginMultiSelect()
-- - Showcase clipping.
-- - Showcase deletion.
-- - Showcase basic drag and drop.
-- - Showcase TreeNode variant (note that tree node don't expand in the demo: supporting expanding tree nodes + clipping a separate thing).
-- - Showcase using inside a table.
--ImGui::SetNextItemOpen(true, ImGuiCond_Once);
if ImGui.TreeNode("Multi-Select (advanced)") then
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (advanced)")
-- Options
local WidgetType_Selectable, WidgetType_TreeNode = 0, 1 -- enum WidgetType
local st = s.advanced
if ImGui.TreeNode("Options") then
if ImGui.RadioButtonEx("Selectables", st.widget_type == WidgetType_Selectable) then st.widget_type = WidgetType_Selectable end -- [Port] RadioButton(label, bool) -> RadioButtonEx
ImGui.SameLine()
if ImGui.RadioButtonEx("Tree nodes", st.widget_type == WidgetType_TreeNode) then st.widget_type = WidgetType_TreeNode end
ImGui.SameLine()
HelpMarker("TreeNode() is technically supported but... using this correctly is more complicated (you need some sort of linear/random access to your tree, which is suited to advanced trees setups already implementing filters and clipper. We will work toward simplifying and demoing this.\n\nFor now the tree demo is actually a little bit meaningless because it is an empty tree with only root nodes.")
_, st.use_clipper = ImGui.Checkbox("Enable clipper", st.use_clipper)
_, st.use_deletion = ImGui.Checkbox("Enable deletion", st.use_deletion)
_, st.use_drag_drop = ImGui.Checkbox("Enable drag & drop", st.use_drag_drop)
_, st.show_in_table = ImGui.Checkbox("Show in a table", st.show_in_table)
_, st.show_color_button = ImGui.Checkbox("Show color button", st.show_color_button)
_, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_SingleSelect", st.flags, 1 --[[ImGuiMultiSelectFlags.SingleSelect]])
_, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", st.flags, 2 --[[ImGuiMultiSelectFlags.NoSelectAll]])
_, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_NoRangeSelect", st.flags, 4 --[[ImGuiMultiSelectFlags.NoRangeSelect]])
_, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", st.flags, 8 --[[ImGuiMultiSelectFlags.NoAutoSelect]])
_, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", st.flags, 16 --[[ImGuiMultiSelectFlags.NoAutoClear]])
_, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClearOnReselect", st.flags, 32 --[[ImGuiMultiSelectFlags.NoAutoClearOnReselect]])
_, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_NoSelectOnRightClick", st.flags, 131072 --[[ImGuiMultiSelectFlags.NoSelectOnRightClick]])
_, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect1d", st.flags, 64 --[[ImGuiMultiSelectFlags.BoxSelect1d]])
_, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", st.flags, 128 --[[ImGuiMultiSelectFlags.BoxSelect2d]])
_, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_BoxSelectNoScroll", st.flags, 256 --[[ImGuiMultiSelectFlags.BoxSelectNoScroll]])
_, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", st.flags, 512 --[[ImGuiMultiSelectFlags.ClearOnEscape]])
_, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", st.flags, 1024 --[[ImGuiMultiSelectFlags.ClearOnClickVoid]])
local pressed
pressed, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_ScopeWindow", st.flags, 2048 --[[ImGuiMultiSelectFlags.ScopeWindow]])
if pressed and bit32.band(st.flags, 2048 --[[ImGuiMultiSelectFlags.ScopeWindow]]) ~= 0 then
st.flags = bit32.band(st.flags, bit32.bnot(4096 --[[ImGuiMultiSelectFlags.ScopeRect]]))
end
pressed, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_ScopeRect", st.flags, 4096 --[[ImGuiMultiSelectFlags.ScopeRect]])
if pressed and bit32.band(st.flags, 4096 --[[ImGuiMultiSelectFlags.ScopeRect]]) ~= 0 then
st.flags = bit32.band(st.flags, bit32.bnot(2048 --[[ImGuiMultiSelectFlags.ScopeWindow]]))
end
pressed, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_SelectOnAuto", st.flags, 8192 --[[ImGuiMultiSelectFlags.SelectOnAuto]])
if pressed then
st.flags = bit32.band(st.flags, bit32.bnot(bit32.bxor(57344 --[[ImGuiMultiSelectFlags.SelectOnMask_]], 8192 --[[ImGuiMultiSelectFlags.SelectOnAuto]])))
end
ImGui.SameLine(); HelpMarker("Apply selection on mouse down when clicking on unselected item, on mouse up when clicking on selected item. (Default)")
pressed, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClickAlways", st.flags, 16384 --[[ImGuiMultiSelectFlags.SelectOnClickAlways]])
if pressed then
st.flags = bit32.band(st.flags, bit32.bnot(bit32.bxor(57344 --[[ImGuiMultiSelectFlags.SelectOnMask_]], 16384 --[[ImGuiMultiSelectFlags.SelectOnClickAlways]])))
end
ImGui.SameLine(); HelpMarker("Prevents Drag and Drop from being used on multi-selection, but allows e.g. BoxSelect to always reselect even when clicking inside an existing selection. (Excel style behavior)")
pressed, st.flags = ImGui.CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClickRelease", st.flags, 32768 --[[ImGuiMultiSelectFlags.SelectOnClickRelease]])
if pressed then
st.flags = bit32.band(st.flags, bit32.bnot(bit32.bxor(57344 --[[ImGuiMultiSelectFlags.SelectOnMask_]], 32768 --[[ImGuiMultiSelectFlags.SelectOnClickRelease]])))
end
ImGui.SameLine(); HelpMarker("Allow dragging an unselected item without altering selection.")
ImGui.TreePop()
end
-- Initialize default list with 1000 items.
-- Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection
local items = st.items
if st.items_next_id == 0 then for _ = 0, 999 do items:push_back(st.items_next_id); st.items_next_id = st.items_next_id + 1 end end
local selection = st.selection
ImGui.Text("Selection size: %d/%d", selection.Size, items.Size)
local items_height = (st.widget_type == WidgetType_TreeNode) and ImGui.GetTextLineHeight() or ImGui.GetTextLineHeightWithSpacing()
ImGui.SetNextWindowContentSize(ImVec2(0.0, items.Size * items_height))
if ImGui.BeginChild("##Basket", ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], ImGui.GetFontSize() * 20), bit32.bor(128 --[[ImGuiChildFlags.FrameStyle]], 8 --[[ImGuiChildFlags.ResizeY]])) then
local color_button_sz = ImVec2(ImGui.GetFontSize(), ImGui.GetFontSize())
if st.widget_type == WidgetType_TreeNode then
ImGui.PushStyleVarY(14 --[[ImGuiStyleVar.ItemSpacing]], 0.0)
end
local ms_io = ImGui.BeginMultiSelect(st.flags, selection.Size, items.Size)
selection:ApplyRequests(ms_io)
local want_delete = (ImGui.Shortcut(522 --[[ImGuiKey.Delete]], 1 --[[ImGuiInputFlags.Repeat]]) and (selection.Size > 0)) or st.request_deletion_from_menu
local item_curr_idx_to_focus = -1
if want_delete then item_curr_idx_to_focus = selection:ApplyDeletionPreLoop(ms_io, items.Size) end
st.request_deletion_from_menu = false
if st.show_in_table then
if st.widget_type == WidgetType_TreeNode then
ImGui.PushStyleVar(17 --[[ImGuiStyleVar.CellPadding]], ImVec2(0.0, 0.0))
end
ImGui.BeginTable("##Split", 2, bit32.bor(1 --[[ImGuiTableFlags.Resizable]], 16 --[[ImGuiTableFlags.NoSavedSettings]], 4194304 --[[ImGuiTableFlags.NoPadOuterX]]))
ImGui.TableSetupColumn("", 8 --[[ImGuiTableColumnFlags.WidthStretch]], 0.70)
ImGui.TableSetupColumn("", 8 --[[ImGuiTableColumnFlags.WidthStretch]], 0.30)
--ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, 0.0f);
end
local clipper = ImGuiListClipper()
if st.use_clipper then
clipper:Begin(items.Size)
if item_curr_idx_to_focus ~= -1 then
clipper:IncludeItemByIndex(item_curr_idx_to_focus) -- Ensure focused item is not clipped.
end
if ms_io.RangeSrcItem ~= -1 then
clipper:IncludeItemByIndex(ms_io.RangeSrcItem) -- Ensure RangeSrc item is not clipped.
end
end
while not st.use_clipper or clipper:Step() do
local item_begin = st.use_clipper and clipper.DisplayStart or 0
local item_end = st.use_clipper and clipper.DisplayEnd or items.Size
for n = item_begin, item_end - 1 do
if st.show_in_table then
ImGui.TableNextColumn()
end
local item_id = items.Data[n + 1]
local item_category = ExampleNames[item_id % #ExampleNames + 1]
local label = string.format("Object %05d: %s", item_id, item_category)
-- IMPORTANT: for deletion refocus to work we need object ID to be stable,
-- aka not depend on their index in the list. Here we use our persistent item_id
-- instead of index to build a unique ID that will persist.
-- (If we used PushID(index) instead, focus wouldn't be restored correctly after deletion).
ImGui.PushID(item_id)
-- Emit a color button, to test that Shift+LeftArrow landing on an item that is not part
-- of the selection scope doesn't erroneously alter our selection.
if st.show_color_button then
local dummy_col = bit32.bor((n * 0xC250B74B) % 4294967296, 4278190080 --[[IM_COL32_A_MASK]]) -- [Port] unsigned 32-bit wrap-around
ImGui.ColorButton("##", ImColor(dummy_col).Value, 64 --[[ImGuiColorEditFlags.NoTooltip]], color_button_sz)
ImGui.SameLine()
end
-- Submit item
local item_is_selected = selection:Contains(n)
local item_is_open = false
ImGui.SetNextItemSelectionUserData(n)
if st.widget_type == WidgetType_Selectable then
ImGui.Selectable(label, item_is_selected, 0 --[[ImGuiSelectableFlags.None]])
elseif st.widget_type == WidgetType_TreeNode then
local tree_node_flags = bit32.bor(2048 --[[ImGuiTreeNodeFlags.SpanAvailWidth]], 128 --[[ImGuiTreeNodeFlags.OpenOnArrow]], 64 --[[ImGuiTreeNodeFlags.OpenOnDoubleClick]])
if item_is_selected then
tree_node_flags = bit32.bor(tree_node_flags, 1 --[[ImGuiTreeNodeFlags.Selected]])
end
item_is_open = ImGui.TreeNodeEx(label, tree_node_flags)
end
-- Focus (for after deletion)
if item_curr_idx_to_focus == n then
ImGui.SetKeyboardFocusHere(-1)
end
-- Drag and Drop
if st.use_drag_drop and ImGui.BeginDragDropSource() then
-- Create payload with full selection OR single unselected item.
-- (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease)
if ImGui.GetDragDropPayload() == nil then
local payload_items = ImVector()
local it = nil
local id = 0
if not item_is_selected then
payload_items:push_back(item_id)
else
local has_more
while true do
has_more, it, id = selection:GetNextSelectedItem(it)
if not has_more then break end
payload_items:push_back(id)
end
end
ImGui.SetDragDropPayload("MULTISELECT_DEMO_ITEMS", payload_items.Data, payload_items.Size * 4) -- [Port] size_in_bytes() = Size * sizeof(int), payload carries the Lua array
end
-- Display payload content in tooltip
local payload = ImGui.GetDragDropPayload()
local payload_items = payload.Data
local payload_count = math.floor(payload.DataSize / 4) -- [Port] sizeof(int) = 4
if payload_count == 1 then
ImGui.Text("Object %05d: %s", payload_items[1], ExampleNames[payload_items[1] % #ExampleNames + 1])
else
ImGui.Text("Dragging %d objects", payload_count)
end
ImGui.EndDragDropSource()
end
if st.widget_type == WidgetType_TreeNode and item_is_open then
ImGui.TreePop()
end
-- Right-click: context menu
if ImGui.BeginPopupContextItem() then
ImGui.BeginDisabled(not st.use_deletion or selection.Size == 0)
label = string.format("Delete %d item(s)###DeleteSelected", selection.Size)
if ImGui.Selectable(label) then
st.request_deletion_from_menu = true
end
ImGui.EndDisabled()
ImGui.Selectable("Close")
ImGui.EndPopup()
end
-- Demo content within a table
if st.show_in_table then
ImGui.TableNextColumn()
ImGui.SetNextItemWidth(-1.1754943508223e-38 --[[FLT_MIN]])
ImGui.PushStyleVar(11 --[[ImGuiStyleVar.FramePadding]], ImVec2(0, 0))
-- [Port] InputText() needs a byte buffer: one cached read-only buffer per category (buf_size includes the terminator)
local category_buf = st.item_category_bufs[item_category]
if category_buf == nil then category_buf = ImGui.NewTextBuffer(item_category, #item_category + 1); st.item_category_bufs[item_category] = category_buf end
ImGui.InputText("##NoLabel", category_buf, #item_category + 1, 512 --[[ImGuiInputTextFlags.ReadOnly]])
ImGui.PopStyleVar()
end
ImGui.PopID()
end
if not st.use_clipper then
break
end
end
if st.show_in_table then
ImGui.EndTable()
if st.widget_type == WidgetType_TreeNode then
ImGui.PopStyleVar()
end
end
-- Apply multi-select requests
ms_io = ImGui.EndMultiSelect()
selection:ApplyRequests(ms_io)
if want_delete then
selection:ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_focus)
end
if st.widget_type == WidgetType_TreeNode then
ImGui.PopStyleVar()
end
end
ImGui.EndChild()
ImGui.TreePop()
end
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsTabs()
-------------------------------------------------------------------------------
-- [Port] ImGuiTabBarFlags* p_flags -> takes flags, returns the new flags
function EditTabBarFittingPolicyFlags(p_flags)
if bit32.band(p_flags, 896 --[[ImGuiTabBarFlags.FittingPolicyMask_]]) == 0 then
p_flags = bit32.bor(p_flags, 128 --[[ImGuiTabBarFlags.FittingPolicyDefault_]])
end
local pressed
pressed, p_flags = ImGui.CheckboxFlags("ImGuiTabBarFlags_FittingPolicyMixed", p_flags, 128 --[[ImGuiTabBarFlags.FittingPolicyMixed]])
if pressed then
p_flags = bit32.band(p_flags, bit32.bnot(bit32.bxor(896 --[[ImGuiTabBarFlags.FittingPolicyMask_]], 128 --[[ImGuiTabBarFlags.FittingPolicyMixed]])))
end
pressed, p_flags = ImGui.CheckboxFlags("ImGuiTabBarFlags_FittingPolicyShrink", p_flags, 256 --[[ImGuiTabBarFlags.FittingPolicyShrink]])
if pressed then
p_flags = bit32.band(p_flags, bit32.bnot(bit32.bxor(896 --[[ImGuiTabBarFlags.FittingPolicyMask_]], 256 --[[ImGuiTabBarFlags.FittingPolicyShrink]])))
end
pressed, p_flags = ImGui.CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", p_flags, 512 --[[ImGuiTabBarFlags.FittingPolicyScroll]])
if pressed then
p_flags = bit32.band(p_flags, bit32.bnot(bit32.bxor(896 --[[ImGuiTabBarFlags.FittingPolicyMask_]], 512 --[[ImGuiTabBarFlags.FittingPolicyScroll]])))
end
return p_flags
end
S.DemoWindowWidgetsTabs = {
tab_bar_flags = ImGuiTabBarFlags.Reorderable,
opened = { true, true, true, true }, -- Persistent user state
active_tabs = ImVector(),
next_tab_id = 0,
show_leading_button = true,
show_trailing_button = true,
show_leading_trailing_tabs = false,
tab_bar_flags_2 = bit32.bor(ImGuiTabBarFlags.AutoSelectNewTabs, ImGuiTabBarFlags.Reorderable, ImGuiTabBarFlags.FittingPolicyMixed),
}
function DemoWindowWidgetsTabs()
local s = S.DemoWindowWidgetsTabs
if ImGui.TreeNode("Tabs") then
IMGUI_DEMO_MARKER("Widgets/Tabs")
if ImGui.TreeNode("Basic") then
IMGUI_DEMO_MARKER("Widgets/Tabs/Basic")
local tab_bar_flags = 0 --[[ImGuiTabBarFlags.None]]
if ImGui.BeginTabBar("MyTabBar", tab_bar_flags) then
if ImGui.BeginTabItem("Avocado") then
ImGui.Text("This is the Avocado tab!\nblah blah blah blah blah")
ImGui.EndTabItem()
end
if ImGui.BeginTabItem("Broccoli") then
ImGui.Text("This is the Broccoli tab!\nblah blah blah blah blah")
ImGui.EndTabItem()
end
if ImGui.BeginTabItem("Cucumber") then
ImGui.Text("This is the Cucumber tab!\nblah blah blah blah blah")
ImGui.EndTabItem()
end
ImGui.EndTabBar()
end
ImGui.Separator()
ImGui.TreePop()
end
if ImGui.TreeNode("Advanced & Close Button") then
IMGUI_DEMO_MARKER("Widgets/Tabs/Advanced & Close Button")
-- Expose a couple of the available flags. In most cases you may just call BeginTabBar() with no flags (0).
_, s.tab_bar_flags = ImGui.CheckboxFlags("ImGuiTabBarFlags_Reorderable", s.tab_bar_flags, 1 --[[ImGuiTabBarFlags.Reorderable]])
_, s.tab_bar_flags = ImGui.CheckboxFlags("ImGuiTabBarFlags_AutoSelectNewTabs", s.tab_bar_flags, 2 --[[ImGuiTabBarFlags.AutoSelectNewTabs]])
_, s.tab_bar_flags = ImGui.CheckboxFlags("ImGuiTabBarFlags_TabListPopupButton", s.tab_bar_flags, 4 --[[ImGuiTabBarFlags.TabListPopupButton]])
_, s.tab_bar_flags = ImGui.CheckboxFlags("ImGuiTabBarFlags_NoCloseWithMiddleMouseButton", s.tab_bar_flags, 8 --[[ImGuiTabBarFlags.NoCloseWithMiddleMouseButton]])
_, s.tab_bar_flags = ImGui.CheckboxFlags("ImGuiTabBarFlags_DrawSelectedOverline", s.tab_bar_flags, 64 --[[ImGuiTabBarFlags.DrawSelectedOverline]])
s.tab_bar_flags = EditTabBarFittingPolicyFlags(s.tab_bar_flags)
-- Tab Bar
ImGui.AlignTextToFramePadding()
ImGui.Text("Opened:")
local names = { "Artichoke", "Beetroot", "Celery", "Daikon" }
local opened = s.opened -- Persistent user state
for n = 0, #opened - 1 do
ImGui.SameLine()
_, opened[n + 1] = ImGui.Checkbox(names[n + 1], opened[n + 1])
end
-- Passing a bool* to BeginTabItem() is similar to passing one to Begin():
-- the underlying bool will be set to false when the tab is closed.
if ImGui.BeginTabBar("MyTabBar", s.tab_bar_flags) then
for n = 0, #opened - 1 do
if opened[n + 1] then
local visible
visible, opened[n + 1] = ImGui.BeginTabItem(names[n + 1], opened[n + 1], 0 --[[ImGuiTabItemFlags.None]])
if visible then
ImGui.Text("This is the %s tab!", names[n + 1])
if bit32.band(n, 1) ~= 0 then
ImGui.Text("I am an odd tab.")
end
ImGui.EndTabItem()
end
end
end
ImGui.EndTabBar()
end
ImGui.Separator()
ImGui.TreePop()
end
if ImGui.TreeNode("TabItemButton & Leading/Trailing flags") then
IMGUI_DEMO_MARKER("Widgets/Tabs/TabItemButton & Leading-Trailing flags")
local active_tabs = s.active_tabs
if s.next_tab_id == 0 then -- Initialize with some default tabs
for i = 0, 2 do
active_tabs:push_back(s.next_tab_id); s.next_tab_id = s.next_tab_id + 1
end
end
-- TabItemButton() and Leading/Trailing flags are distinct features which we will demo together.
-- (It is possible to submit regular tabs with Leading/Trailing flags, or TabItemButton tabs without Leading/Trailing flags...
-- but they tend to make more sense together)
_, s.show_leading_button = ImGui.Checkbox("Show Leading TabItemButton()", s.show_leading_button)
_, s.show_trailing_button = ImGui.Checkbox("Show Trailing TabItemButton()", s.show_trailing_button)
_, s.show_leading_trailing_tabs = ImGui.Checkbox("Show Leading+Trailing TabItem()", s.show_leading_trailing_tabs)
-- Expose some other flags which are useful to showcase how they interact with Leading/Trailing tabs
s.tab_bar_flags_2 = EditTabBarFittingPolicyFlags(s.tab_bar_flags_2)
if ImGui.BeginTabBar("MyTabBar", s.tab_bar_flags_2) then
-- Demo a Leading TabItemButton(): click the "?" button to open a menu
if s.show_leading_button then
if ImGui.TabItemButton("?", bit32.bor(64 --[[ImGuiTabItemFlags.Leading]], 16 --[[ImGuiTabItemFlags.NoTooltip]])) then
ImGui.OpenPopup("MyHelpMenu")
end
end
if ImGui.BeginPopup("MyHelpMenu") then
ImGui.Selectable("Hello!")
ImGui.EndPopup()
end
-- Demo Leading/Trailing Tabs
if s.show_leading_trailing_tabs then
if ImGui.BeginTabItem("Leading", nil, 64 --[[ImGuiTabItemFlags.Leading]]) then
ImGui.EndTabItem()
end
if ImGui.BeginTabItem("Trailing", nil, 128 --[[ImGuiTabItemFlags.Trailing]]) then
ImGui.EndTabItem()
end
end
-- Demo Trailing Tabs: click the "+" button to add a new tab.
-- (In your app you may want to use a font icon instead of the "+")
-- We submit it before the regular tabs, but thanks to the ImGuiTabItemFlags_Trailing flag it will always appear at the end.
if s.show_trailing_button then
if ImGui.TabItemButton("+", bit32.bor(128 --[[ImGuiTabItemFlags.Trailing]], 16 --[[ImGuiTabItemFlags.NoTooltip]])) then
active_tabs:push_back(s.next_tab_id); s.next_tab_id = s.next_tab_id + 1 -- Add new tab
end
end
-- Submit our regular tabs
local n = 0
while n < active_tabs.Size do
local open = true
local name = string.format("%04d", active_tabs.Data[n + 1])
local visible
visible, open = ImGui.BeginTabItem(name, open, 0 --[[ImGuiTabItemFlags.None]])
if visible then
ImGui.Text("This is the %s tab!", name)
ImGui.EndTabItem()
end
if not open then
active_tabs:erase(n + 1)
else
n = n + 1
end
end
ImGui.EndTabBar()
end
ImGui.Separator()
ImGui.TreePop()
end
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsText()
-------------------------------------------------------------------------------
S.DemoWindowWidgetsText = {
custom_size = 16.0,
custom_scale = 1.0,
wrap_width = 200.0,
buf = ImGui.NewTextBuffer("\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e", 32),
}
function DemoWindowWidgetsText()
local s = S.DemoWindowWidgetsText
if ImGui.TreeNode("Text") then
IMGUI_DEMO_MARKER("Widgets/Text")
if ImGui.TreeNode("Colorful Text") then
IMGUI_DEMO_MARKER("Widgets/Text/Colored Text")
-- Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility.
ImGui.TextColored(ImVec4(1.0, 0.0, 1.0, 1.0), "Pink")
ImGui.TextColored(ImVec4(1.0, 1.0, 0.0, 1.0), "Yellow")
ImGui.TextDisabled("Disabled")
ImGui.SameLine(); HelpMarker("The TextDisabled color is stored in ImGuiStyle.")
ImGui.TreePop()
end
if ImGui.TreeNode("Font Size") then
IMGUI_DEMO_MARKER("Widgets/Text/Font Size")
local style = ImGui.GetStyle()
local global_scale = style.FontScaleMain * style.FontScaleDpi
ImGui.Text("style.FontScaleMain = %0.2f", style.FontScaleMain)
ImGui.Text("style.FontScaleDpi = %0.2f", style.FontScaleDpi)
ImGui.Text("global_scale = ~%0.2f", global_scale) -- This is not technically accurate as internal scales may apply, but conceptually let's pretend it is.
ImGui.Text("FontSize = %0.2f", ImGui.GetFontSize())
ImGui.SeparatorText("")
s.custom_size = ImGui.SliderFloat("custom_size", s.custom_size, 10.0, 100.0, "%.0f")
ImGui.Text("ImGui::PushFont(nullptr, custom_size);")
ImGui.PushFont(nil, s.custom_size)
ImGui.Text("FontSize = %.2f (== %.2f * global_scale)", ImGui.GetFontSize(), s.custom_size)
ImGui.PopFont()
ImGui.SeparatorText("")
s.custom_scale = ImGui.SliderFloat("custom_scale", s.custom_scale, 0.5, 4.0, "%.2f")
ImGui.Text("ImGui::PushFont(nullptr, style.FontSizeBase * custom_scale);")
ImGui.PushFont(nil, style.FontSizeBase * s.custom_scale)
ImGui.Text("FontSize = %.2f (== style.FontSizeBase * %.2f * global_scale)", ImGui.GetFontSize(), s.custom_scale)
ImGui.PopFont()
ImGui.SeparatorText("")
local scaling = 0.5
while scaling <= 4.0 do
ImGui.PushFont(nil, style.FontSizeBase * scaling)
ImGui.Text("FontSize = %.2f (== style.FontSizeBase * %.2f * global_scale)", ImGui.GetFontSize(), scaling)
ImGui.PopFont()
scaling = scaling + 0.5
end
ImGui.TreePop()
end
if ImGui.TreeNode("Word Wrapping") then
IMGUI_DEMO_MARKER("Widgets/Text/Word Wrapping")
-- Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility.
ImGui.TextWrapped(
"This text should automatically wrap on the edge of the window. The current implementation " ..
"for text wrapping follows simple rules suitable for English and possibly other languages.")
ImGui.Spacing()
s.wrap_width = ImGui.SliderFloat("Wrap width", s.wrap_width, -20, 600, "%.0f")
local draw_list = ImGui.GetWindowDrawList()
for n = 0, 1 do
ImGui.Text("Test paragraph %d:", n)
local pos = ImGui.GetCursorScreenPos()
local marker_min = ImVec2(pos.x + s.wrap_width, pos.y)
local marker_max = ImVec2(pos.x + s.wrap_width + 10, pos.y + ImGui.GetTextLineHeight())
ImGui.PushTextWrapPos(ImGui.GetCursorPos().x + s.wrap_width)
if n == 0 then
ImGui.Text("The lazy dog is a good dog. This paragraph should fit within %.0f pixels. Testing a 1 character word. The quick brown fox jumps over the lazy dog.", s.wrap_width)
else
ImGui.Text("aaaaaaaa bbbbbbbb, c cccccccc,dddddddd. d eeeeeeee ffffffff. gggggggg!hhhhhhhh")
end
-- Draw actual text bounding box, following by marker of our expected limit (should not overlap!)
draw_list:AddRect(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), IM_COL32(255, 255, 0, 255))
draw_list:AddRectFilled(marker_min, marker_max, IM_COL32(255, 0, 255, 255))
ImGui.PopTextWrapPos()
end
ImGui.TreePop()
end
if ImGui.TreeNode("UTF-8 Text") then
IMGUI_DEMO_MARKER("Widgets/Text/UTF-8 Text")
-- UTF-8 test with Japanese characters
-- (Needs a suitable font? Try "Google Noto" or "Arial Unicode". See docs/FONTS.md for details.)
-- - From C++11 you can use the u8"my text" syntax to encode literal strings as UTF-8
-- - For earlier compiler, you may be able to encode your sources as UTF-8 (e.g. in Visual Studio, you
-- can save your source files as 'UTF-8 without signature').
-- - FOR THIS DEMO FILE ONLY, BECAUSE WE WANT TO SUPPORT OLD COMPILERS, WE ARE *NOT* INCLUDING RAW UTF-8
-- CHARACTERS IN THIS SOURCE FILE. Instead we are encoding a few strings with hexadecimal constants.
-- Don't do this in your application! Please use u8"text in any language" in your application!
-- Note that characters values are preserved even by InputText() if the font cannot be displayed,
-- so you can safely copy & paste garbled characters into another application.
ImGui.TextWrapped(
"CJK text will only appear if the font was loaded with the appropriate CJK character ranges. " ..
"Call io.Fonts->AddFontFromFileTTF() manually to load extra character ranges. " ..
"Read docs/FONTS.md for details.")
ImGui.Text("Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)")
ImGui.Text("Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)")
--static char buf[32] = u8"NIHONGO"; -- <- this is how you would write it with C++11, using real kanjis
ImGui.InputText("UTF-8 input", s.buf, 32)
ImGui.TreePop()
end
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsTextFilter()
-------------------------------------------------------------------------------
S.DemoWindowWidgetsTextFilter = {
filter = ImGuiTextFilter(),
}
function DemoWindowWidgetsTextFilter()
local s = S.DemoWindowWidgetsTextFilter
if ImGui.TreeNode("Text Filter") then
IMGUI_DEMO_MARKER("Widgets/Text Filter")
-- Helper class to easy setup a text filter.
-- You may want to implement a more feature-full filtering scheme in your own application.
HelpMarker("Not a widget per-se, but ImGuiTextFilter is a helper to perform simple filtering on text strings.")
local filter = s.filter
ImGui.Text("Filter usage:\n" ..
" \"\" display all lines\n" ..
" xxx display lines containing \"xxx\"\n" ..
" xxx yyy display lines containing \"xxx\" and \"yyy\"\n" ..
" \"xxx yyy\" display lines containing \"xxx yyy\"\n" ..
" xxx,yyy display lines containing \"xxx\" or \"yyy\"\n" ..
" -xxx hide lines containing \"xxx\"")
ImGui.SetNextItemWidth(-1.1754943508223e-38 --[[FLT_MIN]])
filter:DrawWithHint("##Filter", "Filter (incl -excl)")
if ImGui.BeginChild("##items", ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], ImGui.GetTextLineHeightWithSpacing() * 15), 128 --[[ImGuiChildFlags.FrameStyle]]) then
local lines = { "aaa1.c", "bbb1.c", "ccc1.c", "aaa2.cpp", "bbb2.cpp", "ccc2.cpp", "abc.h", "hello, world" }
for _, item in ipairs(lines) do
if filter:PassFilter(item) then
ImGui.TextUnformatted(item)
end
end
for _, item in ipairs(ExampleNames) do
if filter:PassFilter(item) then
ImGui.TextUnformatted(item)
end
end
end
ImGui.EndChild()
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsTextInput()
-------------------------------------------------------------------------------
S.DemoWindowWidgetsTextInput = {
-- "Multi-line Text Input"
ml = {
text = ImGui.NewTextBuffer(
"/*\n" ..
" The Pentium F00F bug, shorthand for F0 0F C7 C8,\n" ..
" the hexadecimal encoding of one offending instruction,\n" ..
" more formally, the invalid operand with locked CMPXCHG8B\n" ..
" instruction bug, is a design flaw in the majority of\n" ..
" Intel Pentium, Pentium MMX, and Pentium OverDrive\n" ..
" processors (all in the P5 microarchitecture).\n" ..
"*/\n\n" ..
"label:\n" ..
"\tlock cmpxchg8b eax\n", 1024 * 16),
flags = ImGuiInputTextFlags.AllowTabInput,
},
-- "Filtered Text Input"
filtered = {
buf1 = ImGui.NewTextBuffer("", 32), buf2 = ImGui.NewTextBuffer("", 32), buf3 = ImGui.NewTextBuffer("", 32),
buf4 = ImGui.NewTextBuffer("", 32), buf5 = ImGui.NewTextBuffer("", 32), buf6 = ImGui.NewTextBuffer("", 32),
buf7 = ImGui.NewTextBuffer("", 32),
},
-- "Password Input"
password = { password = ImGui.NewTextBuffer("password123", 64) },
-- "Completion, History, Edit Callbacks"
callbacks = {
buf1 = ImGui.NewTextBuffer("", 64), buf2 = ImGui.NewTextBuffer("", 64), buf3 = ImGui.NewTextBuffer("", 64),
edit_count = 0,
},
-- "Resize Callback"
resize = { flags = ImGuiInputTextFlags.None, my_str = ImVector() },
-- "Eliding, Alignment"
eliding = { buf1 = ImGui.NewTextBuffer("/path/to/some/folder/with/long/filename.cpp", 128), flags = ImGuiInputTextFlags.ElideLeft },
-- "Miscellaneous"
misc = { buf1 = ImGui.NewTextBuffer("", 16), flags = ImGuiInputTextFlags.EscapeClearsAll },
}
function DemoWindowWidgetsTextInput()
local S_ti = S.DemoWindowWidgetsTextInput
-- To wire InputText() with std::string or any other custom string type,
-- see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file.
if ImGui.TreeNode("Text Input") then
IMGUI_DEMO_MARKER("Widgets/Text Input")
if ImGui.TreeNode("Multi-line Text Input") then
IMGUI_DEMO_MARKER("Widgets/Text Input/Multi-line Text Input")
local s = S_ti.ml
-- WE ARE USING A FIXED-SIZE BUFFER FOR SIMPLICITY HERE.
-- If you want to use InputText() with std::string or any custom dynamic string type:
-- - For std::string: use the wrapper in misc/cpp/imgui_stdlib.h/.cpp
-- - Otherwise, see the 'Dear ImGui Demo->Widgets->Text Input->Resize Callback' for using ImGuiInputTextFlags_CallbackResize.
HelpMarker("You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example. (This is not demonstrated in imgui_demo.cpp because we don't want to include <string> in here)")
_, s.flags = ImGui.CheckboxFlags("ImGuiInputTextFlags_ReadOnly", s.flags, 512 --[[ImGuiInputTextFlags.ReadOnly]])
_, s.flags = ImGui.CheckboxFlags("ImGuiInputTextFlags_WordWrap", s.flags, 16777216 --[[ImGuiInputTextFlags.WordWrap]])
ImGui.SameLine(); HelpMarker("Feature is currently in Beta. Please read comments in imgui.h")
_, s.flags = ImGui.CheckboxFlags("ImGuiInputTextFlags_AllowTabInput", s.flags, 32 --[[ImGuiInputTextFlags.AllowTabInput]])
ImGui.SameLine(); HelpMarker("When _AllowTabInput is set, passing through the widget with Tabbing doesn't automatically activate it, in order to also cycling through subsequent widgets.")
_, s.flags = ImGui.CheckboxFlags("ImGuiInputTextFlags_CtrlEnterForNewLine", s.flags, 256 --[[ImGuiInputTextFlags.CtrlEnterForNewLine]])
ImGui.InputTextMultiline("##source", s.text, 1024 * 16, ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], ImGui.GetTextLineHeight() * 16), s.flags)
ImGui.TreePop()
end
if ImGui.TreeNode("Filtered Text Input") then
IMGUI_DEMO_MARKER("Widgets/Text Input/Filtered Text Input")
local s = S_ti.filtered
local TextFilters = {}
-- Modify character input by altering 'data->Eventchar' (ImGuiInputTextFlags_CallbackCharFilter callback)
function TextFilters.FilterCasingSwap(data)
if data.EventChar >= string.byte('a') and data.EventChar <= string.byte('z') then data.EventChar = data.EventChar - (string.byte('a') - string.byte('A')) -- Lowercase becomes uppercase
elseif data.EventChar >= string.byte('A') and data.EventChar <= string.byte('Z') then data.EventChar = data.EventChar + (string.byte('a') - string.byte('A')) end -- Uppercase becomes lowercase
return 0
end
-- Return 0 (pass) if the character is 'i' or 'm' or 'g' or 'u' or 'i', otherwise return 1 (filter out)
function TextFilters.FilterImGuiLetters(data)
if data.EventChar < 256 and string.find("imgui", string.char(data.EventChar), 1, true) then
return 0
end
return 1
end
ImGui.InputText("default", s.buf1, 32)
ImGui.InputText("decimal", s.buf2, 32, 1 --[[ImGuiInputTextFlags.CharsDecimal]])
ImGui.InputText("hexadecimal", s.buf3, 32, bit32.bor(2 --[[ImGuiInputTextFlags.CharsHexadecimal]], 8 --[[ImGuiInputTextFlags.CharsUppercase]]))
ImGui.InputText("uppercase", s.buf4, 32, 8 --[[ImGuiInputTextFlags.CharsUppercase]])
ImGui.InputText("no blank", s.buf5, 32, 16 --[[ImGuiInputTextFlags.CharsNoBlank]])
ImGui.InputText("casing swap", s.buf6, 32, 2097152 --[[ImGuiInputTextFlags.CallbackCharFilter]], TextFilters.FilterCasingSwap) -- Use CharFilter callback to replace characters.
ImGui.InputText("\"imgui\"", s.buf7, 32, 2097152 --[[ImGuiInputTextFlags.CallbackCharFilter]], TextFilters.FilterImGuiLetters) -- Use CharFilter callback to disable some characters.
ImGui.TreePop()
end
if ImGui.TreeNode("Password Input") then
IMGUI_DEMO_MARKER("Widgets/Text Input/Password input")
local s = S_ti.password
ImGui.InputText("password", s.password, 64, 1024 --[[ImGuiInputTextFlags.Password]])
ImGui.SameLine(); HelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n")
ImGui.InputTextWithHint("password (w/ hint)", "<password>", s.password, 64, 1024 --[[ImGuiInputTextFlags.Password]])
ImGui.InputText("password (clear)", s.password, 64)
ImGui.TreePop()
end
if ImGui.TreeNode("Completion, History, Edit Callbacks") then
IMGUI_DEMO_MARKER("Widgets/Text Input/Completion, History, Edit Callbacks")
local s = S_ti.callbacks
local Funcs = {}
function Funcs.MyCallback(data)
if data.EventFlag == 262144 --[[ImGuiInputTextFlags.CallbackCompletion]] then
data:InsertChars(data.CursorPos, "..")
elseif data.EventFlag == 524288 --[[ImGuiInputTextFlags.CallbackHistory]] then
if data.EventKey == 515 --[[ImGuiKey.UpArrow]] then
data:DeleteChars(0, data.BufTextLen)
data:InsertChars(0, "Pressed Up!")
data:SelectAll()
elseif data.EventKey == 516 --[[ImGuiKey.DownArrow]] then
data:DeleteChars(0, data.BufTextLen)
data:InsertChars(0, "Pressed Down!")
data:SelectAll()
end
elseif data.EventFlag == 8388608 --[[ImGuiInputTextFlags.CallbackEdit]] then
-- Toggle casing of first character
local c = data.Buf[1]
if (c >= string.byte('a') and c <= string.byte('z')) or (c >= string.byte('A') and c <= string.byte('Z')) then data.Buf[1] = bit32.bxor(data.Buf[1], 32) end
data.BufDirty = true
-- Increment a counter
local p_int = data.UserData -- [Port] int* -> the statics table, counter in .edit_count
p_int.edit_count = p_int.edit_count + 1
end
return 0
end
ImGui.InputText("Completion", s.buf1, 64, 262144 --[[ImGuiInputTextFlags.CallbackCompletion]], Funcs.MyCallback)
ImGui.SameLine(); HelpMarker(
"Here we append \"..\" each time Tab is pressed. " ..
"See 'Examples>Console' for a more meaningful demonstration of using this callback.")
ImGui.InputText("History", s.buf2, 64, 524288 --[[ImGuiInputTextFlags.CallbackHistory]], Funcs.MyCallback)
ImGui.SameLine(); HelpMarker(
"Here we replace and select text each time Up/Down are pressed. " ..
"See 'Examples>Console' for a more meaningful demonstration of using this callback.")
ImGui.InputText("Edit", s.buf3, 64, 8388608 --[[ImGuiInputTextFlags.CallbackEdit]], Funcs.MyCallback, s) -- [Port] (void*)&edit_count -> s
ImGui.SameLine(); HelpMarker(
"Here we toggle the casing of the first character on every edit + count edits.")
ImGui.SameLine(); ImGui.Text("(%d)", s.edit_count)
ImGui.TreePop()
end
if ImGui.TreeNode("Resize Callback") then
IMGUI_DEMO_MARKER("Widgets/Text Input/Resize Callback")
local s = S_ti.resize
-- To wire InputText() with std::string or any other custom string type,
-- you can use the ImGuiInputTextFlags_CallbackResize flag + create a custom ImGui::InputText() wrapper
-- using your preferred type. See misc/cpp/imgui_stdlib.h for an implementation of this using std::string.
HelpMarker(
"Using ImGuiInputTextFlags_CallbackResize to wire your custom string type to InputText().\n\n" ..
"See misc/cpp/imgui_stdlib.h for an implementation of this for std::string.")
local Funcs = {}
function Funcs.MyResizeCallback(data)
if data.EventFlag == 4194304 --[[ImGuiInputTextFlags.CallbackResize]] then
local my_str = data.UserData
assert(my_str.Data == data.Buf)
my_str:resize(data.BufSize, 0) -- NB: On resizing calls, generally data->BufSize == data->BufTextLen + 1 -- [Port] new bytes filled with 0
data.Buf = my_str.Data
end
return 0
end
-- Note: Because ImGui:: is a namespace you would typically add your own function into the namespace.
-- For example, you code may declare a function 'ImGui::InputText(const char* label, MyString* my_str)'
function Funcs.MyInputTextMultiline(label, my_str, size, flags)
if size == nil then size = ImVec2(0, 0) end
if flags == nil then flags = 0 end
assert(bit32.band(flags, 4194304 --[[ImGuiInputTextFlags.CallbackResize]]) == 0)
return ImGui.InputTextMultiline(label, my_str.Data, my_str.Size, size, bit32.bor(flags, 4194304 --[[ImGuiInputTextFlags.CallbackResize]]), Funcs.MyResizeCallback, my_str)
end
-- For this demo we are using ImVector as a string container.
-- Note that because we need to store a terminating zero character, our size/capacity are 1 more
-- than usually reported by a typical string class.
_, s.flags = ImGui.CheckboxFlags("ImGuiInputTextFlags_WordWrap", s.flags, 16777216 --[[ImGuiInputTextFlags.WordWrap]])
local my_str = s.my_str
if my_str:empty() then
my_str:push_back(0)
end
Funcs.MyInputTextMultiline("##MyStr", my_str, ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], ImGui.GetTextLineHeight() * 16), s.flags)
ImGui.Text("Data: %s\nSize: %d\nCapacity: %d", tostring(my_str.Data), my_str.Size, my_str.Capacity) -- [Port] %p -> %s + tostring
ImGui.TreePop()
end
if ImGui.TreeNode("Eliding, Alignment") then
IMGUI_DEMO_MARKER("Widgets/Text Input/Eliding, Alignment")
local s = S_ti.eliding
_, s.flags = ImGui.CheckboxFlags("ImGuiInputTextFlags_ElideLeft", s.flags, 131072 --[[ImGuiInputTextFlags.ElideLeft]])
ImGui.InputText("Path", s.buf1, 128, s.flags)
ImGui.TreePop()
end
if ImGui.TreeNode("Miscellaneous") then
IMGUI_DEMO_MARKER("Widgets/Text Input/Miscellaneous")
local s = S_ti.misc
_, s.flags = ImGui.CheckboxFlags("ImGuiInputTextFlags_EscapeClearsAll", s.flags, 128 --[[ImGuiInputTextFlags.EscapeClearsAll]])
_, s.flags = ImGui.CheckboxFlags("ImGuiInputTextFlags_ReadOnly", s.flags, 512 --[[ImGuiInputTextFlags.ReadOnly]])
_, s.flags = ImGui.CheckboxFlags("ImGuiInputTextFlags_NoUndoRedo", s.flags, 65536 --[[ImGuiInputTextFlags.NoUndoRedo]])
ImGui.InputText("Hello", s.buf1, 16, s.flags)
ImGui.TreePop()
end
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsTooltips()
-------------------------------------------------------------------------------
S.DemoWindowWidgetsTooltips = {
arr = { 0.6, 0.1, 1.0, 0.5, 0.92, 0.1, 0.2 },
always_on = 0,
}
function DemoWindowWidgetsTooltips()
local s = S.DemoWindowWidgetsTooltips
if ImGui.TreeNode("Tooltips") then
IMGUI_DEMO_MARKER("Widgets/Tooltips")
-- Tooltips are windows following the mouse. They do not take focus away.
ImGui.SeparatorText("General")
-- Typical use cases:
-- - Short-form (text only): SetItemTooltip("Hello");
-- - Short-form (any contents): if (BeginItemTooltip()) { Text("Hello"); EndTooltip(); }
-- - Full-form (text only): if (IsItemHovered(...)) { SetTooltip("Hello"); }
-- - Full-form (any contents): if (IsItemHovered(...) && BeginTooltip()) { Text("Hello"); EndTooltip(); }
HelpMarker(
"Tooltip are typically created by using a IsItemHovered() + SetTooltip() sequence.\n\n" ..
"We provide a helper SetItemTooltip() function to perform the two with standards flags.")
local sz = ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], 0.0)
ImGui.Button("Basic", sz)
ImGui.SetItemTooltip("I am a tooltip")
ImGui.Button("Fancy", sz)
if ImGui.BeginItemTooltip() then
ImGui.Text("I am a fancy tooltip")
ImGui.PlotLines("Curve", s.arr, nil, #s.arr)
ImGui.Text("Sin(time) = %f", math.sin(ImGui.GetTime()))
ImGui.EndTooltip()
end
ImGui.SeparatorText("Always On")
-- Showcase NOT relying on a IsItemHovered() to emit a tooltip.
-- Here the tooltip is always emitted when 'always_on == true'.
_, s.always_on = ImGui.RadioButton("Off", s.always_on, 0)
ImGui.SameLine()
_, s.always_on = ImGui.RadioButton("Always On (Simple)", s.always_on, 1)
ImGui.SameLine()
_, s.always_on = ImGui.RadioButton("Always On (Advanced)", s.always_on, 2)
if s.always_on == 1 then
ImGui.SetTooltip("I am following you around.")
elseif s.always_on == 2 and ImGui.BeginTooltip() then
ImGui.ProgressBar(math.sin(ImGui.GetTime()) * 0.5 + 0.5, ImVec2(ImGui.GetFontSize() * 25, 0.0))
ImGui.EndTooltip()
end
ImGui.SeparatorText("Custom")
HelpMarker(
"Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() is the preferred way to standardize " ..
"tooltip activation details across your application. You may however decide to use custom " ..
"flags for a specific tooltip instance.")
-- The following examples are passed for documentation purpose but may not be useful to most users.
-- Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() will pull ImGuiHoveredFlags flags values from
-- 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on whether mouse or keyboard/gamepad is being used.
-- With default settings, ImGuiHoveredFlags_ForTooltip is equivalent to ImGuiHoveredFlags_DelayShort + ImGuiHoveredFlags_Stationary.
ImGui.Button("Manual", sz)
if ImGui.IsItemHovered(4096 --[[ImGuiHoveredFlags.ForTooltip]]) then
ImGui.SetTooltip("I am a manually emitted tooltip.")
end
ImGui.Button("DelayNone", sz)
if ImGui.IsItemHovered(16384 --[[ImGuiHoveredFlags.DelayNone]]) then
ImGui.SetTooltip("I am a tooltip with no delay.")
end
ImGui.Button("DelayShort", sz)
if ImGui.IsItemHovered(bit32.bor(32768 --[[ImGuiHoveredFlags.DelayShort]], 131072 --[[ImGuiHoveredFlags.NoSharedDelay]])) then
ImGui.SetTooltip("I am a tooltip with a short delay (%0.2f sec).", ImGui.GetStyle().HoverDelayShort)
end
ImGui.Button("DelayLong", sz)
if ImGui.IsItemHovered(bit32.bor(65536 --[[ImGuiHoveredFlags.DelayNormal]], 131072 --[[ImGuiHoveredFlags.NoSharedDelay]])) then
ImGui.SetTooltip("I am a tooltip with a long delay (%0.2f sec).", ImGui.GetStyle().HoverDelayNormal)
end
ImGui.Button("Stationary", sz)
if ImGui.IsItemHovered(8192 --[[ImGuiHoveredFlags.Stationary]]) then
ImGui.SetTooltip("I am a tooltip requiring mouse to be stationary before activating.")
end
-- Using ImGuiHoveredFlags_ForTooltip will pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav',
-- which default value include the ImGuiHoveredFlags_AllowWhenDisabled flag.
ImGui.BeginDisabled()
ImGui.Button("Disabled item", sz)
if ImGui.IsItemHovered(4096 --[[ImGuiHoveredFlags.ForTooltip]]) then
ImGui.SetTooltip("I am a tooltip for a disabled item.")
end
ImGui.EndDisabled()
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsTreeNodes()
-------------------------------------------------------------------------------
S.DemoWindowWidgetsTreeNodes = {
base_flags = bit32.bor(ImGuiTreeNodeFlags.DrawLinesFull, ImGuiTreeNodeFlags.DefaultOpen), -- "Hierarchy Lines"
selection_mask = 0x00,
base_flags_2 = bit32.bor(ImGuiTreeNodeFlags.OpenOnArrow, ImGuiTreeNodeFlags.OpenOnDoubleClick, ImGuiTreeNodeFlags.SpanAvailWidth), -- "Advanced"
align_label_with_current_x_position = false,
use_drag_and_drop = false,
}
function DemoWindowWidgetsTreeNodes()
local s = S.DemoWindowWidgetsTreeNodes
if ImGui.TreeNode("Tree Nodes") then
IMGUI_DEMO_MARKER("Widgets/Tree Nodes")
-- See see "Examples -> Property Editor" (ShowExampleAppPropertyEditor() function) for a fancier, data-driven tree.
if ImGui.TreeNode("Basic Trees") then
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Basic Trees")
for i = 0, 4 do
-- Use SetNextItemOpen() so set the default state of a node to be open. We could
-- also use TreeNodeEx() with the ImGuiTreeNodeFlags_DefaultOpen flag to achieve the same thing!
if i == 0 then
ImGui.SetNextItemOpen(true, 2 --[[ImGuiCond.Once]])
end
-- Here we use PushID() to generate a unique base ID, and then the "" used as TreeNode id won't conflict.
-- An alternative to using 'PushID() + TreeNode("", ...)' to generate a unique ID is to use 'TreeNode((void*)(intptr_t)i, ...)',
-- aka generate a dummy pointer-sized value to be hashed. The demo below uses that technique. Both are fine.
ImGui.PushID(i)
if ImGui.TreeNode("", "Child %d", i) then
ImGui.Text("blah blah")
ImGui.SameLine()
if ImGui.SmallButton("button") then end
ImGui.TreePop()
end
ImGui.PopID()
end
ImGui.TreePop()
end
if ImGui.TreeNode("Hierarchy Lines") then
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Hierarchy Lines")
HelpMarker("Default option for DrawLinesXXX is stored in style.TreeLinesFlags")
_, s.base_flags = ImGui.CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesNone", s.base_flags, 262144 --[[ImGuiTreeNodeFlags.DrawLinesNone]])
_, s.base_flags = ImGui.CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesFull", s.base_flags, 524288 --[[ImGuiTreeNodeFlags.DrawLinesFull]])
_, s.base_flags = ImGui.CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesToNodes", s.base_flags, 1048576 --[[ImGuiTreeNodeFlags.DrawLinesToNodes]])
if ImGui.TreeNodeEx("Parent", s.base_flags) then
if ImGui.TreeNodeEx("Child 1", s.base_flags) then
ImGui.Button("Button for Child 1")
ImGui.TreePop()
end
if ImGui.TreeNodeEx("Child 2", s.base_flags) then
ImGui.Button("Button for Child 2")
ImGui.TreePop()
end
ImGui.Text("Remaining contents")
ImGui.Text("Remaining contents")
ImGui.TreePop()
end
ImGui.TreePop()
end
if ImGui.TreeNode("Clipping Large Trees") then
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Clipping Large Trees")
ImGui.TextWrapped(
"- Using ImGuiListClipper with trees is a less easy than on arrays or grids.\n" ..
"- Refer to 'Demo->Examples->Property Editor' for an example of how to do that.\n" ..
"- Discuss in #3823")
ImGui.TreePop()
end
if ImGui.TreeNode("Selectable Nodes") then
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Selectable Nodes")
HelpMarker(
"Manually implemented selectable nodes.\n" ..
"Click to select, Ctrl+Click to toggle, click on arrows or double-click to open.\n\n" ..
"You may also use the multi-select API (see 'Demo->Widgets->Selection State & Multi-Select') for more advanced multi-selection features.")
-- Hold in 'selection_mask' a simple representation of what may be user-side selection state.
-- - You may retain selection state inside or outside your objects in whatever format you see fit.
-- You may use ImGuiSelectionBasicStorage which is conceptually close to a set<> of identifiers.
-- - We record which node was clicked and then apply selection at the end of the loop.
-- - This is a manual and simplified reimplementation of multi-selection, which the full
-- BeginMultiSelect() API implements better, but which is not trivial to wire for trees.
local node_clicked_idx = -1
for node_n = 0, 5 do
-- Disable the default "open on single-click behavior" + set Selected flag according to our selection.
-- To alter selection we use if 'IsItemClicked() && !IsItemToggledOpen()', so clicking on an arrow doesn't alter selection.
-- In a BeginMultiSelect()/EndMultiSelect() we could use IsItemToggledSelection() but here we reimplement and use our own logic.
local flags = bit32.bor(128 --[[ImGuiTreeNodeFlags.OpenOnArrow]], 64 --[[ImGuiTreeNodeFlags.OpenOnDoubleClick]], 2048 --[[ImGuiTreeNodeFlags.SpanAvailWidth]])
if bit32.band(s.selection_mask, bit32.lshift(1, node_n)) ~= 0 then
flags = bit32.bor(flags, 1 --[[ImGuiTreeNodeFlags.Selected]])
end
local is_open = ImGui.TreeNodeEx(node_n, flags, "Selectable Node %d", node_n) -- [Port] (void*)(intptr_t)node_n -> node_n
if ImGui.IsItemClicked() and not ImGui.IsItemToggledOpen() then
node_clicked_idx = node_n
end
if is_open then
ImGui.BulletText("<Node contents here>")
ImGui.TreePop()
end
end
if node_clicked_idx ~= -1 then
-- Update selection state (process outside of tree loop to avoid visual inconsistencies during the clicking frame)
if ImGui.GetIO().KeyCtrl then
s.selection_mask = bit32.bxor(s.selection_mask, bit32.lshift(1, node_clicked_idx)) -- Ctrl+Click to toggle
else --if (!(selection_mask & (1 << node_clicked_idx))) -- Depending on selection behavior you want, may want to preserve selection when clicking on item that is part of the selection
s.selection_mask = bit32.lshift(1, node_clicked_idx) -- Click to single-select
end
end
ImGui.TreePop()
end
if ImGui.TreeNode("Advanced") then
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Advanced")
_, s.base_flags_2 = ImGui.CheckboxFlags("ImGuiTreeNodeFlags_OpenOnArrow", s.base_flags_2, 128 --[[ImGuiTreeNodeFlags.OpenOnArrow]])
_, s.base_flags_2 = ImGui.CheckboxFlags("ImGuiTreeNodeFlags_OpenOnDoubleClick", s.base_flags_2, 64 --[[ImGuiTreeNodeFlags.OpenOnDoubleClick]])
_, s.base_flags_2 = ImGui.CheckboxFlags("ImGuiTreeNodeFlags_SpanAvailWidth", s.base_flags_2, 2048 --[[ImGuiTreeNodeFlags.SpanAvailWidth]]); ImGui.SameLine(); HelpMarker("Extend hit area to all available width instead of allowing more items to be laid out after the node.")
_, s.base_flags_2 = ImGui.CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", s.base_flags_2, 4096 --[[ImGuiTreeNodeFlags.SpanFullWidth]])
_, s.base_flags_2 = ImGui.CheckboxFlags("ImGuiTreeNodeFlags_SpanLabelWidth", s.base_flags_2, 8192 --[[ImGuiTreeNodeFlags.SpanLabelWidth]]); ImGui.SameLine(); HelpMarker("Reduce hit area to the text label and a bit of margin.")
_, s.base_flags_2 = ImGui.CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", s.base_flags_2, 16384 --[[ImGuiTreeNodeFlags.SpanAllColumns]]); ImGui.SameLine(); HelpMarker("For use in Tables only.")
_, s.base_flags_2 = ImGui.CheckboxFlags("ImGuiTreeNodeFlags_AllowOverlap", s.base_flags_2, 4 --[[ImGuiTreeNodeFlags.AllowOverlap]])
_, s.base_flags_2 = ImGui.CheckboxFlags("ImGuiTreeNodeFlags_Framed", s.base_flags_2, 2 --[[ImGuiTreeNodeFlags.Framed]]); ImGui.SameLine(); HelpMarker("Draw frame with background (e.g. for CollapsingHeader)")
_, s.base_flags_2 = ImGui.CheckboxFlags("ImGuiTreeNodeFlags_FramePadding", s.base_flags_2, 1024 --[[ImGuiTreeNodeFlags.FramePadding]])
_, s.base_flags_2 = ImGui.CheckboxFlags("ImGuiTreeNodeFlags_NavLeftJumpsToParent", s.base_flags_2, 131072 --[[ImGuiTreeNodeFlags.NavLeftJumpsToParent]])
HelpMarker("Default option for DrawLinesXXX is stored in style.TreeLinesFlags")
_, s.base_flags_2 = ImGui.CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesNone", s.base_flags_2, 262144 --[[ImGuiTreeNodeFlags.DrawLinesNone]])
_, s.base_flags_2 = ImGui.CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesFull", s.base_flags_2, 524288 --[[ImGuiTreeNodeFlags.DrawLinesFull]])
_, s.base_flags_2 = ImGui.CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesToNodes", s.base_flags_2, 1048576 --[[ImGuiTreeNodeFlags.DrawLinesToNodes]])
_, s.align_label_with_current_x_position = ImGui.Checkbox("Align label with current X position", s.align_label_with_current_x_position)
_, s.use_drag_and_drop = ImGui.Checkbox("Make Tree Nodes as drag & drop sources", s.use_drag_and_drop)
if s.align_label_with_current_x_position then
ImGui.Unindent(ImGui.GetTreeNodeToLabelSpacing())
end
for node_n = 0, 5 do
local node_flags = s.base_flags_2
if node_n < 3 then
-- Items 0..2 are Tree Node
local is_open = ImGui.TreeNodeEx(node_n, node_flags, "Selectable Node %d", node_n)
if s.use_drag_and_drop and ImGui.BeginDragDropSource() then
ImGui.SetDragDropPayload("MY_TREENODE_PAYLOAD_TYPE", nil, 0)
ImGui.Text("This is a drag and drop source")
ImGui.EndDragDropSource()
end
if node_n == 2 and bit32.band(s.base_flags_2, 8192 --[[ImGuiTreeNodeFlags.SpanLabelWidth]]) ~= 0 then
-- Item 2 has an additional inline button to help demonstrate SpanLabelWidth.
ImGui.SameLine()
if ImGui.SmallButton("button") then end
end
if is_open then
ImGui.BulletText("Blah blah\nBlah Blah")
ImGui.SameLine()
ImGui.SmallButton("Button")
ImGui.TreePop()
end
else
-- Items 3..5 are Tree Leaves
-- The only reason we use TreeNode at all is to allow selection of the leaf. Otherwise we can
-- use BulletText() or advance the cursor by GetTreeNodeToLabelSpacing() and call Text().
node_flags = bit32.bor(node_flags, 256 --[[ImGuiTreeNodeFlags.Leaf]], 8 --[[ImGuiTreeNodeFlags.NoTreePushOnOpen]]) -- ImGuiTreeNodeFlags_Bullet
ImGui.TreeNodeEx(node_n, node_flags, "Selectable Leaf %d", node_n)
if s.use_drag_and_drop and ImGui.BeginDragDropSource() then
ImGui.SetDragDropPayload("MY_TREENODE_PAYLOAD_TYPE", nil, 0)
ImGui.Text("This is a drag and drop source")
ImGui.EndDragDropSource()
end
end
end
if s.align_label_with_current_x_position then
ImGui.Indent(ImGui.GetTreeNodeToLabelSpacing())
end
ImGui.TreePop()
end
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgetsVerticalSliders()
-------------------------------------------------------------------------------
S.DemoWindowWidgetsVerticalSliders = {
int_value = 0,
values = { 0.0, 0.60, 0.35, 0.9, 0.70, 0.20, 0.0 },
values2 = { 0.20, 0.80, 0.40, 0.25 },
}
function DemoWindowWidgetsVerticalSliders()
local s = S.DemoWindowWidgetsVerticalSliders
if ImGui.TreeNode("Vertical Sliders") then
IMGUI_DEMO_MARKER("Widgets/Vertical Sliders")
local spacing = 4
ImGui.PushStyleVar(14 --[[ImGuiStyleVar.ItemSpacing]], ImVec2(spacing, spacing))
s.int_value = ImGui.VSliderInt("##int", ImVec2(18, 160), s.int_value, 0, 5)
ImGui.SameLine()
local values = s.values
ImGui.PushID("set1")
for i = 0, 6 do
if i > 0 then ImGui.SameLine() end
ImGui.PushID(i)
ImGui.PushStyleColor(7 --[[ImGuiCol.FrameBg]], ImColor.HSV(i / 7.0, 0.5, 0.5).Value)
ImGui.PushStyleColor(8 --[[ImGuiCol.FrameBgHovered]], ImColor.HSV(i / 7.0, 0.6, 0.5).Value)
ImGui.PushStyleColor(9 --[[ImGuiCol.FrameBgActive]], ImColor.HSV(i / 7.0, 0.7, 0.5).Value)
ImGui.PushStyleColor(20 --[[ImGuiCol.SliderGrab]], ImColor.HSV(i / 7.0, 0.9, 0.9).Value)
values[i + 1] = ImGui.VSliderFloat("##v", ImVec2(18, 160), values[i + 1], 0.0, 1.0, "")
if ImGui.IsItemActive() or ImGui.IsItemHovered() then
ImGui.SetTooltip("%.3f", values[i + 1])
end
ImGui.PopStyleColor(4)
ImGui.PopID()
end
ImGui.PopID()
ImGui.SameLine()
ImGui.PushID("set2")
local values2 = s.values2
local rows = 3
local small_slider_size = ImVec2(18, math.floor((160.0 - (rows - 1) * spacing) / rows))
for nx = 0, 3 do
if nx > 0 then ImGui.SameLine() end
ImGui.BeginGroup()
for ny = 0, rows - 1 do
ImGui.PushID(nx * rows + ny)
values2[nx + 1] = ImGui.VSliderFloat("##v", small_slider_size, values2[nx + 1], 0.0, 1.0, "")
if ImGui.IsItemActive() or ImGui.IsItemHovered() then
ImGui.SetTooltip("%.3f", values2[nx + 1])
end
ImGui.PopID()
end
ImGui.EndGroup()
end
ImGui.PopID()
ImGui.SameLine()
ImGui.PushID("set3")
for i = 0, 3 do
if i > 0 then ImGui.SameLine() end
ImGui.PushID(i)
ImGui.PushStyleVar(21 --[[ImGuiStyleVar.GrabMinSize]], 40)
values[i + 1] = ImGui.VSliderFloat("##v", ImVec2(40, 160), values[i + 1], 0.0, 1.0, "%.2f\nsec")
ImGui.PopStyleVar()
ImGui.PopID()
end
ImGui.PopID()
ImGui.PopStyleVar()
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowWidgets()
-------------------------------------------------------------------------------
function DemoWindowWidgets(demo_data)
--ImGui.SetNextItemOpen(true, ImGuiCond.Once)
if not ImGui.CollapsingHeader("Widgets") then
return
end
-- IMGUI_DEMO_MARKER("Widgets");
local disable_all = demo_data.DisableSections -- The Checkbox for that is inside the "Disabled" section at the bottom
local override_liveedit = demo_data.LiveEditOverride
if disable_all then
ImGui.BeginDisabled()
end
if override_liveedit then
ImGui.PushItemFlag(128 --[[ImGuiItemFlags.LiveEditOnInputText]], bit32.band(demo_data.LiveEditFlags, 128 --[[ImGuiItemFlags.LiveEditOnInputText]]) ~= 0)
ImGui.PushItemFlag(256 --[[ImGuiItemFlags.LiveEditOnInputScalar]], bit32.band(demo_data.LiveEditFlags, 256 --[[ImGuiItemFlags.LiveEditOnInputScalar]]) ~= 0)
end
DemoWindowWidgetsBasic()
DemoWindowWidgetsBullets()
DemoWindowWidgetsCollapsingHeaders()
DemoWindowWidgetsComboBoxes()
DemoWindowWidgetsColorAndPickers()
DemoWindowWidgetsDataTypes()
if disable_all then
ImGui.EndDisabled()
end
DemoWindowWidgetsDisableBlocks(demo_data)
if disable_all then
ImGui.BeginDisabled()
end
DemoWindowWidgetsDragAndDrop()
DemoWindowWidgetsDragsAndSliders()
DemoWindowWidgetsFonts()
DemoWindowWidgetsImages()
DemoWindowWidgetsListBoxes()
DemoWindowWidgetsLiveEdit(demo_data)
DemoWindowWidgetsMixedValues()
DemoWindowWidgetsMultiComponents()
DemoWindowWidgetsPlotting()
DemoWindowWidgetsProgressBars()
DemoWindowWidgetsQueryingStatuses()
DemoWindowWidgetsSelectables()
DemoWindowWidgetsSelectionAndMultiSelect(demo_data)
DemoWindowWidgetsTabs()
DemoWindowWidgetsText()
DemoWindowWidgetsTextFilter()
DemoWindowWidgetsTextInput()
DemoWindowWidgetsTooltips()
DemoWindowWidgetsTreeNodes()
DemoWindowWidgetsVerticalSliders()
if override_liveedit then
ImGui.PopItemFlag()
ImGui.PopItemFlag()
end
if disable_all then
ImGui.EndDisabled()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowLayout()
-------------------------------------------------------------------------------
S.DemoWindowLayout = {
-- Child windows
disable_mouse_wheel = false,
disable_menu = false,
draw_lines = 3,
max_height_in_lines = 10,
offset_x = 0,
override_bg_color = true,
child_flags = bit32.bor(ImGuiChildFlags.Borders, ImGuiChildFlags.ResizeX, ImGuiChildFlags.ResizeY),
-- Widgets Width
f = 0.0,
show_indented_items = true,
-- Basic Horizontal Layout
c1 = false, c2 = false, c3 = false, c4 = false,
f0 = 1.0, f1 = 2.0, f2 = 3.0,
item = -1,
selection = { 0, 1, 2, 3 },
-- Scrolling
track_item = 50,
enable_track = true,
enable_extra_decorations = false,
scroll_to_off_px = 0.0,
scroll_to_pos_px = 200.0,
lines = 7,
show_horizontal_contents_size_demo_window = false,
show_h_scrollbar = true,
show_button = true,
show_tree_nodes = true,
show_text_wrapped = false,
show_columns = true,
show_tab_bar = true,
show_child = false,
explicit_content_size = false,
contents_size_x = 300.0,
-- Text Clipping
size = ImVec2(100.0, 100.0),
offset = ImVec2(30.0, 30.0),
-- Overlap Mode
enable_allow_overlap = true,
}
function DemoWindowLayout()
local s = S.DemoWindowLayout
if not ImGui.CollapsingHeader("Layout & Scrolling") then
return
end
if ImGui.TreeNode("Child windows") then
IMGUI_DEMO_MARKER("Layout/Child windows")
ImGui.SeparatorText("Child windows")
HelpMarker("Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window.")
_, s.disable_mouse_wheel = ImGui.Checkbox("Disable Mouse Wheel", s.disable_mouse_wheel)
_, s.disable_menu = ImGui.Checkbox("Disable Menu", s.disable_menu)
-- Child 1: no border, enable horizontal scrollbar
do
local window_flags = 2048 --[[ImGuiWindowFlags.HorizontalScrollbar]]
if s.disable_mouse_wheel then
window_flags = bit32.bor(window_flags, 16 --[[ImGuiWindowFlags.NoScrollWithMouse]])
end
ImGui.BeginChild("ChildL", ImVec2(ImGui.GetContentRegionAvail().x * 0.5, 260), 0 --[[ImGuiChildFlags.None]], window_flags)
for i = 0, 99 do
ImGui.Text("%04d: scrollable region", i)
end
ImGui.EndChild()
end
ImGui.SameLine()
-- Child 2: rounded border
do
local window_flags = 0 --[[ImGuiWindowFlags.None]]
if s.disable_mouse_wheel then
window_flags = bit32.bor(window_flags, 16 --[[ImGuiWindowFlags.NoScrollWithMouse]])
end
if not s.disable_menu then
window_flags = bit32.bor(window_flags, 1024 --[[ImGuiWindowFlags.MenuBar]])
end
ImGui.PushStyleVar(7 --[[ImGuiStyleVar.ChildRounding]], 5.0)
ImGui.BeginChild("ChildR", ImVec2(0, 260), 1 --[[ImGuiChildFlags.Borders]], window_flags)
if not s.disable_menu and ImGui.BeginMenuBar() then
if ImGui.BeginMenu("Menu") then
ShowExampleMenuFile()
ImGui.EndMenu()
end
ImGui.EndMenuBar()
end
if ImGui.BeginTable("split", 2, bit32.bor(1 --[[ImGuiTableFlags.Resizable]], 16 --[[ImGuiTableFlags.NoSavedSettings]])) then
for i = 0, 99 do
local buf = string.format("%03d", i)
ImGui.TableNextColumn()
ImGui.Button(buf, ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], 0.0))
end
ImGui.EndTable()
end
ImGui.EndChild()
ImGui.PopStyleVar()
end
-- Child 3: manual-resize
ImGui.SeparatorText("Manual-resize")
do
HelpMarker("Drag bottom border to resize. Double-click bottom border to auto-fit to vertical contents.")
--if (ImGui.Button("Set Height to 200"))
-- ImGui.SetNextWindowSize(ImVec2(-FLT_MIN, 200.0f));
ImGui.PushStyleColor(3 --[[ImGuiCol.ChildBg]], ImGui.GetStyleColorVec4(7 --[[ImGuiCol.FrameBg]]))
if ImGui.BeginChild("ResizableChild", ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], ImGui.GetTextLineHeightWithSpacing() * 8), bit32.bor(1 --[[ImGuiChildFlags.Borders]], 8 --[[ImGuiChildFlags.ResizeY]])) then
for n = 0, 9 do
ImGui.Text("Line %04d", n)
end
end
ImGui.PopStyleColor()
ImGui.EndChild()
end
-- Child 4: auto-resizing height with a limit
ImGui.SeparatorText("Auto-resize with constraints")
do
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 8)
s.draw_lines = ImGui.DragInt("Lines Count", s.draw_lines, 0.2)
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 8)
s.max_height_in_lines = ImGui.DragInt("Max Height (in Lines)", s.max_height_in_lines, 0.2)
ImGui.SetNextWindowSizeConstraints(ImVec2(0.0, ImGui.GetTextLineHeightWithSpacing() * 1), ImVec2(3.4028234663853e+38 --[[FLT_MAX]], ImGui.GetTextLineHeightWithSpacing() * s.max_height_in_lines))
if ImGui.BeginChild("ConstrainedChild", ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], 0.0), bit32.bor(1 --[[ImGuiChildFlags.Borders]], 32 --[[ImGuiChildFlags.AutoResizeY]])) then
for n = 0, s.draw_lines - 1 do
ImGui.Text("Line %04d", n)
end
end
ImGui.EndChild()
end
ImGui.SeparatorText("Misc/Advanced")
-- Demonstrate a few extra things
-- - Changing ImGuiCol.ChildBg (which is transparent black in default styles)
-- - Using SetCursorPos() to position child window (the child window is an item from the POV of parent window)
-- You can also call SetNextWindowPos() to position the child window. The parent window will effectively
-- layout from this position.
-- - Using ImGui.GetItemRectMin/Max() to query the "item" state (because the child window is an item from
-- the POV of the parent window). See 'Demo->Querying Status (Edited/Active/Hovered etc.)' for details.
do
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 8)
s.offset_x = ImGui.DragInt("Offset X", s.offset_x, 1.0, -1000, 1000)
_, s.override_bg_color = ImGui.Checkbox("Override ChildBg color", s.override_bg_color)
_, s.child_flags = ImGui.CheckboxFlags("ImGuiChildFlags_Borders", s.child_flags, 1 --[[ImGuiChildFlags.Borders]])
_, s.child_flags = ImGui.CheckboxFlags("ImGuiChildFlags_AlwaysUseWindowPadding", s.child_flags, 2 --[[ImGuiChildFlags.AlwaysUseWindowPadding]])
_, s.child_flags = ImGui.CheckboxFlags("ImGuiChildFlags_ResizeX", s.child_flags, 4 --[[ImGuiChildFlags.ResizeX]])
_, s.child_flags = ImGui.CheckboxFlags("ImGuiChildFlags_ResizeY", s.child_flags, 8 --[[ImGuiChildFlags.ResizeY]])
_, s.child_flags = ImGui.CheckboxFlags("ImGuiChildFlags_FrameStyle", s.child_flags, 128 --[[ImGuiChildFlags.FrameStyle]])
ImGui.SameLine(); HelpMarker("Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding.")
if bit32.band(s.child_flags, 128 --[[ImGuiChildFlags.FrameStyle]]) ~= 0 then
s.override_bg_color = false
end
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + s.offset_x)
if s.override_bg_color then
ImGui.PushStyleColor(3 --[[ImGuiCol.ChildBg]], IM_COL32(255, 0, 0, 100))
end
ImGui.BeginChild("Red", ImVec2(200, 100), s.child_flags, 0 --[[ImGuiWindowFlags.None]])
if s.override_bg_color then
ImGui.PopStyleColor()
end
for n = 0, 49 do
ImGui.Text("Some test %d", n)
end
ImGui.EndChild()
local child_is_hovered = ImGui.IsItemHovered()
local child_rect_min = ImGui.GetItemRectMin()
local child_rect_max = ImGui.GetItemRectMax()
ImGui.Text("Hovered: %d", child_is_hovered and 1 or 0)
ImGui.Text("Rect of child window is: (%.0f,%.0f) (%.0f,%.0f)", child_rect_min.x, child_rect_min.y, child_rect_max.x, child_rect_max.y)
end
ImGui.TreePop()
end
if ImGui.TreeNode("Widgets Width") then
IMGUI_DEMO_MARKER("Layout/Widgets Width")
_, s.show_indented_items = ImGui.Checkbox("Show indented items", s.show_indented_items)
-- Use SetNextItemWidth() to set the width of a single upcoming item.
-- Use PushItemWidth()/PopItemWidth() to set the width of a group of items.
-- In real code use you'll probably want to choose width values that are proportional to your font size
-- e.g. Using '20.0f * GetFontSize()' as width instead of '200.0f', etc.
ImGui.Text("SetNextItemWidth/PushItemWidth(100)")
ImGui.SameLine(); HelpMarker("Fixed width.")
ImGui.PushItemWidth(100)
s.f = ImGui.DragFloat("float##1b", s.f)
if s.show_indented_items then
ImGui.Indent()
s.f = ImGui.DragFloat("float (indented)##1b", s.f)
ImGui.Unindent()
end
ImGui.PopItemWidth()
ImGui.Text("SetNextItemWidth/PushItemWidth(-100)")
ImGui.SameLine(); HelpMarker("Align to right edge minus 100")
ImGui.PushItemWidth(-100)
s.f = ImGui.DragFloat("float##2a", s.f)
if s.show_indented_items then
ImGui.Indent()
s.f = ImGui.DragFloat("float (indented)##2b", s.f)
ImGui.Unindent()
end
ImGui.PopItemWidth()
ImGui.Text("SetNextItemWidth/PushItemWidth(GetContentRegionAvail().x * 0.5f)")
ImGui.SameLine(); HelpMarker("Half of available width.\n(~ right-cursor_pos)\n(works within a column set)")
ImGui.PushItemWidth(ImGui.GetContentRegionAvail().x * 0.5)
s.f = ImGui.DragFloat("float##3a", s.f)
if s.show_indented_items then
ImGui.Indent()
s.f = ImGui.DragFloat("float (indented)##3b", s.f)
ImGui.Unindent()
end
ImGui.PopItemWidth()
ImGui.Text("SetNextItemWidth/PushItemWidth(-GetContentRegionAvail().x * 0.5f)")
ImGui.SameLine(); HelpMarker("Align to right edge minus half")
ImGui.PushItemWidth(-ImGui.GetContentRegionAvail().x * 0.5)
s.f = ImGui.DragFloat("float##4a", s.f)
if s.show_indented_items then
ImGui.Indent()
s.f = ImGui.DragFloat("float (indented)##4b", s.f)
ImGui.Unindent()
end
ImGui.PopItemWidth()
ImGui.Text("SetNextItemWidth/PushItemWidth(-Min(GetContentRegionAvail().x * 0.40f, GetFontSize() * 12))")
ImGui.PushItemWidth(-ImMin(ImGui.GetFontSize() * 12, ImGui.GetContentRegionAvail().x * 0.40))
s.f = ImGui.DragFloat("float##5a", s.f)
if s.show_indented_items then
ImGui.Indent()
s.f = ImGui.DragFloat("float (indented)##5b", s.f)
ImGui.Unindent()
end
ImGui.PopItemWidth()
-- Demonstrate using PushItemWidth to surround three items.
-- Calling SetNextItemWidth() before each of them would have the same effect.
ImGui.Text("SetNextItemWidth/PushItemWidth(-FLT_MIN)")
ImGui.SameLine(); HelpMarker("Align to right edge")
ImGui.PushItemWidth(-1.1754943508223e-38 --[[FLT_MIN]])
s.f = ImGui.DragFloat("##float6a", s.f)
if s.show_indented_items then
ImGui.Indent()
s.f = ImGui.DragFloat("float (indented)##6b", s.f)
ImGui.Unindent()
end
ImGui.PopItemWidth()
ImGui.TreePop()
end
if ImGui.TreeNode("Basic Horizontal Layout") then
IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout")
ImGui.TextWrapped("(Use ImGui::SameLine() to keep adding items to the right of the preceding item)")
-- Text
IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine")
ImGui.Text("Two items: Hello"); ImGui.SameLine()
ImGui.TextColored(ImVec4(1, 1, 0, 1), "Sailor")
-- Adjust spacing
ImGui.Text("More spacing: Hello"); ImGui.SameLine(0, 20)
ImGui.TextColored(ImVec4(1, 1, 0, 1), "Sailor")
-- Button
ImGui.AlignTextToFramePadding()
ImGui.Text("Normal buttons"); ImGui.SameLine()
ImGui.Button("Banana"); ImGui.SameLine()
ImGui.Button("Apple"); ImGui.SameLine()
ImGui.Button("Corniflower")
-- Button
ImGui.Text("Small buttons"); ImGui.SameLine()
ImGui.SmallButton("Like this one"); ImGui.SameLine()
ImGui.Text("can fit within a text block.")
-- Aligned to arbitrary position. Easy/cheap column.
IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (with offset)")
ImGui.Text("Aligned")
ImGui.SameLine(150); ImGui.Text("x=150")
ImGui.SameLine(300); ImGui.Text("x=300")
ImGui.Text("Aligned")
ImGui.SameLine(150); ImGui.SmallButton("x=150")
ImGui.SameLine(300); ImGui.SmallButton("x=300")
-- Checkbox
IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (more)")
_, s.c1 = ImGui.Checkbox("My", s.c1); ImGui.SameLine()
_, s.c2 = ImGui.Checkbox("Tailor", s.c2); ImGui.SameLine()
_, s.c3 = ImGui.Checkbox("Is", s.c3); ImGui.SameLine()
_, s.c4 = ImGui.Checkbox("Rich", s.c4)
-- Various
ImGui.PushItemWidth(ImGui.CalcTextSize("AAAAAAA").x)
local items = { "AAAA", "BBBB", "CCCC", "DDDD" }
s.item = ImGui.Combo("Combo", s.item, items, #items); ImGui.SameLine()
s.f0 = ImGui.SliderFloat("X", s.f0, 0.0, 5.0); ImGui.SameLine()
s.f1 = ImGui.SliderFloat("Y", s.f1, 0.0, 5.0); ImGui.SameLine()
s.f2 = ImGui.SliderFloat("Z", s.f2, 0.0, 5.0)
ImGui.Text("Lists:")
for i = 0, 3 do
if i > 0 then ImGui.SameLine() end
ImGui.PushID(i)
s.selection[i + 1] = ImGui.ListBox("", s.selection[i + 1], items, #items)
ImGui.PopID()
--ImGui.SetItemTooltip("ListBox %d hovered", i);
end
ImGui.PopItemWidth()
-- Dummy
IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Dummy")
local button_sz = ImVec2(40, 40)
ImGui.Button("A", button_sz); ImGui.SameLine()
ImGui.Dummy(button_sz); ImGui.SameLine()
ImGui.Button("B", button_sz)
-- Manually wrapping
-- (we should eventually provide this as an automatic layout feature, but for now you can do it manually)
IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Manual wrapping")
ImGui.Text("Manual wrapping:")
local style = ImGui.GetStyle()
local buttons_count = 20
local window_visible_x2 = ImGui.GetCursorScreenPos().x + ImGui.GetContentRegionAvail().x
for n = 0, buttons_count - 1 do
ImGui.PushID(n)
ImGui.Button("Box", button_sz)
local last_button_x2 = ImGui.GetItemRectMax().x
local next_button_x2 = last_button_x2 + style.ItemSpacing.x + button_sz.x -- Expected position if next button was on same line
if n + 1 < buttons_count and next_button_x2 < window_visible_x2 then
ImGui.SameLine()
end
ImGui.PopID()
end
ImGui.TreePop()
end
if ImGui.TreeNode("Groups") then
IMGUI_DEMO_MARKER("Layout/Groups")
HelpMarker(
"BeginGroup() basically locks the horizontal position for new line. " ..
"EndGroup() bundles the whole group so that you can use \"item\" functions such as " ..
"IsItemHovered()/IsItemActive() or SameLine() etc. on the whole group.")
ImGui.BeginGroup()
do
ImGui.BeginGroup()
ImGui.Button("AAA")
ImGui.SameLine()
ImGui.Button("BBB")
ImGui.SameLine()
ImGui.BeginGroup()
ImGui.Button("CCC")
ImGui.Button("DDD")
ImGui.EndGroup()
ImGui.SameLine()
ImGui.Button("EEE")
ImGui.EndGroup()
ImGui.SetItemTooltip("First group hovered")
end
-- Capture the group size and create widgets using the same size
local size = ImGui.GetItemRectSize()
local values = { 0.5, 0.20, 0.80, 0.60, 0.25 }
ImGui.PlotHistogram("##values", values, nil, #values, 0, nil, 0.0, 1.0, size)
ImGui.Button("ACTION", ImVec2((size.x - ImGui.GetStyle().ItemSpacing.x) * 0.5, size.y))
ImGui.SameLine()
ImGui.Button("REACTION", ImVec2((size.x - ImGui.GetStyle().ItemSpacing.x) * 0.5, size.y))
ImGui.EndGroup()
ImGui.SameLine()
ImGui.Button("LEVERAGE\nBUZZWORD", size)
ImGui.SameLine()
if ImGui.BeginListBox("List", size) then
ImGui.Selectable("Selected", true)
ImGui.Selectable("Not Selected", false)
ImGui.EndListBox()
end
ImGui.TreePop()
end
if ImGui.TreeNode("Text Baseline Alignment") then
IMGUI_DEMO_MARKER("Layout/Text Baseline Alignment")
do
ImGui.BulletText("Text baseline:")
ImGui.SameLine(); HelpMarker(
"This is testing the vertical alignment that gets applied on text to keep it aligned with widgets. " ..
"Lines only composed of text or \"small\" widgets use less vertical space than lines with framed widgets.")
ImGui.Indent()
ImGui.Text("KO Blahblah"); ImGui.SameLine()
ImGui.Button("Some framed item"); ImGui.SameLine()
HelpMarker("Baseline of button will look misaligned with text..")
-- If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets.
-- (because we don't know what's coming after the Text() statement, we need to move the text baseline
-- down by FramePadding.y ahead of time)
ImGui.AlignTextToFramePadding()
ImGui.Text("OK Blahblah"); ImGui.SameLine()
ImGui.Button("Some framed item##2"); ImGui.SameLine()
HelpMarker("We call AlignTextToFramePadding() to vertically align the text baseline by +FramePadding.y")
-- SmallButton() uses the same vertical padding as Text
ImGui.Button("TEST##1"); ImGui.SameLine()
ImGui.Text("TEST"); ImGui.SameLine()
ImGui.SmallButton("TEST##2")
-- If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets.
ImGui.AlignTextToFramePadding()
ImGui.Text("Text aligned to framed item"); ImGui.SameLine()
ImGui.Button("Item##1"); ImGui.SameLine()
ImGui.Text("Item"); ImGui.SameLine()
ImGui.SmallButton("Item##2"); ImGui.SameLine()
ImGui.Button("Item##3")
ImGui.Unindent()
end
ImGui.Spacing()
do
ImGui.BulletText("Multi-line text:")
ImGui.Indent()
ImGui.Text("One\nTwo\nThree"); ImGui.SameLine()
ImGui.Text("Hello\nWorld"); ImGui.SameLine()
ImGui.Text("Banana")
ImGui.Text("Banana"); ImGui.SameLine()
ImGui.Text("Hello\nWorld"); ImGui.SameLine()
ImGui.Text("One\nTwo\nThree")
ImGui.Button("HOP##1"); ImGui.SameLine()
ImGui.Text("Banana"); ImGui.SameLine()
ImGui.Text("Hello\nWorld"); ImGui.SameLine()
ImGui.Text("Banana")
ImGui.Button("HOP##2"); ImGui.SameLine()
ImGui.Text("Hello\nWorld"); ImGui.SameLine()
ImGui.Text("Banana")
ImGui.Unindent()
end
ImGui.Spacing()
do
ImGui.BulletText("Misc items:")
ImGui.Indent()
-- SmallButton() sets FramePadding to zero. Text baseline is aligned to match baseline of previous Button.
ImGui.Button("80x80", ImVec2(80, 80))
ImGui.SameLine()
ImGui.Button("50x50", ImVec2(50, 50))
ImGui.SameLine()
ImGui.Button("Button()")
ImGui.SameLine()
ImGui.SmallButton("SmallButton()")
-- Tree
-- (here the node appears after a button and has odd intent, so we use ImGuiTreeNodeFlags.DrawLinesNone to disable hierarchy outline)
local spacing = ImGui.GetStyle().ItemInnerSpacing.x
ImGui.Button("Button##1") -- Will make line higher
ImGui.SameLine(0.0, spacing)
if ImGui.TreeNodeEx("Node##1", 262144 --[[ImGuiTreeNodeFlags.DrawLinesNone]]) then
-- Placeholder tree data
for i = 0, 5 do
ImGui.BulletText("Item %d..", i)
end
ImGui.TreePop()
end
local padding = math.floor(ImGui.GetFontSize() * 1.20) -- Large padding
ImGui.PushStyleVarY(11 --[[ImGuiStyleVar.FramePadding]], padding)
ImGui.Button("Button##2")
ImGui.PopStyleVar()
ImGui.SameLine(0.0, spacing)
if ImGui.TreeNodeEx("Node##2", 262144 --[[ImGuiTreeNodeFlags.DrawLinesNone]]) then
ImGui.TreePop()
end
-- Vertically align text node a bit lower so it'll be vertically centered with upcoming widget.
-- Otherwise you can use SmallButton() (smaller fit).
ImGui.AlignTextToFramePadding()
-- Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add
-- other contents "inside" the node.
local node_open = ImGui.TreeNode("Node##3")
ImGui.SameLine(0.0, spacing); ImGui.Button("Button##3")
if node_open then
-- Placeholder tree data
for i = 0, 5 do
ImGui.BulletText("Item %d..", i)
end
ImGui.TreePop()
end
-- Bullet
ImGui.Button("Button##4")
ImGui.SameLine(0.0, spacing)
ImGui.BulletText("Bullet text")
ImGui.AlignTextToFramePadding()
ImGui.BulletText("Node")
ImGui.SameLine(0.0, spacing); ImGui.Button("Button##5")
ImGui.Unindent()
end
ImGui.TreePop()
end
if ImGui.TreeNode("Scrolling") then
IMGUI_DEMO_MARKER("Layout/Scrolling/Vertical")
-- Vertical scroll functions
HelpMarker("Use SetScrollHereY() or SetScrollFromPosY() to scroll to a given vertical position.")
_, s.enable_extra_decorations = ImGui.Checkbox("Decoration", s.enable_extra_decorations)
ImGui.PushItemWidth(ImGui.GetFontSize() * 10)
local track_changed
s.track_item, track_changed = ImGui.DragInt("##item", s.track_item, 0.25, 0, 99, "Item = %d")
s.enable_track = s.enable_track or track_changed
ImGui.SameLine()
_, s.enable_track = ImGui.Checkbox("Track", s.enable_track)
local scroll_to_off
s.scroll_to_off_px, scroll_to_off = ImGui.DragFloat("##off", s.scroll_to_off_px, 1.00, 0, 3.4028234663853e+38 --[[FLT_MAX]], "+%.0f px")
ImGui.SameLine()
scroll_to_off = ImGui.Button("Scroll Offset") or scroll_to_off
local scroll_to_pos
s.scroll_to_pos_px, scroll_to_pos = ImGui.DragFloat("##pos", s.scroll_to_pos_px, 1.00, -10, 3.4028234663853e+38 --[[FLT_MAX]], "X/Y = %.0f px")
ImGui.SameLine()
scroll_to_pos = ImGui.Button("Scroll To Pos") or scroll_to_pos
ImGui.PopItemWidth()
if scroll_to_off or scroll_to_pos then
s.enable_track = false
end
local style = ImGui.GetStyle()
local child_w = (ImGui.GetContentRegionAvail().x - 4 * style.ItemSpacing.x) / 5
if child_w < 1.0 then
child_w = 1.0
end
ImGui.PushID("##VerticalScrolling")
for i = 0, 4 do
if i > 0 then ImGui.SameLine() end
ImGui.BeginGroup()
local names = { "Top", "25%", "Center", "75%", "Bottom" }
ImGui.TextUnformatted(names[i + 1])
local child_flags = 0
if s.enable_extra_decorations then child_flags = 1024 --[[ImGuiWindowFlags.MenuBar]] end
local child_id = ImGui.GetID(i)
local child_is_visible = ImGui.BeginChild(child_id, ImVec2(child_w, 200.0), 1 --[[ImGuiChildFlags.Borders]], child_flags)
if ImGui.BeginMenuBar() then
ImGui.TextUnformatted("abc")
ImGui.EndMenuBar()
end
if scroll_to_off then
ImGui.SetScrollY(s.scroll_to_off_px)
end
if scroll_to_pos then
ImGui.SetScrollFromPosY(ImGui.GetCursorStartPos().y + s.scroll_to_pos_px, i * 0.25)
end
if child_is_visible then -- Avoid calling SetScrollHereY when running with culled items
for item = 0, 99 do
if s.enable_track and item == s.track_item then
ImGui.TextColored(ImVec4(1, 1, 0, 1), "Item %d", item)
ImGui.SetScrollHereY(i * 0.25) -- 0.0f:top, 0.5f:center, 1.0f:bottom
else
ImGui.Text("Item %d", item)
end
end
end
local scroll_y = ImGui.GetScrollY()
local scroll_max_y = ImGui.GetScrollMaxY()
ImGui.EndChild()
ImGui.Text("%.0f/%.0f", scroll_y, scroll_max_y)
ImGui.EndGroup()
end
ImGui.PopID()
-- Horizontal scroll functions
IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal")
ImGui.Spacing()
HelpMarker(
"Use SetScrollHereX() or SetScrollFromPosX() to scroll to a given horizontal position.\n\n" ..
"Because the clipping rectangle of most window hides half worth of WindowPadding on the " ..
"left/right, using SetScrollFromPosX(+1) will usually result in clipped text whereas the " ..
"equivalent SetScrollFromPosY(+1) wouldn't.")
ImGui.PushID("##HorizontalScrolling")
for i = 0, 4 do
local child_height = ImGui.GetTextLineHeight() + style.ScrollbarSize + style.WindowPadding.y * 2.0
local child_flags = 2048 --[[ImGuiWindowFlags.HorizontalScrollbar]]
if s.enable_extra_decorations then child_flags = bit32.bor(child_flags, 16384 --[[ImGuiWindowFlags.AlwaysVerticalScrollbar]]) end
local child_id = ImGui.GetID(i)
local child_is_visible = ImGui.BeginChild(child_id, ImVec2(-100, child_height), 1 --[[ImGuiChildFlags.Borders]], child_flags)
if scroll_to_off then
ImGui.SetScrollX(s.scroll_to_off_px)
end
if scroll_to_pos then
ImGui.SetScrollFromPosX(ImGui.GetCursorStartPos().x + s.scroll_to_pos_px, i * 0.25)
end
if child_is_visible then -- Avoid calling SetScrollHereY when running with culled items
for item = 0, 99 do
if item > 0 then
ImGui.SameLine()
end
if s.enable_track and item == s.track_item then
ImGui.TextColored(ImVec4(1, 1, 0, 1), "Item %d", item)
ImGui.SetScrollHereX(i * 0.25) -- 0.0f:left, 0.5f:center, 1.0f:right
else
ImGui.Text("Item %d", item)
end
end
end
local scroll_x = ImGui.GetScrollX()
local scroll_max_x = ImGui.GetScrollMaxX()
ImGui.EndChild()
ImGui.SameLine()
local names = { "Left", "25%", "Center", "75%", "Right" }
ImGui.Text("%s\n%.0f/%.0f", names[i + 1], scroll_x, scroll_max_x)
ImGui.Spacing()
end
ImGui.PopID()
-- Miscellaneous Horizontal Scrolling Demo
IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal (more)")
HelpMarker(
"Horizontal scrolling for a window is enabled via the ImGuiWindowFlags_HorizontalScrollbar flag.\n\n" ..
"You may want to also explicitly specify content width by using SetNextWindowContentWidth() before Begin().")
s.lines = ImGui.SliderInt("Lines", s.lines, 1, 15)
ImGui.PushStyleVar(12 --[[ImGuiStyleVar.FrameRounding]], 3.0)
ImGui.PushStyleVar(11 --[[ImGuiStyleVar.FramePadding]], ImVec2(2.0, 1.0))
local scrolling_child_size = ImVec2(0, ImGui.GetFrameHeightWithSpacing() * 7 + 30)
ImGui.BeginChild("scrolling", scrolling_child_size, 1 --[[ImGuiChildFlags.Borders]], 2048 --[[ImGuiWindowFlags.HorizontalScrollbar]])
for line = 0, s.lines - 1 do
-- Display random stuff. For the sake of this trivial demo we are using basic Button() + SameLine()
-- If you want to create your own time line for a real application you may be better off manipulating
-- the cursor position yourself, aka using SetCursorPos/SetCursorScreenPos to position the widgets
-- yourself. You may also want to use the lower-level ImDrawList API.
local num_buttons = 10 + ((line % 2 == 1) and line * 9 or line * 3)
local base_w = ImGui.GetFontSize() * 3
for n = 0, num_buttons - 1 do
if n > 0 then ImGui.SameLine() end
ImGui.PushID(n + line * 1000)
local num_buf = string.format("%d", n)
local label = num_buf
if n % 15 == 0 then label = "FizzBuzz" elseif n % 3 == 0 then label = "Fizz" elseif n % 5 == 0 then label = "Buzz" end
local hue = n * 0.05
ImGui.PushStyleColor(22 --[[ImGuiCol.Button]], ImColor.HSV(hue, 0.6, 0.6).Value)
ImGui.PushStyleColor(23 --[[ImGuiCol.ButtonHovered]], ImColor.HSV(hue, 0.7, 0.7).Value)
ImGui.PushStyleColor(24 --[[ImGuiCol.ButtonActive]], ImColor.HSV(hue, 0.8, 0.8).Value)
ImGui.Button(label, ImVec2(base_w + math.sin(line + n) * base_w * 0.5, 0.0))
ImGui.PopStyleColor(3)
ImGui.PopID()
end
end
local scroll_x = ImGui.GetScrollX()
local scroll_max_x = ImGui.GetScrollMaxX()
ImGui.EndChild()
ImGui.PopStyleVar(2)
local scroll_x_delta = 0.0
ImGui.SmallButton("<<")
if ImGui.IsItemActive() then
scroll_x_delta = -ImGui.GetIO().DeltaTime * 1000.0
end
ImGui.SameLine()
ImGui.Text("Scroll from code"); ImGui.SameLine()
ImGui.SmallButton(">>")
if ImGui.IsItemActive() then
scroll_x_delta = ImGui.GetIO().DeltaTime * 1000.0
end
ImGui.SameLine()
ImGui.Text("%.0f/%.0f", scroll_x, scroll_max_x)
if scroll_x_delta ~= 0.0 then
-- Demonstrate a trick: you can use Begin to set yourself in the context of another window
-- (here we are already out of your child window)
ImGui.BeginChild("scrolling")
ImGui.SetScrollX(ImGui.GetScrollX() + scroll_x_delta)
ImGui.EndChild()
end
ImGui.Spacing()
_, s.show_horizontal_contents_size_demo_window = ImGui.Checkbox("Show Horizontal contents size demo window", s.show_horizontal_contents_size_demo_window)
if s.show_horizontal_contents_size_demo_window then
if s.explicit_content_size then
ImGui.SetNextWindowContentSize(ImVec2(s.contents_size_x, 0.0))
end
local window_flags = 0
if s.show_h_scrollbar then window_flags = 2048 --[[ImGuiWindowFlags.HorizontalScrollbar]] end
s.show_horizontal_contents_size_demo_window = ImGui.Begin("Horizontal contents size demo window", s.show_horizontal_contents_size_demo_window, window_flags)
IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal contents size demo window")
ImGui.PushStyleVar(14 --[[ImGuiStyleVar.ItemSpacing]], ImVec2(2, 0))
ImGui.PushStyleVar(11 --[[ImGuiStyleVar.FramePadding]], ImVec2(2, 0))
HelpMarker(
"Test how different widgets react and impact the work rectangle growing when horizontal scrolling is enabled.\n\n" ..
"Use 'Metrics->Tools->Show windows rectangles' to visualize rectangles.")
_, s.show_h_scrollbar = ImGui.Checkbox("H-scrollbar", s.show_h_scrollbar)
_, s.show_button = ImGui.Checkbox("Button", s.show_button) -- Will grow contents size (unless explicitly overwritten)
_, s.show_tree_nodes = ImGui.Checkbox("Tree nodes", s.show_tree_nodes) -- Will grow contents size and display highlight over full width
_, s.show_text_wrapped = ImGui.Checkbox("Text wrapped", s.show_text_wrapped)-- Will grow and use contents size
_, s.show_columns = ImGui.Checkbox("Columns", s.show_columns) -- Will use contents size
_, s.show_tab_bar = ImGui.Checkbox("Tab bar", s.show_tab_bar) -- Will use contents size
_, s.show_child = ImGui.Checkbox("Child", s.show_child) -- Will grow and use contents size
_, s.explicit_content_size = ImGui.Checkbox("Explicit content size", s.explicit_content_size)
ImGui.Text("Scroll %.1f/%.1f %.1f/%.1f", ImGui.GetScrollX(), ImGui.GetScrollMaxX(), ImGui.GetScrollY(), ImGui.GetScrollMaxY())
if s.explicit_content_size then
ImGui.SameLine()
ImGui.SetNextItemWidth(ImGui.CalcTextSize("123456").x)
s.contents_size_x = ImGui.DragFloat("##csx", s.contents_size_x)
local p = ImGui.GetCursorScreenPos()
ImGui.GetWindowDrawList():AddRectFilled(p, ImVec2(p.x + 10, p.y + 10), 4294967295 --[[IM_COL32_WHITE]])
ImGui.GetWindowDrawList():AddRectFilled(ImVec2(p.x + s.contents_size_x - 10, p.y), ImVec2(p.x + s.contents_size_x, p.y + 10), 4294967295 --[[IM_COL32_WHITE]])
ImGui.Dummy(ImVec2(0, 10))
end
ImGui.PopStyleVar(2)
ImGui.Separator()
if s.show_button then
ImGui.Button("this is a 300-wide button", ImVec2(300, 0))
end
if s.show_tree_nodes then
local open = true
if ImGui.TreeNode("this is a tree node") then
if ImGui.TreeNode("another one of those tree node...") then
ImGui.Text("Some tree contents")
ImGui.TreePop()
end
ImGui.TreePop()
end
_, open = ImGui.CollapsingHeader("CollapsingHeader", open)
end
if s.show_text_wrapped then
ImGui.TextWrapped("This text should automatically wrap on the edge of the work rectangle.")
end
if s.show_columns then
ImGui.Text("Tables:")
if ImGui.BeginTable("table", 4, 1920 --[[ImGuiTableFlags.Borders]]) then
for n = 0, 3 do
ImGui.TableNextColumn()
ImGui.Text("Width %.2f", ImGui.GetContentRegionAvail().x)
end
ImGui.EndTable()
end
ImGui.Text("Columns:")
ImGui.Columns(4)
for n = 0, 3 do
ImGui.Text("Width %.2f", ImGui.GetColumnWidth())
ImGui.NextColumn()
end
ImGui.Columns(1)
end
if s.show_tab_bar and ImGui.BeginTabBar("Hello") then
if ImGui.BeginTabItem("OneOneOne") then ImGui.EndTabItem() end
if ImGui.BeginTabItem("TwoTwoTwo") then ImGui.EndTabItem() end
if ImGui.BeginTabItem("ThreeThreeThree") then ImGui.EndTabItem() end
if ImGui.BeginTabItem("FourFourFour") then ImGui.EndTabItem() end
ImGui.EndTabBar()
end
if s.show_child then
ImGui.BeginChild("child", ImVec2(0, 0), 1 --[[ImGuiChildFlags.Borders]])
ImGui.EndChild()
end
ImGui.End()
end
ImGui.TreePop()
end
if ImGui.TreeNode("Text Clipping") then
IMGUI_DEMO_MARKER("Layout/Text Clipping")
local size = s.size
local offset = s.offset
ImGui.DragFloat2("size", size, 0.5, 1.0, 200.0, "%.0f")
ImGui.TextWrapped("(Click and drag to scroll)")
HelpMarker(
"(Left) Using ImGui::PushClipRect():\n" ..
"Will alter ImGui hit-testing logic + ImDrawList rendering.\n" ..
"(use this if you want your clipping rectangle to affect interactions)\n\n" ..
"(Center) Using ImDrawList::PushClipRect():\n" ..
"Will alter ImDrawList rendering only.\n" ..
"(use this as a shortcut if you are only using ImDrawList calls)\n\n" ..
"(Right) Using ImDrawList::AddText() with a fine ClipRect:\n" ..
"Will alter only this specific ImDrawList::AddText() rendering.\n" ..
"This is often used internally to avoid altering the clipping rectangle and minimize draw calls.")
for n = 0, 2 do repeat
if n > 0 then
ImGui.SameLine()
end
ImGui.PushID(n)
ImGui.InvisibleButton("##canvas", size)
if ImGui.IsItemActive() and ImGui.IsMouseDragging(0 --[[ImGuiMouseButton.Left]]) then
offset.x = offset.x + ImGui.GetIO().MouseDelta.x
offset.y = offset.y + ImGui.GetIO().MouseDelta.y
end
ImGui.PopID()
if not ImGui.IsItemVisible() then -- Skip rendering as ImDrawList elements are not clipped.
break -- continue
end
local p0 = ImGui.GetItemRectMin()
local p1 = ImGui.GetItemRectMax()
local text_str = "Line 1 hello\nLine 2 clip me!"
local text_pos = ImVec2(p0.x + offset.x, p0.y + offset.y)
local draw_list = ImGui.GetWindowDrawList()
if n == 0 then
ImGui.PushClipRect(p0, p1, true)
draw_list:AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255))
draw_list:AddText(text_pos, 4294967295 --[[IM_COL32_WHITE]], text_str)
ImGui.PopClipRect()
elseif n == 1 then
draw_list:PushClipRect(p0, p1, true)
draw_list:AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255))
draw_list:AddText(text_pos, 4294967295 --[[IM_COL32_WHITE]], text_str)
draw_list:PopClipRect()
elseif n == 2 then
local clip_rect = ImVec4(p0.x, p0.y, p1.x, p1.y) -- AddText() takes a ImVec4* here so let's convert.
draw_list:AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255))
draw_list:AddText(ImGui.GetFont(), ImGui.GetFontSize(), text_pos, 4294967295 --[[IM_COL32_WHITE]], text_str, 1, #text_str + 1, 0.0, clip_rect) -- [Port] explicit text_begin/text_end (port's AddText needs text_end)
end
until true end
ImGui.TreePop()
end
if ImGui.TreeNode("Overlap Mode") then
IMGUI_DEMO_MARKER("Layout/Overlap Mode")
HelpMarker(
"Hit-testing is by default performed in item submission order, which generally is perceived as 'back-to-front'.\n\n" ..
"By using SetNextItemAllowOverlap() you can notify that an item may be overlapped by another. " ..
"Doing so alters the hovering logic: items using AllowOverlap mode requires an extra frame to accept hovered state.")
_, s.enable_allow_overlap = ImGui.Checkbox("Enable AllowOverlap", s.enable_allow_overlap)
local button1_pos = ImGui.GetCursorScreenPos()
local button2_pos = ImVec2(button1_pos.x + 50.0, button1_pos.y + 50.0)
if s.enable_allow_overlap then
ImGui.SetNextItemAllowOverlap()
end
ImGui.Button("Button 1", ImVec2(80, 80))
ImGui.SetCursorScreenPos(button2_pos)
ImGui.Button("Button 2", ImVec2(80, 80))
-- This is typically used with width-spanning items.
-- (note that Selectable() has a dedicated flag ImGuiSelectableFlags.AllowOverlap, which is a shortcut
-- for using SetNextItemAllowOverlap(). For demo purpose we use SetNextItemAllowOverlap() here.)
if s.enable_allow_overlap then
ImGui.SetNextItemAllowOverlap()
end
ImGui.Selectable("Some Selectable", false)
ImGui.SameLine()
ImGui.SmallButton("++")
ImGui.TreePop()
end
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowPopups()
-------------------------------------------------------------------------------
S.DemoWindowPopups = {
selected_fish = -1, -- static int selected_fish = -1;
toggles = { true, false, false, false, false }, -- static bool toggles[] = { true, false, false, false, false };
selected = -1, -- static int selected = -1;
value = 0.5, -- static float value = 0.5f;
name = ImGui.NewTextBuffer("Label1", 32), -- static char name[32] = "Label1";
dont_ask_me_next_time = false, -- static bool dont_ask_me_next_time = false;
item = 1, -- static int item = 1;
color = { 0.4, 0.7, 0.0, 0.5 }, -- static float color[4] = { 0.4f, 0.7f, 0.0f, 0.5f };
}
function DemoWindowPopups()
local s = S.DemoWindowPopups
if not ImGui.CollapsingHeader("Popups & Modal windows") then
return
end
-- The properties of popups windows are:
-- - They block normal mouse hovering detection outside them. (*)
-- - Unless modal, they can be closed by clicking anywhere outside them, or by pressing ESCAPE.
-- - Their visibility state (~bool) is held internally by Dear ImGui instead of being held by the programmer as
-- we are used to with regular Begin() calls. User can manipulate the visibility state by calling OpenPopup().
-- (*) One can use IsItemHovered(ImGuiHoveredFlags.AllowWhenBlockedByPopup) to bypass it and detect hovering even
-- when normally blocked by a popup.
-- Those three properties are connected. The library needs to hold their visibility state BECAUSE it can close
-- popups at any time.
-- Typical use for regular windows:
-- bool my_tool_is_active = false; if (ImGui.Button("Open")) my_tool_is_active = true; [...] if (my_tool_is_active) Begin("My Tool", my_tool_is_active) { [...] } End();
-- Typical use for popups:
-- if (ImGui.Button("Open")) ImGui.OpenPopup("MyPopup"); if (ImGui.BeginPopup("MyPopup")) { [...] EndPopup(); }
-- With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state.
-- This may be a bit confusing at first but it should quickly make sense. Follow on the examples below.
if ImGui.TreeNode("Popups") then
IMGUI_DEMO_MARKER("Popups/Popups")
ImGui.TextWrapped(
"When a popup is active, it inhibits interacting with windows that are behind the popup. " ..
"Clicking outside the popup closes it.")
local names = { "Bream", "Haddock", "Mackerel", "Pollock", "Tilefish" }
local toggles = s.toggles
-- Simple selection popup (if you want to show the current selection inside the Button itself,
-- you may want to build a string using the "###" operator to preserve a constant ID with a variable label)
if ImGui.Button("Select..") then
ImGui.OpenPopup("my_select_popup")
end
ImGui.SameLine()
local fish_name = "<None>"
if s.selected_fish ~= -1 then fish_name = names[s.selected_fish + 1] end
ImGui.TextUnformatted(fish_name)
if ImGui.BeginPopup("my_select_popup") then
ImGui.SeparatorText("Aquarium")
for i = 0, #names - 1 do
if ImGui.Selectable(names[i + 1]) then
s.selected_fish = i
end
end
ImGui.EndPopup()
end
-- Showing a menu with toggles
if ImGui.Button("Toggle..") then
ImGui.OpenPopup("my_toggle_popup")
end
if ImGui.BeginPopup("my_toggle_popup") then
for i = 0, #names - 1 do
_, toggles[i + 1] = ImGui.MenuItem(names[i + 1], "", toggles[i + 1])
end
if ImGui.BeginMenu("Sub-menu") then
ImGui.MenuItem("Click me")
ImGui.EndMenu()
end
ImGui.Separator()
ImGui.Text("Tooltip here")
ImGui.SetItemTooltip("I am a tooltip over a popup")
if ImGui.Button("Stacked Popup") then
ImGui.OpenPopup("another popup")
end
if ImGui.BeginPopup("another popup") then
for i = 0, #names - 1 do
_, toggles[i + 1] = ImGui.MenuItem(names[i + 1], "", toggles[i + 1])
end
if ImGui.BeginMenu("Sub-menu") then
ImGui.MenuItem("Click me")
if ImGui.Button("Stacked Popup") then
ImGui.OpenPopup("another popup")
end
if ImGui.BeginPopup("another popup") then
ImGui.Text("I am the last one here.")
ImGui.EndPopup()
end
ImGui.EndMenu()
end
ImGui.EndPopup()
end
ImGui.EndPopup()
end
-- Call the more complete ShowExampleMenuFile which we use in various places of this demo
if ImGui.Button("With a menu..") then
ImGui.OpenPopup("my_file_popup")
end
if ImGui.BeginPopup("my_file_popup", 1024 --[[ImGuiWindowFlags.MenuBar]]) then
if ImGui.BeginMenuBar() then
if ImGui.BeginMenu("File") then
ShowExampleMenuFile()
ImGui.EndMenu()
end
if ImGui.BeginMenu("Edit") then
ImGui.MenuItem("Dummy")
ImGui.EndMenu()
end
ImGui.EndMenuBar()
end
ImGui.Text("Hello from popup!")
ImGui.Button("This is a dummy button..")
ImGui.EndPopup()
end
ImGui.TreePop()
end
if ImGui.TreeNode("Context menus") then
IMGUI_DEMO_MARKER("Popups/Context menus")
HelpMarker("\"Context\" functions are simple helpers to associate a Popup to a given Item or Window identifier.")
-- BeginPopupContextItem() is a helper to provide common/simple popup behavior of essentially doing:
-- if (id == 0)
-- id = GetItemID(); -- Use last item id
-- if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton.Right))
-- OpenPopup(id);
-- return BeginPopup(id);
-- For advanced uses you may want to replicate and customize this code.
-- See more details in BeginPopupContextItem().
-- Example 1
-- When used after an item that has an ID (e.g. Button), we can skip providing an ID to BeginPopupContextItem(),
-- and BeginPopupContextItem() will use the last item ID as the popup ID.
do
local names = { "Label1", "Label2", "Label3", "Label4", "Label5" }
for n = 0, 4 do
if ImGui.Selectable(names[n + 1], s.selected == n) then
s.selected = n
end
if ImGui.BeginPopupContextItem() then -- <-- use last item id as popup id
s.selected = n
ImGui.Text("This is a popup for \"%s\"!", names[n + 1])
if ImGui.Button("Close") then
ImGui.CloseCurrentPopup()
end
ImGui.EndPopup()
end
ImGui.SetItemTooltip("Right-click to open popup")
end
end
-- Example 2
-- Popup on a Text() element which doesn't have an identifier: we need to provide an identifier to BeginPopupContextItem().
-- Using an explicit identifier is also convenient if you want to activate the popups from different locations.
do
HelpMarker("Text() elements don't have stable identifiers so we need to provide one.")
ImGui.Text("Value = %.3f <-- (1) right-click this text", s.value)
if ImGui.BeginPopupContextItem("my popup") then
if ImGui.Selectable("Set to zero") then s.value = 0.0 end
if ImGui.Selectable("Set to PI") then s.value = 3.1415 end
ImGui.SetNextItemWidth(-1.1754943508223e-38 --[[FLT_MIN]])
s.value = ImGui.DragFloat("##Value", s.value, 0.1, 0.0, 0.0)
ImGui.EndPopup()
end
-- We can also use OpenPopupOnItemClick() to toggle the visibility of a given popup.
-- Here we make it that right-clicking this other text element opens the same popup as above.
-- The popup itself will be submitted by the code above.
ImGui.Text("(2) Or right-click this text")
ImGui.OpenPopupOnItemClick("my popup", 8 --[[ImGuiPopupFlags.MouseButtonRight]])
-- Back to square one: manually open the same popup.
if ImGui.Button("(3) Or click this button") then
ImGui.OpenPopup("my popup")
end
end
-- Example 3
-- When using BeginPopupContextItem() with an implicit identifier (nil == use last item ID),
-- we need to make sure your item identifier is stable.
-- In this example we showcase altering the item label while preserving its identifier, using the ### operator (see FAQ).
do
HelpMarker("Showcase using a popup ID linked to item ID, with the item having a changing label + stable ID using the ### operator.")
local buf = string.format("Button: %s###Button", ImGui.TextBufferToString(s.name)) -- ### operator override ID ignoring the preceding label
ImGui.Button(buf)
if ImGui.BeginPopupContextItem() then
ImGui.Text("Edit name:")
ImGui.InputText("##edit", s.name, 32)
if ImGui.Button("Close") then
ImGui.CloseCurrentPopup()
end
ImGui.EndPopup()
end
ImGui.SameLine(); ImGui.Text("(<-- right-click here)")
end
ImGui.TreePop()
end
if ImGui.TreeNode("Modals") then
IMGUI_DEMO_MARKER("Popups/Modals")
ImGui.TextWrapped("Modal windows are like popups but the user cannot close them by clicking outside.")
if ImGui.Button("Delete..") then
ImGui.OpenPopup("Delete?")
end
-- Always center this window when appearing
local center = ImGui.GetMainViewport():GetCenter()
ImGui.SetNextWindowPos(center, 8 --[[ImGuiCond.Appearing]], ImVec2(0.5, 0.5))
if ImGui.BeginPopupModal("Delete?", nil, 64 --[[ImGuiWindowFlags.AlwaysAutoResize]]) then
ImGui.Text("All those beautiful files will be deleted.\nThis operation cannot be undone!")
ImGui.Separator()
--static int unused_i = 0;
--ImGui.Combo("Combo", unused_i, "Delete\0Delete harder\0");
ImGui.PushStyleVar(11 --[[ImGuiStyleVar.FramePadding]], ImVec2(0, 0))
_, s.dont_ask_me_next_time = ImGui.Checkbox("Don't ask me next time", s.dont_ask_me_next_time)
ImGui.PopStyleVar()
if ImGui.Button("OK", ImVec2(120, 0)) then ImGui.CloseCurrentPopup() end
ImGui.SetItemDefaultFocus()
ImGui.SameLine()
if ImGui.Button("Cancel", ImVec2(120, 0)) then ImGui.CloseCurrentPopup() end
ImGui.EndPopup()
end
if ImGui.Button("Stacked modals..") then
ImGui.OpenPopup("Stacked 1")
end
if ImGui.BeginPopupModal("Stacked 1", nil, 1024 --[[ImGuiWindowFlags.MenuBar]]) then
if ImGui.BeginMenuBar() then
if ImGui.BeginMenu("File") then
if ImGui.MenuItem("Some menu item") then end
ImGui.EndMenu()
end
ImGui.EndMenuBar()
end
ImGui.Text("Hello from Stacked The First\nUsing style.Colors[ImGuiCol_ModalWindowDimBg] behind it.")
-- Testing behavior of widgets stacking their own regular popups over the modal.
s.item = ImGui.Combo("Combo", s.item, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0")
ImGui.ColorEdit4("Color", s.color)
if ImGui.Button("Add another modal..") then
ImGui.OpenPopup("Stacked 2")
end
-- Also demonstrate passing a bool* to BeginPopupModal(), this will create a regular close button which
-- will close the popup. Note that the visibility state of popups is owned by imgui, so the input value
-- of the bool actually doesn't matter here.
local unused_open = true
local visible
visible, unused_open = ImGui.BeginPopupModal("Stacked 2", unused_open)
if visible then
ImGui.Text("Hello from Stacked The Second!")
ImGui.ColorEdit4("Color", s.color) -- Allow opening another nested popup
if ImGui.Button("Close") then
ImGui.CloseCurrentPopup()
end
ImGui.EndPopup()
end
if ImGui.Button("Close") then
ImGui.CloseCurrentPopup()
end
ImGui.EndPopup()
end
ImGui.TreePop()
end
if ImGui.TreeNode("Menus inside a regular window") then
IMGUI_DEMO_MARKER("Popups/Menus inside a regular window")
ImGui.TextWrapped("Below we are testing adding menu items to a regular window. It's rather unusual but should work!")
ImGui.Separator()
ImGui.MenuItem("Menu item", "Ctrl+M")
if ImGui.BeginMenu("Menu inside a regular window") then
ShowExampleMenuFile()
ImGui.EndMenu()
end
ImGui.Separator()
ImGui.TreePop()
end
end
-- Dummy data structure that we use for the Table demo.
-- (pre-C++11 doesn't allow us to instantiate ImVector<MyItem> template if this structure is defined inside the demo function)
-- [Port] namespace { ... } -> globals with a MyItem prefix (part 08_tables uses them).
-- We are passing our own identifier to TableSetupColumn() to facilitate identifying columns in the sorting code.
-- This identifier will be passed down into ImGuiTableSortSpec::ColumnUserID.
-- But it is possible to omit the user id parameter of TableSetupColumn() and just use the column index instead! (ImGuiTableSortSpec::ColumnIndex)
-- If you don't use sorting, you will generally never care about giving column an ID!
-- enum MyItemColumnID
MyItemColumnID_ID = 0
MyItemColumnID_Name = 1
MyItemColumnID_Action = 2
MyItemColumnID_Quantity = 3
MyItemColumnID_Description = 4
-- struct MyItem: [Port] constructor; fields ID (int), Name (string), Quantity (int)
function MyItem(ID, Name, Quantity)
return { ID = ID, Name = Name, Quantity = Quantity }
end
-- We have a problem which is affecting _only this demo_ and should not affect your code:
-- As we don't rely on std:: or other third-party library to compile dear imgui, we only have reliable access to qsort(),
-- however qsort doesn't allow passing user data to comparing function.
-- As a workaround, we are storing the sort specs in a static/global for the comparing function to access.
-- In your own use case you would probably pass the sort specs to your sorting/comparing functions directly and not use a global.
-- We could technically call ImGui.TableGetSortSpecs() in CompareWithSortSpecs(), but considering that this function is called
-- very often by the sorting algorithm it would be a little wasteful.
MyItem_s_current_sort_specs = nil
-- [Port] items: 1-based Lua array of MyItem (e.g. ImVector .Data), sorted in place over [1, items_count].
local function MyItem_Less(a, b) return MyItem_CompareWithSortSpecs(a, b) < 0 end -- [Port] table.sort wants a "less than" predicate
function MyItem_SortWithSortSpecs(sort_specs, items, items_count)
MyItem_s_current_sort_specs = sort_specs -- Store in variable accessible by the sort function.
if items_count > 1 then
if items_count == #items then
table.sort(items, MyItem_Less)
else -- [Port] sort only the used range
local tmp = {}
for i = 1, items_count do tmp[i] = items[i] end
table.sort(tmp, MyItem_Less)
for i = 1, items_count do items[i] = tmp[i] end
end
end
MyItem_s_current_sort_specs = nil
end
-- Compare function to be used by qsort()
-- [Port] strcmp -> Lua string comparison; returns <0, 0, >0 like upstream (strict weak order via the ID fallback).
function MyItem_CompareWithSortSpecs(a, b)
for n = 0, MyItem_s_current_sort_specs.SpecsCount - 1 do
-- Here we identify columns using the ColumnUserID value that we ourselves passed to TableSetupColumn()
-- We could also choose to identify columns based on their index (sort_spec.ColumnIndex), which is simpler!
local sort_spec = MyItem_s_current_sort_specs.Specs[n + 1] -- [Port] Specs is a 1-based array
local delta = 0
local id = sort_spec.ColumnUserID
if id == MyItemColumnID_ID then delta = (a.ID - b.ID)
elseif id == MyItemColumnID_Name then delta = (a.Name < b.Name and -1 or (a.Name > b.Name and 1 or 0))
elseif id == MyItemColumnID_Quantity then delta = (a.Quantity - b.Quantity)
elseif id == MyItemColumnID_Description then delta = (a.Name < b.Name and -1 or (a.Name > b.Name and 1 or 0))
else assert(false) end
if delta > 0 then
return (sort_spec.SortDirection == 1 --[[ImGuiSortDirection.Ascending]]) and 1 or -1
end
if delta < 0 then
return (sort_spec.SortDirection == 1 --[[ImGuiSortDirection.Ascending]]) and -1 or 1
end
end
-- qsort() is instable so always return a way to differentiate items.
-- Your own compare function may want to avoid fallback on implicit sort specs.
-- e.g. a Name compare if it wasn't already part of the sort specs.
return a.ID - b.ID
end
-- Make the UI compact because there are so many fields
function PushStyleCompact()
local style = ImGui.GetStyle()
ImGui.PushStyleVarY(11 --[[ImGuiStyleVar.FramePadding]], math.floor(style.FramePadding.y * 0.60))
ImGui.PushStyleVarY(14 --[[ImGuiStyleVar.ItemSpacing]], math.floor(style.ItemSpacing.y * 0.60))
end
function PopStyleCompact()
ImGui.PopStyleVar(2)
end
S.EditTableSizingFlags = { policies = nil }
-- Show a combo box with a choice of sizing policies
-- [Port] p_flags -> takes flags, returns the new flags.
function EditTableSizingFlags(p_flags)
local s = S.EditTableSizingFlags
-- struct EnumDesc { ImGuiTableFlags Value; const char* Name; const char* Tooltip; };
if s.policies == nil then
s.policies =
{
{ Value = 0 --[[ImGuiTableFlags.None]], Name = "Default", Tooltip = "Use default sizing policy:\n- ImGuiTableFlags_SizingFixedFit if ScrollX is on or if host window has ImGuiWindowFlags_AlwaysAutoResize.\n- ImGuiTableFlags_SizingStretchSame otherwise." },
{ Value = 8192 --[[ImGuiTableFlags.SizingFixedFit]], Name = "ImGuiTableFlags_SizingFixedFit", Tooltip = "Columns default to _WidthFixed (if resizable) or _WidthAuto (if not resizable), matching contents width." },
{ Value = 16384 --[[ImGuiTableFlags.SizingFixedSame]], Name = "ImGuiTableFlags_SizingFixedSame", Tooltip = "Columns are all the same width, matching the maximum contents width.\nImplicitly disable ImGuiTableFlags_Resizable and enable ImGuiTableFlags_NoKeepColumnsVisible." },
{ Value = 24576 --[[ImGuiTableFlags.SizingStretchProp]], Name = "ImGuiTableFlags_SizingStretchProp", Tooltip = "Columns default to _WidthStretch with weights proportional to their widths." },
{ Value = 32768 --[[ImGuiTableFlags.SizingStretchSame]], Name = "ImGuiTableFlags_SizingStretchSame", Tooltip = "Columns default to _WidthStretch with same weights." },
}
end
local policies = s.policies
local idx = 0
while idx < #policies do
if policies[idx + 1].Value == bit32.band(p_flags, 57344 --[[ImGuiTableFlags.SizingMask_]]) then
break
end
idx = idx + 1
end
local preview_text = ""
if idx < #policies then
preview_text = policies[idx + 1].Name
if idx > 0 then preview_text = string.sub(preview_text, #"ImGuiTableFlags" + 1) end
end
if ImGui.BeginCombo("Sizing Policy", preview_text) then
for n = 0, #policies - 1 do
if ImGui.Selectable(policies[n + 1].Name, idx == n) then
p_flags = bit32.bor(bit32.band(p_flags, bit32.bnot(57344 --[[ImGuiTableFlags.SizingMask_]])), policies[n + 1].Value)
end
end
ImGui.EndCombo()
end
ImGui.SameLine()
ImGui.TextDisabled("(?)")
if ImGui.BeginItemTooltip() then
ImGui.PushTextWrapPos(ImGui.GetFontSize() * 50.0)
for m = 0, #policies - 1 do
ImGui.Separator()
ImGui.Text("%s:", policies[m + 1].Name)
ImGui.Separator()
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().IndentSpacing * 0.5)
ImGui.TextUnformatted(policies[m + 1].Tooltip)
end
ImGui.PopTextWrapPos()
ImGui.EndTooltip()
end
return p_flags
end
-- [Port] p_flags -> takes flags, returns the new flags.
function EditTableColumnsFlags(p_flags)
local pressed
_, p_flags = ImGui.CheckboxFlags("_Disabled", p_flags, 1 --[[ImGuiTableColumnFlags.Disabled]]); ImGui.SameLine(); HelpMarker("Master disable flag (also hide from context menu)")
_, p_flags = ImGui.CheckboxFlags("_DefaultHide", p_flags, 2 --[[ImGuiTableColumnFlags.DefaultHide]])
_, p_flags = ImGui.CheckboxFlags("_DefaultSort", p_flags, 4 --[[ImGuiTableColumnFlags.DefaultSort]])
pressed, p_flags = ImGui.CheckboxFlags("_WidthStretch", p_flags, 8 --[[ImGuiTableColumnFlags.WidthStretch]])
if pressed then
p_flags = bit32.band(p_flags, bit32.bnot(bit32.bxor(24 --[[ImGuiTableColumnFlags.WidthMask_]], 8 --[[ImGuiTableColumnFlags.WidthStretch]])))
end
pressed, p_flags = ImGui.CheckboxFlags("_WidthFixed", p_flags, 16 --[[ImGuiTableColumnFlags.WidthFixed]])
if pressed then
p_flags = bit32.band(p_flags, bit32.bnot(bit32.bxor(24 --[[ImGuiTableColumnFlags.WidthMask_]], 16 --[[ImGuiTableColumnFlags.WidthFixed]])))
end
_, p_flags = ImGui.CheckboxFlags("_NoResize", p_flags, 32 --[[ImGuiTableColumnFlags.NoResize]])
_, p_flags = ImGui.CheckboxFlags("_NoReorder", p_flags, 64 --[[ImGuiTableColumnFlags.NoReorder]])
_, p_flags = ImGui.CheckboxFlags("_NoHide", p_flags, 128 --[[ImGuiTableColumnFlags.NoHide]])
_, p_flags = ImGui.CheckboxFlags("_NoClip", p_flags, 256 --[[ImGuiTableColumnFlags.NoClip]])
_, p_flags = ImGui.CheckboxFlags("_NoSort", p_flags, 512 --[[ImGuiTableColumnFlags.NoSort]])
_, p_flags = ImGui.CheckboxFlags("_NoSortAscending", p_flags, 1024 --[[ImGuiTableColumnFlags.NoSortAscending]])
_, p_flags = ImGui.CheckboxFlags("_NoSortDescending", p_flags, 2048 --[[ImGuiTableColumnFlags.NoSortDescending]])
_, p_flags = ImGui.CheckboxFlags("_NoHeaderLabel", p_flags, 4096 --[[ImGuiTableColumnFlags.NoHeaderLabel]])
_, p_flags = ImGui.CheckboxFlags("_NoHeaderWidth", p_flags, 8192 --[[ImGuiTableColumnFlags.NoHeaderWidth]])
_, p_flags = ImGui.CheckboxFlags("_PreferSortAscending", p_flags, 16384 --[[ImGuiTableColumnFlags.PreferSortAscending]])
_, p_flags = ImGui.CheckboxFlags("_PreferSortDescending", p_flags, 32768 --[[ImGuiTableColumnFlags.PreferSortDescending]])
_, p_flags = ImGui.CheckboxFlags("_IndentEnable", p_flags, 65536 --[[ImGuiTableColumnFlags.IndentEnable]]); ImGui.SameLine(); HelpMarker("Default for column 0")
_, p_flags = ImGui.CheckboxFlags("_IndentDisable", p_flags, 131072 --[[ImGuiTableColumnFlags.IndentDisable]]); ImGui.SameLine(); HelpMarker("Default for column >0")
_, p_flags = ImGui.CheckboxFlags("_AngledHeader", p_flags, 262144 --[[ImGuiTableColumnFlags.AngledHeader]])
return p_flags
end
function ShowTableColumnsStatusFlags(flags)
ImGui.CheckboxFlags("_IsEnabled", flags, 16777216 --[[ImGuiTableColumnFlags.IsEnabled]])
ImGui.CheckboxFlags("_IsVisible", flags, 33554432 --[[ImGuiTableColumnFlags.IsVisible]])
ImGui.CheckboxFlags("_IsSorted", flags, 67108864 --[[ImGuiTableColumnFlags.IsSorted]])
ImGui.CheckboxFlags("_IsHovered", flags, 134217728 --[[ImGuiTableColumnFlags.IsHovered]])
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowTables()
-------------------------------------------------------------------------------
S.DemoWindowTables = { -- statics of DemoWindowTables(), one sub-table per TreeNode
disable_indent = false,
BordersBackground = {
flags = bit32.bor(ImGuiTableFlags.Borders, ImGuiTableFlags.RowBg),
display_headers = false,
contents_type = 0, -- CT_Text
},
ResizableStretch = {
flags = bit32.bor(ImGuiTableFlags.SizingStretchSame, ImGuiTableFlags.Resizable, ImGuiTableFlags.BordersOuter, ImGuiTableFlags.BordersV, ImGuiTableFlags.ContextMenuInBody),
},
ResizableFixed = {
flags = bit32.bor(ImGuiTableFlags.SizingFixedFit, ImGuiTableFlags.Resizable, ImGuiTableFlags.BordersOuter, ImGuiTableFlags.BordersV, ImGuiTableFlags.ContextMenuInBody),
},
ResizableMixed = {
flags = bit32.bor(ImGuiTableFlags.SizingFixedFit, ImGuiTableFlags.RowBg, ImGuiTableFlags.Borders, ImGuiTableFlags.Resizable, ImGuiTableFlags.Reorderable, ImGuiTableFlags.Hideable),
},
Reorderable = {
flags = bit32.bor(ImGuiTableFlags.Resizable, ImGuiTableFlags.Reorderable, ImGuiTableFlags.Hideable, ImGuiTableFlags.BordersOuter, ImGuiTableFlags.BordersV),
},
Padding = {
flags1 = ImGuiTableFlags.BordersV,
show_headers = false,
flags2 = bit32.bor(ImGuiTableFlags.Borders, ImGuiTableFlags.RowBg),
cell_padding = ImVec2(0.0, 0.0),
show_widget_frame_bg = true,
text_bufs = {}, -- 1-based, cell + 1
init = true,
},
SizingPolicies = {
flags1 = bit32.bor(ImGuiTableFlags.BordersV, ImGuiTableFlags.BordersOuterH, ImGuiTableFlags.RowBg, ImGuiTableFlags.ContextMenuInBody),
sizing_policy_flags = { ImGuiTableFlags.SizingFixedFit, ImGuiTableFlags.SizingFixedSame, ImGuiTableFlags.SizingStretchProp, ImGuiTableFlags.SizingStretchSame },
flags = bit32.bor(ImGuiTableFlags.ScrollY, ImGuiTableFlags.Borders, ImGuiTableFlags.RowBg, ImGuiTableFlags.Resizable),
contents_type = 0, -- CT_ShowWidth
column_count = 3,
text_buf = ImGui.NewTextBuffer("", 32),
},
VerticalScrolling = {
flags = bit32.bor(ImGuiTableFlags.ScrollY, ImGuiTableFlags.RowBg, ImGuiTableFlags.BordersOuter, ImGuiTableFlags.BordersV, ImGuiTableFlags.Resizable, ImGuiTableFlags.Reorderable, ImGuiTableFlags.Hideable),
},
HorizontalScrolling = {
flags = bit32.bor(ImGuiTableFlags.ScrollX, ImGuiTableFlags.ScrollY, ImGuiTableFlags.RowBg, ImGuiTableFlags.BordersOuter, ImGuiTableFlags.BordersV, ImGuiTableFlags.Resizable, ImGuiTableFlags.Reorderable, ImGuiTableFlags.Hideable),
freeze_cols = 1,
freeze_rows = 1,
flags2 = bit32.bor(ImGuiTableFlags.SizingStretchSame, ImGuiTableFlags.ScrollX, ImGuiTableFlags.ScrollY, ImGuiTableFlags.BordersOuter, ImGuiTableFlags.RowBg, ImGuiTableFlags.ContextMenuInBody),
inner_width = 1000.0,
},
ColumnsFlags = {
column_flags = { ImGuiTableColumnFlags.DefaultSort, ImGuiTableColumnFlags.None, ImGuiTableColumnFlags.DefaultHide },
column_flags_out = { 0, 0, 0 }, -- Output from TableGetColumnFlags()
},
ColumnsWidths = {
flags1 = bit32.bor(ImGuiTableFlags.Borders, ImGuiTableFlags.NoBordersInBodyUntilResize),
flags2 = ImGuiTableFlags.None,
},
OuterSize = {
flags = bit32.bor(ImGuiTableFlags.Borders, ImGuiTableFlags.Resizable, ImGuiTableFlags.ContextMenuInBody, ImGuiTableFlags.RowBg, ImGuiTableFlags.SizingFixedFit, ImGuiTableFlags.NoHostExtendX),
},
BackgroundColor = {
flags = ImGuiTableFlags.RowBg,
row_bg_type = 1,
row_bg_target = 1,
cell_bg_type = 1,
},
TreeView = {
table_flags = bit32.bor(ImGuiTableFlags.BordersV, ImGuiTableFlags.BordersOuterH, ImGuiTableFlags.Resizable, ImGuiTableFlags.RowBg, ImGuiTableFlags.NoBordersInBody),
tree_node_flags_base = bit32.bor(ImGuiTreeNodeFlags.SpanAllColumns, ImGuiTreeNodeFlags.DefaultOpen, ImGuiTreeNodeFlags.DrawLinesFull),
MyTreeNode = nil, -- [Port] local struct, created once on first use
nodes = { -- 1-based; ChildIdx stays 0-based like upstream
{ Name = "Root with Long Name", Type = "Folder", Size = -1, ChildIdx = 1, ChildCount = 3 }, -- 0
{ Name = "Music", Type = "Folder", Size = -1, ChildIdx = 4, ChildCount = 2 }, -- 1
{ Name = "Textures", Type = "Folder", Size = -1, ChildIdx = 6, ChildCount = 3 }, -- 2
{ Name = "desktop.ini", Type = "System file", Size = 1024, ChildIdx = -1, ChildCount = -1 }, -- 3
{ Name = "File1_a.wav", Type = "Audio file", Size = 123000, ChildIdx = -1, ChildCount = -1 }, -- 4
{ Name = "File1_b.wav", Type = "Audio file", Size = 456000, ChildIdx = -1, ChildCount = -1 }, -- 5
{ Name = "Image001.png", Type = "Image file", Size = 203128, ChildIdx = -1, ChildCount = -1 }, -- 6
{ Name = "Copy of Image001.png", Type = "Image file", Size = 203256, ChildIdx = -1, ChildCount = -1 }, -- 7
{ Name = "Copy of Image001 (Final2).png", Type = "Image file", Size = 203512, ChildIdx = -1, ChildCount = -1 }, -- 8
},
},
ItemWidth = {
dummy_f = 0.0,
},
CustomHeaders = {
column_selected = { false, false, false },
},
AngledHeaders = {
table_flags = bit32.bor(ImGuiTableFlags.SizingFixedFit, ImGuiTableFlags.ScrollX, ImGuiTableFlags.ScrollY, ImGuiTableFlags.BordersOuter, ImGuiTableFlags.BordersInnerH, ImGuiTableFlags.Hideable, ImGuiTableFlags.Resizable, ImGuiTableFlags.Reorderable, ImGuiTableFlags.HighlightHoveredColumn),
column_flags = bit32.bor(ImGuiTableColumnFlags.AngledHeader, ImGuiTableColumnFlags.WidthFixed),
bools = {}, -- 1-based, filled with false on first use
frozen_cols = 1,
frozen_rows = 2,
},
ContextMenus = {
flags1 = bit32.bor(ImGuiTableFlags.Resizable, ImGuiTableFlags.Reorderable, ImGuiTableFlags.Hideable, ImGuiTableFlags.Borders, ImGuiTableFlags.ContextMenuInBody),
},
SyncedInstances = {
flags = bit32.bor(ImGuiTableFlags.Resizable, ImGuiTableFlags.Reorderable, ImGuiTableFlags.Hideable, ImGuiTableFlags.Borders, ImGuiTableFlags.SizingFixedFit, ImGuiTableFlags.NoSavedSettings),
},
template_items_names = {
"Banana", "Apple", "Cherry", "Watermelon", "Grapefruit", "Strawberry", "Mango",
"Kiwi", "Orange", "Pineapple", "Blueberry", "Plum", "Coconut", "Pear", "Apricot"
},
Sorting = {
items = ImVector(),
flags = bit32.bor(
ImGuiTableFlags.Resizable, ImGuiTableFlags.Reorderable, ImGuiTableFlags.Hideable, ImGuiTableFlags.Sortable, ImGuiTableFlags.SortMulti,
ImGuiTableFlags.RowBg, ImGuiTableFlags.BordersOuter, ImGuiTableFlags.BordersV, ImGuiTableFlags.NoBordersInBody,
ImGuiTableFlags.ScrollY),
},
Advanced = {
flags = bit32.bor(
ImGuiTableFlags.Resizable, ImGuiTableFlags.Reorderable, ImGuiTableFlags.Hideable,
ImGuiTableFlags.Sortable, ImGuiTableFlags.SortMulti,
ImGuiTableFlags.RowBg, ImGuiTableFlags.Borders, ImGuiTableFlags.NoBordersInBody,
ImGuiTableFlags.ScrollX, ImGuiTableFlags.ScrollY,
ImGuiTableFlags.SizingFixedFit),
columns_base_flags = ImGuiTableColumnFlags.None,
contents_type = 5, -- CT_SelectableSpanRow
freeze_cols = 1,
freeze_rows = 1,
items_count = 0, -- IM_COUNTOF(template_items_names) * 2, set below
outer_size_value = nil, -- ImVec2(0.0f, TEXT_BASE_HEIGHT * 12), set lazily (needs font)
row_min_height = 0.0, -- Auto
inner_width_with_scroll = 0.0, -- Auto-extend
outer_size_enabled = true,
show_headers = true,
show_wrapped_text = false,
items = ImVector(),
selection = ImVector(),
items_need_sort = false,
show_debug_details = false,
},
}
S.DemoWindowTables.Advanced.items_count = #S.DemoWindowTables.template_items_names * 2
function DemoWindowTables()
local _
local s = S.DemoWindowTables
--ImGui.SetNextItemOpen(true, ImGuiCond.Once)
if not ImGui.CollapsingHeader("Tables & Columns") then
return
end
-- Using those as a base value to create width/height that are factor of the size of our font
local TEXT_BASE_WIDTH = ImGui.CalcTextSize("A").x
local TEXT_BASE_HEIGHT = ImGui.GetTextLineHeightWithSpacing()
ImGui.PushID("Tables")
local open_action = -1
if ImGui.Button("Expand all") then
open_action = 1
end
ImGui.SameLine()
if ImGui.Button("Collapse all") then
open_action = 0
end
ImGui.SameLine()
-- Options
_, s.disable_indent = ImGui.Checkbox("Disable tree indentation", s.disable_indent)
ImGui.SameLine()
HelpMarker("Disable the indenting of tree nodes so demo tables can use the full window width.")
ImGui.Separator()
if s.disable_indent then
ImGui.PushStyleVar(16 --[[ImGuiStyleVar.IndentSpacing]], 0.0)
end
-- About Styling of tables
-- Most settings are configured on a per-table basis via the flags passed to BeginTable() and TableSetupColumns APIs.
-- There are however a few settings that a shared and part of the ImGuiStyle structure:
-- style.CellPadding -- Padding within each cell
-- style.Colors[ImGuiCol.TableHeaderBg] -- Table header background
-- style.Colors[ImGuiCol.TableBorderStrong] -- Table outer and header borders
-- style.Colors[ImGuiCol.TableBorderLight] -- Table inner borders
-- style.Colors[ImGuiCol.TableRowBg] -- Table row background when ImGuiTableFlags_RowBg is enabled (even rows)
-- style.Colors[ImGuiCol.TableRowBgAlt] -- Table row background when ImGuiTableFlags_RowBg is enabled (odds rows)
-- Demos
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Basic") then
IMGUI_DEMO_MARKER("Tables/Basic")
-- Here we will showcase three different ways to output a table.
-- They are very simple variations of a same thing!
-- [Method 1] Using TableNextRow() to create a new row, and TableSetColumnIndex() to select the column.
-- In many situations, this is the most flexible and easy to use pattern.
HelpMarker("Using TableNextRow() + calling TableSetColumnIndex() _before_ each cell, in a loop.")
if ImGui.BeginTable("table1", 3) then
for row = 0, 4 - 1 do
ImGui.TableNextRow()
for column = 0, 3 - 1 do
ImGui.TableSetColumnIndex(column)
ImGui.Text("Row %d Column %d", row, column)
end
end
ImGui.EndTable()
end
-- [Method 2] Using TableNextColumn() called multiple times, instead of using a for loop + TableSetColumnIndex().
-- This is generally more convenient when you have code manually submitting the contents of each column.
HelpMarker("Using TableNextRow() + calling TableNextColumn() _before_ each cell, manually.")
if ImGui.BeginTable("table2", 3) then
for row = 0, 4 - 1 do
ImGui.TableNextRow()
ImGui.TableNextColumn()
ImGui.Text("Row %d", row)
ImGui.TableNextColumn()
ImGui.Text("Some contents")
ImGui.TableNextColumn()
ImGui.Text("123.456")
end
ImGui.EndTable()
end
-- [Method 3] We call TableNextColumn() _before_ each cell. We never call TableNextRow(),
-- as TableNextColumn() will automatically wrap around and create new rows as needed.
-- This is generally more convenient when your cells all contains the same type of data.
HelpMarker(
"Only using TableNextColumn(), which tends to be convenient for tables where every cell contains " ..
"the same type of contents.\n This is also more similar to the old NextColumn() function of the " ..
"Columns API, and provided to facilitate the Columns->Tables API transition.")
if ImGui.BeginTable("table3", 3) then
for item = 0, 14 - 1 do
ImGui.TableNextColumn()
ImGui.Text("Item %d", item)
end
ImGui.EndTable()
end
ImGui.TreePop()
end
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Borders, background") then
IMGUI_DEMO_MARKER("Tables/Borders, background")
-- Expose a few Borders related flags interactively
local CT_Text, CT_FillButton = 0, 1 -- enum ContentsType
local s = s.BordersBackground
PushStyleCompact()
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_RowBg", s.flags, 64 --[[ImGuiTableFlags.RowBg]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_Borders", s.flags, 1920 --[[ImGuiTableFlags.Borders]])
ImGui.SameLine(); HelpMarker("ImGuiTableFlags_Borders\n = ImGuiTableFlags_BordersInnerV\n | ImGuiTableFlags_BordersOuterV\n | ImGuiTableFlags_BordersInnerH\n | ImGuiTableFlags_BordersOuterH")
ImGui.Indent()
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_BordersH", s.flags, 384 --[[ImGuiTableFlags.BordersH]])
ImGui.Indent()
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_BordersOuterH", s.flags, 256 --[[ImGuiTableFlags.BordersOuterH]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_BordersInnerH", s.flags, 128 --[[ImGuiTableFlags.BordersInnerH]])
ImGui.Unindent()
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_BordersV", s.flags, 1536 --[[ImGuiTableFlags.BordersV]])
ImGui.Indent()
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_BordersOuterV", s.flags, 1024 --[[ImGuiTableFlags.BordersOuterV]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_BordersInnerV", s.flags, 512 --[[ImGuiTableFlags.BordersInnerV]])
ImGui.Unindent()
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_BordersOuter", s.flags, 1280 --[[ImGuiTableFlags.BordersOuter]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_BordersInner", s.flags, 640 --[[ImGuiTableFlags.BordersInner]])
ImGui.Unindent()
ImGui.AlignTextToFramePadding(); ImGui.Text("Cell contents:")
ImGui.SameLine(); _, s.contents_type = ImGui.RadioButton("Text", s.contents_type, CT_Text)
ImGui.SameLine(); _, s.contents_type = ImGui.RadioButton("FillButton", s.contents_type, CT_FillButton)
_, s.display_headers = ImGui.Checkbox("Display headers", s.display_headers)
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_NoBordersInBody", s.flags, 2048 --[[ImGuiTableFlags.NoBordersInBody]]); ImGui.SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers)")
PopStyleCompact()
if ImGui.BeginTable("table1", 3, s.flags) then
-- Display headers so we can inspect their interaction with borders
-- (Headers are not the main purpose of this section of the demo, so we are not elaborating on them now. See other sections for details)
if s.display_headers then
ImGui.TableSetupColumn("One")
ImGui.TableSetupColumn("Two")
ImGui.TableSetupColumn("Three")
ImGui.TableHeadersRow()
end
for row = 0, 5 - 1 do
ImGui.TableNextRow()
for column = 0, 3 - 1 do
ImGui.TableSetColumnIndex(column)
local buf = string.format("Hello %d,%d", column, row)
if s.contents_type == CT_Text then
ImGui.TextUnformatted(buf)
elseif s.contents_type == CT_FillButton then
ImGui.Button(buf, ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], 0.0))
end
end
end
ImGui.EndTable()
end
ImGui.TreePop()
end
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Resizable, stretch") then
IMGUI_DEMO_MARKER("Tables/Resizable, stretch")
-- By default, if we don't enable ScrollX the sizing policy for each column is "Stretch"
-- All columns maintain a sizing weight, and they will occupy all available width.
local s = s.ResizableStretch
PushStyleCompact()
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_Resizable", s.flags, 1 --[[ImGuiTableFlags.Resizable]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_BordersV", s.flags, 1536 --[[ImGuiTableFlags.BordersV]])
ImGui.SameLine(); HelpMarker(
"Using the _Resizable flag automatically enables the _BordersInnerV flag as well, " ..
"this is why the resize borders are still showing when unchecking this.")
PopStyleCompact()
if ImGui.BeginTable("table1", 3, s.flags) then
for row = 0, 5 - 1 do
ImGui.TableNextRow()
for column = 0, 3 - 1 do
ImGui.TableSetColumnIndex(column)
ImGui.Text("Hello %d,%d", column, row)
end
end
ImGui.EndTable()
end
ImGui.TreePop()
end
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Resizable, fixed") then
IMGUI_DEMO_MARKER("Tables/Resizable, fixed")
-- Here we use ImGuiTableFlags_SizingFixedFit (even though _ScrollX is not set)
-- So columns will adopt the "Fixed" policy and will maintain a fixed width regardless of the whole available width (unless table is small)
-- If there is not enough available width to fit all columns, they will however be resized down.
-- FIXME-TABLE: Providing a stretch-on-init would make sense especially for tables which don't have saved settings
HelpMarker(
"Using _Resizable + _SizingFixedFit flags.\n" ..
"Fixed-width columns generally makes more sense if you want to use horizontal scrolling.\n\n" ..
"Double-click a column border to auto-fit the column to its contents.")
PushStyleCompact()
local s = s.ResizableFixed
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_NoHostExtendX", s.flags, 65536 --[[ImGuiTableFlags.NoHostExtendX]])
PopStyleCompact()
if ImGui.BeginTable("table1", 3, s.flags) then
for row = 0, 5 - 1 do
ImGui.TableNextRow()
for column = 0, 3 - 1 do
ImGui.TableSetColumnIndex(column)
ImGui.Text("Hello %d,%d", column, row)
end
end
ImGui.EndTable()
end
ImGui.TreePop()
end
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Resizable, mixed") then
IMGUI_DEMO_MARKER("Tables/Resizable, mixed")
HelpMarker(
"Using TableSetupColumn() to alter resizing policy on a per-column basis.\n\n" ..
"When combining Fixed and Stretch columns, generally you only want one, maybe two trailing columns to use _WidthStretch.")
local s = s.ResizableMixed
if ImGui.BeginTable("table1", 3, s.flags) then
ImGui.TableSetupColumn("AAA", 16 --[[ImGuiTableColumnFlags.WidthFixed]])
ImGui.TableSetupColumn("BBB", 16 --[[ImGuiTableColumnFlags.WidthFixed]])
ImGui.TableSetupColumn("CCC", 8 --[[ImGuiTableColumnFlags.WidthStretch]])
ImGui.TableHeadersRow()
for row = 0, 5 - 1 do
ImGui.TableNextRow()
for column = 0, 3 - 1 do
ImGui.TableSetColumnIndex(column)
ImGui.Text("%s %d,%d", (column == 2) and "Stretch" or "Fixed", column, row)
end
end
ImGui.EndTable()
end
if ImGui.BeginTable("table2", 6, s.flags) then
ImGui.TableSetupColumn("AAA", 16 --[[ImGuiTableColumnFlags.WidthFixed]])
ImGui.TableSetupColumn("BBB", 16 --[[ImGuiTableColumnFlags.WidthFixed]])
ImGui.TableSetupColumn("CCC", bit32.bor(16 --[[ImGuiTableColumnFlags.WidthFixed]], 2 --[[ImGuiTableColumnFlags.DefaultHide]]))
ImGui.TableSetupColumn("DDD", 8 --[[ImGuiTableColumnFlags.WidthStretch]])
ImGui.TableSetupColumn("EEE", 8 --[[ImGuiTableColumnFlags.WidthStretch]])
ImGui.TableSetupColumn("FFF", bit32.bor(8 --[[ImGuiTableColumnFlags.WidthStretch]], 2 --[[ImGuiTableColumnFlags.DefaultHide]]))
ImGui.TableHeadersRow()
for row = 0, 5 - 1 do
ImGui.TableNextRow()
for column = 0, 6 - 1 do
ImGui.TableSetColumnIndex(column)
ImGui.Text("%s %d,%d", (column >= 3) and "Stretch" or "Fixed", column, row)
end
end
ImGui.EndTable()
end
ImGui.TreePop()
end
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Reorderable, hideable, with headers") then
IMGUI_DEMO_MARKER("Tables/Reorderable, hideable, with headers")
HelpMarker(
"Click and drag column headers to reorder columns.\n\n" ..
"Right-click on a header to open a context menu.")
local s = s.Reorderable
PushStyleCompact()
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_Resizable", s.flags, 1 --[[ImGuiTableFlags.Resizable]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_Reorderable", s.flags, 2 --[[ImGuiTableFlags.Reorderable]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_Hideable", s.flags, 4 --[[ImGuiTableFlags.Hideable]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_NoBordersInBody", s.flags, 2048 --[[ImGuiTableFlags.NoBordersInBody]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", s.flags, 4096 --[[ImGuiTableFlags.NoBordersInBodyUntilResize]]); ImGui.SameLine(); HelpMarker("Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)")
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", s.flags, 268435456 --[[ImGuiTableFlags.HighlightHoveredColumn]])
PopStyleCompact()
if ImGui.BeginTable("table1", 3, s.flags) then
-- Submit columns name with TableSetupColumn() and call TableHeadersRow() to create a row with a header in each column.
-- (Later we will show how TableSetupColumn() has other uses, optional flags, sizing weight etc.)
ImGui.TableSetupColumn("One")
ImGui.TableSetupColumn("Two")
ImGui.TableSetupColumn("Three")
ImGui.TableHeadersRow()
for row = 0, 6 - 1 do
ImGui.TableNextRow()
for column = 0, 3 - 1 do
ImGui.TableSetColumnIndex(column)
ImGui.Text("Hello %d,%d", column, row)
end
end
ImGui.EndTable()
end
-- Use outer_size.x == 0.0f instead of default to make the table as tight as possible
-- (only valid when no scrolling and no stretch column)
if ImGui.BeginTable("table2", 3, bit32.bor(s.flags, 8192 --[[ImGuiTableFlags.SizingFixedFit]]), ImVec2(0.0, 0.0)) then
ImGui.TableSetupColumn("One")
ImGui.TableSetupColumn("Two")
ImGui.TableSetupColumn("Three")
ImGui.TableHeadersRow()
for row = 0, 6 - 1 do
ImGui.TableNextRow()
for column = 0, 3 - 1 do
ImGui.TableSetColumnIndex(column)
ImGui.Text("Fixed %d,%d", column, row)
end
end
ImGui.EndTable()
end
ImGui.TreePop()
end
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Padding") then
IMGUI_DEMO_MARKER("Tables/Padding")
-- First example: showcase use of padding flags and effect of BorderOuterV/BorderInnerV on X padding.
-- We don't expose BorderOuterH/BorderInnerH here because they have no effect on X padding.
HelpMarker(
"We often want outer padding activated when any using features which makes the edges of a column visible:\n" ..
"e.g.:\n" ..
"- BorderOuterV\n" ..
"- any form of row selection\n" ..
"Because of this, activating BorderOuterV sets the default to PadOuterX. " ..
"Using PadOuterX or NoPadOuterX you can override the default.\n\n" ..
"Actual padding values are using style.CellPadding.\n\n" ..
"In this demo we don't show horizontal borders to emphasize how they don't affect default horizontal padding.")
local s = s.Padding
PushStyleCompact()
_, s.flags1 = ImGui.CheckboxFlags("ImGuiTableFlags_PadOuterX", s.flags1, 2097152 --[[ImGuiTableFlags.PadOuterX]])
ImGui.SameLine(); HelpMarker("Enable outer-most padding (default if ImGuiTableFlags_BordersOuterV is set)")
_, s.flags1 = ImGui.CheckboxFlags("ImGuiTableFlags_NoPadOuterX", s.flags1, 4194304 --[[ImGuiTableFlags.NoPadOuterX]])
ImGui.SameLine(); HelpMarker("Disable outer-most padding (default if ImGuiTableFlags_BordersOuterV is not set)")
_, s.flags1 = ImGui.CheckboxFlags("ImGuiTableFlags_NoPadInnerX", s.flags1, 8388608 --[[ImGuiTableFlags.NoPadInnerX]])
ImGui.SameLine(); HelpMarker("Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off)")
_, s.flags1 = ImGui.CheckboxFlags("ImGuiTableFlags_BordersOuterV", s.flags1, 1024 --[[ImGuiTableFlags.BordersOuterV]])
_, s.flags1 = ImGui.CheckboxFlags("ImGuiTableFlags_BordersInnerV", s.flags1, 512 --[[ImGuiTableFlags.BordersInnerV]])
_, s.show_headers = ImGui.Checkbox("show_headers", s.show_headers)
PopStyleCompact()
if ImGui.BeginTable("table_padding", 3, s.flags1) then
if s.show_headers then
ImGui.TableSetupColumn("One")
ImGui.TableSetupColumn("Two")
ImGui.TableSetupColumn("Three")
ImGui.TableHeadersRow()
end
for row = 0, 5 - 1 do
ImGui.TableNextRow()
for column = 0, 3 - 1 do
ImGui.TableSetColumnIndex(column)
if row == 0 then
ImGui.Text("Avail %.2f", ImGui.GetContentRegionAvail().x)
else
local buf = string.format("Hello %d,%d", column, row)
ImGui.Button(buf, ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], 0.0))
end
--if bit32.band(ImGui.TableGetColumnFlags(), ImGuiTableColumnFlags.IsHovered) ~= 0 then
-- ImGui.TableSetBgColor(ImGuiTableBgTarget.CellBg, IM_COL32(0, 100, 0, 255)) end
end
end
ImGui.EndTable()
end
-- Second example: set style.CellPadding to (0.0) or a custom value.
-- FIXME-TABLE: Vertical border effectively not displayed the same way as horizontal one...
HelpMarker("Setting style.CellPadding to (0,0) or a custom value.")
PushStyleCompact()
_, s.flags2 = ImGui.CheckboxFlags("ImGuiTableFlags_Borders", s.flags2, 1920 --[[ImGuiTableFlags.Borders]])
_, s.flags2 = ImGui.CheckboxFlags("ImGuiTableFlags_BordersH", s.flags2, 384 --[[ImGuiTableFlags.BordersH]])
_, s.flags2 = ImGui.CheckboxFlags("ImGuiTableFlags_BordersV", s.flags2, 1536 --[[ImGuiTableFlags.BordersV]])
_, s.flags2 = ImGui.CheckboxFlags("ImGuiTableFlags_BordersInner", s.flags2, 640 --[[ImGuiTableFlags.BordersInner]])
_, s.flags2 = ImGui.CheckboxFlags("ImGuiTableFlags_BordersOuter", s.flags2, 1280 --[[ImGuiTableFlags.BordersOuter]])
_, s.flags2 = ImGui.CheckboxFlags("ImGuiTableFlags_RowBg", s.flags2, 64 --[[ImGuiTableFlags.RowBg]])
_, s.flags2 = ImGui.CheckboxFlags("ImGuiTableFlags_Resizable", s.flags2, 1 --[[ImGuiTableFlags.Resizable]])
_, s.show_widget_frame_bg = ImGui.Checkbox("show_widget_frame_bg", s.show_widget_frame_bg)
ImGui.SliderFloat2("CellPadding", s.cell_padding, 0.0, 10.0, "%.0f")
PopStyleCompact()
ImGui.PushStyleVar(17 --[[ImGuiStyleVar.CellPadding]], s.cell_padding)
if ImGui.BeginTable("table_padding_2", 3, s.flags2) then
-- static char text_bufs[3 * 5][16] -- Mini text storage for 3x5 cells
if not s.show_widget_frame_bg then
ImGui.PushStyleColor(7 --[[ImGuiCol.FrameBg]], 0)
end
for cell = 0, 3 * 5 - 1 do
ImGui.TableNextColumn()
if s.init then
s.text_bufs[cell + 1] = ImGui.NewTextBuffer("edit me", 16) -- [Port] strcpy() -> new buffer
end
ImGui.SetNextItemWidth(-1.1754943508223e-38 --[[FLT_MIN]])
ImGui.PushID(cell)
ImGui.InputText("##cell", s.text_bufs[cell + 1], 16)
ImGui.PopID()
end
if not s.show_widget_frame_bg then
ImGui.PopStyleColor()
end
s.init = false
ImGui.EndTable()
end
ImGui.PopStyleVar()
ImGui.TreePop()
end
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Sizing policies") then
IMGUI_DEMO_MARKER("Tables/Explicit widths")
local s = s.SizingPolicies
PushStyleCompact()
_, s.flags1 = ImGui.CheckboxFlags("ImGuiTableFlags_Resizable", s.flags1, 1 --[[ImGuiTableFlags.Resizable]])
_, s.flags1 = ImGui.CheckboxFlags("ImGuiTableFlags_NoHostExtendX", s.flags1, 65536 --[[ImGuiTableFlags.NoHostExtendX]])
PopStyleCompact()
local sizing_policy_flags = s.sizing_policy_flags -- 1-based
for table_n = 0, 4 - 1 do
ImGui.PushID(table_n)
ImGui.SetNextItemWidth(TEXT_BASE_WIDTH * 30)
sizing_policy_flags[table_n + 1] = EditTableSizingFlags(sizing_policy_flags[table_n + 1])
-- To make it easier to understand the different sizing policy,
-- For each policy: we display one table where the columns have equal contents width,
-- and one where the columns have different contents width.
if ImGui.BeginTable("table1", 3, bit32.bor(sizing_policy_flags[table_n + 1], s.flags1)) then
for row = 0, 3 - 1 do
ImGui.TableNextRow()
ImGui.TableNextColumn(); ImGui.Text("Oh dear")
ImGui.TableNextColumn(); ImGui.Text("Oh dear")
ImGui.TableNextColumn(); ImGui.Text("Oh dear")
end
ImGui.EndTable()
end
if ImGui.BeginTable("table2", 3, bit32.bor(sizing_policy_flags[table_n + 1], s.flags1)) then
for row = 0, 3 - 1 do
ImGui.TableNextRow()
ImGui.TableNextColumn(); ImGui.Text("AAAA")
ImGui.TableNextColumn(); ImGui.Text("BBBBBBBB")
ImGui.TableNextColumn(); ImGui.Text("CCCCCCCCCCCC")
end
ImGui.EndTable()
end
ImGui.PopID()
end
ImGui.Spacing()
ImGui.TextUnformatted("Advanced")
ImGui.SameLine()
HelpMarker(
"This section allows you to interact and see the effect of various sizing policies " ..
"depending on whether Scroll is enabled and the contents of your columns.")
local CT_ShowWidth, CT_ShortText, CT_LongText, CT_Button, CT_FillButton, CT_InputText = 0, 1, 2, 3, 4, 5 -- enum ContentsType
PushStyleCompact()
ImGui.PushID("Advanced")
ImGui.PushItemWidth(TEXT_BASE_WIDTH * 30)
s.flags = EditTableSizingFlags(s.flags)
s.contents_type = ImGui.Combo("Contents", s.contents_type, "Show width\0Short Text\0Long Text\0Button\0Fill Button\0InputText\0")
if s.contents_type == CT_FillButton then
ImGui.SameLine()
HelpMarker(
"Be mindful that using right-alignment (e.g. size.x = -FLT_MIN) creates a feedback loop " ..
"where contents width can feed into auto-column width can feed into contents width.")
end
s.column_count = ImGui.DragInt("Columns", s.column_count, 0.1, 1, 64, "%d", 1536 --[[ImGuiSliderFlags.AlwaysClamp]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_Resizable", s.flags, 1 --[[ImGuiTableFlags.Resizable]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_PreciseWidths", s.flags, 524288 --[[ImGuiTableFlags.PreciseWidths]])
ImGui.SameLine(); HelpMarker("Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth.")
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_ScrollX", s.flags, 16777216 --[[ImGuiTableFlags.ScrollX]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_ScrollY", s.flags, 33554432 --[[ImGuiTableFlags.ScrollY]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_NoClip", s.flags, 1048576 --[[ImGuiTableFlags.NoClip]])
ImGui.PopItemWidth()
ImGui.PopID()
PopStyleCompact()
if ImGui.BeginTable("table2", s.column_count, s.flags, ImVec2(0.0, TEXT_BASE_HEIGHT * 7)) then
for cell = 0, 10 * s.column_count - 1 do
ImGui.TableNextColumn()
local column = ImGui.TableGetColumnIndex()
local row = ImGui.TableGetRowIndex()
ImGui.PushID(cell)
local label = string.format("Hello %d,%d", column, row)
local contents_type = s.contents_type
if contents_type == CT_ShortText then ImGui.TextUnformatted(label)
elseif contents_type == CT_LongText then ImGui.Text("Some %s text %d,%d\nOver two lines..", (column == 0) and "long" or "longeeer", column, row)
elseif contents_type == CT_ShowWidth then ImGui.Text("W: %.1f", ImGui.GetContentRegionAvail().x)
elseif contents_type == CT_Button then ImGui.Button(label)
elseif contents_type == CT_FillButton then ImGui.Button(label, ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], 0.0))
elseif contents_type == CT_InputText then ImGui.SetNextItemWidth(-1.1754943508223e-38 --[[FLT_MIN]]); ImGui.InputText("##", s.text_buf, 32)
end
ImGui.PopID()
end
ImGui.EndTable()
end
ImGui.TreePop()
end
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Vertical scrolling, with clipping") then
IMGUI_DEMO_MARKER("Tables/Vertical scrolling, with clipping")
HelpMarker(
"Here we activate ScrollY, which will create a child window container to allow hosting scrollable contents.\n\n" ..
"We also demonstrate using ImGuiListClipper to virtualize the submission of many items.")
local s = s.VerticalScrolling
PushStyleCompact()
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_ScrollY", s.flags, 33554432 --[[ImGuiTableFlags.ScrollY]])
PopStyleCompact()
-- When using ScrollX or ScrollY we need to specify a size for our table container!
-- Otherwise by default the table will fit all available space, like a BeginChild() call.
local outer_size = ImVec2(0.0, TEXT_BASE_HEIGHT * 8)
if ImGui.BeginTable("table_scrolly", 3, s.flags, outer_size) then
ImGui.TableSetupScrollFreeze(0, 1) -- Make top row always visible
ImGui.TableSetupColumn("One", 0 --[[ImGuiTableColumnFlags.None]])
ImGui.TableSetupColumn("Two", 0 --[[ImGuiTableColumnFlags.None]])
ImGui.TableSetupColumn("Three", 0 --[[ImGuiTableColumnFlags.None]])
ImGui.TableHeadersRow()
-- Demonstrate using clipper for large vertical lists
local clipper = ImGuiListClipper()
clipper:Begin(1000)
while clipper:Step() do
for row = clipper.DisplayStart, clipper.DisplayEnd - 1 do
ImGui.TableNextRow()
for column = 0, 3 - 1 do
ImGui.TableSetColumnIndex(column)
ImGui.Text("Hello %d,%d", column, row)
end
end
end
ImGui.EndTable()
end
ImGui.TreePop()
end
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Horizontal scrolling") then
IMGUI_DEMO_MARKER("Tables/Horizontal scrolling")
HelpMarker(
"When ScrollX is enabled, the default sizing policy becomes ImGuiTableFlags_SizingFixedFit, " ..
"as automatically stretching columns doesn't make much sense with horizontal scrolling.\n\n" ..
"Also note that as of the current version, you will almost always want to enable ScrollY along with ScrollX, " ..
"because the container window won't automatically extend vertically to fix contents " ..
"(this may be improved in future versions).")
local s = s.HorizontalScrolling
PushStyleCompact()
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_Resizable", s.flags, 1 --[[ImGuiTableFlags.Resizable]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_ScrollX", s.flags, 16777216 --[[ImGuiTableFlags.ScrollX]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_ScrollY", s.flags, 33554432 --[[ImGuiTableFlags.ScrollY]])
ImGui.SetNextItemWidth(ImGui.GetFrameHeight())
s.freeze_cols = ImGui.DragInt("freeze_cols", s.freeze_cols, 0.2, 0, 9, nil, 128 --[[ImGuiSliderFlags.NoInput]])
ImGui.SetNextItemWidth(ImGui.GetFrameHeight())
s.freeze_rows = ImGui.DragInt("freeze_rows", s.freeze_rows, 0.2, 0, 9, nil, 128 --[[ImGuiSliderFlags.NoInput]])
PopStyleCompact()
-- When using ScrollX or ScrollY we need to specify a size for our table container!
-- Otherwise by default the table will fit all available space, like a BeginChild() call.
local outer_size = ImVec2(0.0, TEXT_BASE_HEIGHT * 8)
if ImGui.BeginTable("table_scrollx", 7, s.flags, outer_size) then
ImGui.TableSetupScrollFreeze(s.freeze_cols, s.freeze_rows)
ImGui.TableSetupColumn("Line #", 128 --[[ImGuiTableColumnFlags.NoHide]]) -- Make the first column not hideable to match our use of TableSetupScrollFreeze()
ImGui.TableSetupColumn("One")
ImGui.TableSetupColumn("Two")
ImGui.TableSetupColumn("Three")
ImGui.TableSetupColumn("Four")
ImGui.TableSetupColumn("Five")
ImGui.TableSetupColumn("Six")
ImGui.TableHeadersRow()
for row = 0, 20 - 1 do
ImGui.TableNextRow()
for column = 0, 7 - 1 do repeat
-- Both TableNextColumn() and TableSetColumnIndex() return true when a column is visible or performing width measurement.
-- Because here we know that:
-- - A) all our columns are contributing the same to row height
-- - B) column 0 is always visible,
-- We only always submit this one column and can skip others.
-- More advanced per-column clipping behaviors may benefit from polling the status flags via TableGetColumnFlags().
if not ImGui.TableSetColumnIndex(column) and column > 0 then
break -- continue
end
if column == 0 then
ImGui.Text("Line %d", row)
else
ImGui.Text("Hello world %d,%d", column, row)
end
until true end
end
ImGui.EndTable()
end
ImGui.Spacing()
ImGui.TextUnformatted("Stretch + ScrollX")
ImGui.SameLine()
HelpMarker(
"Showcase using Stretch columns + ScrollX together: " ..
"this is rather unusual and only makes sense when specifying an 'inner_width' for the table!\n" ..
"Without an explicit value, inner_width is == outer_size.x and therefore using Stretch columns " ..
"along with ScrollX doesn't make sense.")
PushStyleCompact()
ImGui.PushID("flags3")
ImGui.PushItemWidth(TEXT_BASE_WIDTH * 30)
_, s.flags2 = ImGui.CheckboxFlags("ImGuiTableFlags_ScrollX", s.flags2, 16777216 --[[ImGuiTableFlags.ScrollX]])
s.inner_width = ImGui.DragFloat("inner_width", s.inner_width, 1.0, 0.0, 3.4028234663853e+38 --[[FLT_MAX]], "%.1f")
ImGui.PopItemWidth()
ImGui.PopID()
PopStyleCompact()
if ImGui.BeginTable("table2", 7, s.flags2, outer_size, s.inner_width) then
for cell = 0, 20 * 7 - 1 do
ImGui.TableNextColumn()
ImGui.Text("Hello world %d,%d", ImGui.TableGetColumnIndex(), ImGui.TableGetRowIndex())
end
ImGui.EndTable()
end
ImGui.TreePop()
end
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Columns flags") then
IMGUI_DEMO_MARKER("Tables/Columns flags")
-- Create a first table just to show all the options/flags we want to make visible in our example!
local column_count = 3
local column_names = { "One", "Two", "Three" } -- 1-based
local column_flags = s.ColumnsFlags.column_flags -- 1-based
local column_flags_out = s.ColumnsFlags.column_flags_out -- Output from TableGetColumnFlags()
if ImGui.BeginTable("table_columns_flags_checkboxes", column_count, 0 --[[ImGuiTableFlags.None]]) then
PushStyleCompact()
for column = 0, column_count - 1 do
ImGui.TableNextColumn()
ImGui.PushID(column)
ImGui.AlignTextToFramePadding() -- FIXME-TABLE: Workaround for wrong text baseline propagation across columns
ImGui.Text("'%s'", column_names[column + 1])
ImGui.Spacing()
ImGui.Text("Input flags:")
column_flags[column + 1] = EditTableColumnsFlags(column_flags[column + 1])
ImGui.Spacing()
ImGui.Text("Output flags:")
ImGui.BeginDisabled()
ShowTableColumnsStatusFlags(column_flags_out[column + 1])
ImGui.EndDisabled()
ImGui.PopID()
end
PopStyleCompact()
ImGui.EndTable()
end
-- Create the real table we care about for the example!
-- We use a scrolling table to be able to showcase the difference between the _IsEnabled and _IsVisible flags above,
-- otherwise in a non-scrolling table columns are always visible (unless using ImGuiTableFlags_NoKeepColumnsVisible
-- + resizing the parent window down).
local flags
= bit32.bor(8192 --[[ImGuiTableFlags.SizingFixedFit]], 16777216 --[[ImGuiTableFlags.ScrollX]], 33554432 --[[ImGuiTableFlags.ScrollY]]
, 64 --[[ImGuiTableFlags.RowBg]], 1280 --[[ImGuiTableFlags.BordersOuter]], 1536 --[[ImGuiTableFlags.BordersV]]
, 1 --[[ImGuiTableFlags.Resizable]], 2 --[[ImGuiTableFlags.Reorderable]], 4 --[[ImGuiTableFlags.Hideable]], 8 --[[ImGuiTableFlags.Sortable]])
local outer_size = ImVec2(0.0, TEXT_BASE_HEIGHT * 9)
if ImGui.BeginTable("table_columns_flags", column_count, flags, outer_size) then
local has_angled_header = false
for column = 0, column_count - 1 do
has_angled_header = has_angled_header or (bit32.band(column_flags[column + 1], 262144 --[[ImGuiTableColumnFlags.AngledHeader]]) ~= 0)
ImGui.TableSetupColumn(column_names[column + 1], column_flags[column + 1])
end
if has_angled_header then
ImGui.TableAngledHeadersRow()
end
ImGui.TableHeadersRow()
for column = 0, column_count - 1 do
column_flags_out[column + 1] = ImGui.TableGetColumnFlags(column)
end
local indent_step = math.floor(math.floor(TEXT_BASE_WIDTH) / 2)
for row = 0, 8 - 1 do
-- Add some indentation to demonstrate usage of per-column IndentEnable/IndentDisable flags.
ImGui.Indent(indent_step)
ImGui.TableNextRow()
for column = 0, column_count - 1 do
ImGui.TableSetColumnIndex(column)
ImGui.Text("%s %s", (column == 0) and "Indented" or "Hello", ImGui.TableGetColumnName(column))
end
end
ImGui.Unindent(indent_step * 8.0)
ImGui.EndTable()
end
ImGui.TreePop()
end
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Columns widths") then
IMGUI_DEMO_MARKER("Tables/Columns widths")
HelpMarker("Using TableSetupColumn() to setup default width.")
local s = s.ColumnsWidths
PushStyleCompact()
_, s.flags1 = ImGui.CheckboxFlags("ImGuiTableFlags_Resizable", s.flags1, 1 --[[ImGuiTableFlags.Resizable]])
_, s.flags1 = ImGui.CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", s.flags1, 4096 --[[ImGuiTableFlags.NoBordersInBodyUntilResize]])
PopStyleCompact()
if ImGui.BeginTable("table1", 3, s.flags1) then
-- We could also set ImGuiTableFlags_SizingFixedFit on the table and all columns will default to ImGuiTableColumnFlags_WidthFixed.
ImGui.TableSetupColumn("one", 16 --[[ImGuiTableColumnFlags.WidthFixed]], 100.0) -- Default to 100.0f
ImGui.TableSetupColumn("two", 16 --[[ImGuiTableColumnFlags.WidthFixed]], 200.0) -- Default to 200.0f
ImGui.TableSetupColumn("three", 16 --[[ImGuiTableColumnFlags.WidthFixed]]) -- Default to auto
ImGui.TableHeadersRow()
for row = 0, 4 - 1 do
ImGui.TableNextRow()
for column = 0, 3 - 1 do
ImGui.TableSetColumnIndex(column)
if row == 0 then
ImGui.Text("(w: %5.1f)", ImGui.GetContentRegionAvail().x)
else
ImGui.Text("Hello %d,%d", column, row)
end
end
end
ImGui.EndTable()
end
HelpMarker(
"Using TableSetupColumn() to setup explicit width.\n\nUnless _NoKeepColumnsVisible is set, " ..
"fixed columns with set width may still be shrunk down if there's not enough space in the host.")
PushStyleCompact()
_, s.flags2 = ImGui.CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", s.flags2, 262144 --[[ImGuiTableFlags.NoKeepColumnsVisible]])
_, s.flags2 = ImGui.CheckboxFlags("ImGuiTableFlags_BordersInnerV", s.flags2, 512 --[[ImGuiTableFlags.BordersInnerV]])
_, s.flags2 = ImGui.CheckboxFlags("ImGuiTableFlags_BordersOuterV", s.flags2, 1024 --[[ImGuiTableFlags.BordersOuterV]])
PopStyleCompact()
if ImGui.BeginTable("table2", 4, s.flags2) then
-- We could also set ImGuiTableFlags_SizingFixedFit on the table and then all columns
-- will default to ImGuiTableColumnFlags_WidthFixed.
ImGui.TableSetupColumn("", 16 --[[ImGuiTableColumnFlags.WidthFixed]], 100.0)
ImGui.TableSetupColumn("", 16 --[[ImGuiTableColumnFlags.WidthFixed]], TEXT_BASE_WIDTH * 15.0)
ImGui.TableSetupColumn("", 16 --[[ImGuiTableColumnFlags.WidthFixed]], TEXT_BASE_WIDTH * 30.0)
ImGui.TableSetupColumn("", 16 --[[ImGuiTableColumnFlags.WidthFixed]], TEXT_BASE_WIDTH * 15.0)
for row = 0, 5 - 1 do
ImGui.TableNextRow()
for column = 0, 4 - 1 do
ImGui.TableSetColumnIndex(column)
if row == 0 then
ImGui.Text("(w: %5.1f)", ImGui.GetContentRegionAvail().x)
else
ImGui.Text("Hello %d,%d", column, row)
end
end
end
ImGui.EndTable()
end
ImGui.TreePop()
end
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Nested tables") then
IMGUI_DEMO_MARKER("Tables/Nested tables")
HelpMarker("This demonstrates embedding a table into another table cell.")
if ImGui.BeginTable("table_nested1", 2, bit32.bor(1920 --[[ImGuiTableFlags.Borders]], 1 --[[ImGuiTableFlags.Resizable]], 2 --[[ImGuiTableFlags.Reorderable]], 4 --[[ImGuiTableFlags.Hideable]])) then
ImGui.TableSetupColumn("A0")
ImGui.TableSetupColumn("A1")
ImGui.TableHeadersRow()
ImGui.TableNextColumn()
ImGui.Text("A0 Row 0")
do
local rows_height = (TEXT_BASE_HEIGHT * 2.0) + (ImGui.GetStyle().CellPadding.y * 2.0)
if ImGui.BeginTable("table_nested2", 2, bit32.bor(1920 --[[ImGuiTableFlags.Borders]], 1 --[[ImGuiTableFlags.Resizable]], 2 --[[ImGuiTableFlags.Reorderable]], 4 --[[ImGuiTableFlags.Hideable]])) then
ImGui.TableSetupColumn("B0")
ImGui.TableSetupColumn("B1")
ImGui.TableHeadersRow()
ImGui.TableNextRow(0 --[[ImGuiTableRowFlags.None]], rows_height)
ImGui.TableNextColumn()
ImGui.Text("B0 Row 0")
ImGui.TableNextColumn()
ImGui.Text("B1 Row 0")
ImGui.TableNextRow(0 --[[ImGuiTableRowFlags.None]], rows_height)
ImGui.TableNextColumn()
ImGui.Text("B0 Row 1")
ImGui.TableNextColumn()
ImGui.Text("B1 Row 1")
ImGui.EndTable()
end
end
ImGui.TableNextColumn(); ImGui.Text("A1 Row 0")
ImGui.TableNextColumn(); ImGui.Text("A0 Row 1")
ImGui.TableNextColumn(); ImGui.Text("A1 Row 1")
ImGui.EndTable()
end
ImGui.TreePop()
end
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Row height") then
IMGUI_DEMO_MARKER("Tables/Row height")
HelpMarker(
"You can pass a 'min_row_height' to TableNextRow().\n\nRows are padded with 'style.CellPadding.y' on top and bottom, " ..
"so effectively the minimum row height will always be >= 'style.CellPadding.y * 2.0f'.\n\n" ..
"We cannot honor a _maximum_ row height as that would require a unique clipping rectangle per row.")
if ImGui.BeginTable("table_row_height", 1, 1920 --[[ImGuiTableFlags.Borders]]) then
for row = 0, 8 - 1 do
local min_row_height = math.floor(TEXT_BASE_HEIGHT * 0.30 * row + ImGui.GetStyle().CellPadding.y * 2.0)
ImGui.TableNextRow(0 --[[ImGuiTableRowFlags.None]], min_row_height)
ImGui.TableNextColumn()
ImGui.Text("min_row_height = %.2f", min_row_height)
end
ImGui.EndTable()
end
HelpMarker(
"Showcase using SameLine(0,0) to share Current Line Height between cells.\n\n" ..
"Please note that Tables Row Height is not the same thing as Current Line Height, " ..
"as a table cell may contains multiple lines.")
if ImGui.BeginTable("table_share_lineheight", 2, 1920 --[[ImGuiTableFlags.Borders]]) then
ImGui.TableNextRow()
ImGui.TableNextColumn()
ImGui.ColorButton("##1", ImVec4(0.13, 0.26, 0.40, 1.0), 0 --[[ImGuiColorEditFlags.None]], ImVec2(40, 40))
ImGui.TableNextColumn()
ImGui.Text("Line 1")
ImGui.Text("Line 2")
ImGui.TableNextRow()
ImGui.TableNextColumn()
ImGui.ColorButton("##2", ImVec4(0.13, 0.26, 0.40, 1.0), 0 --[[ImGuiColorEditFlags.None]], ImVec2(40, 40))
ImGui.TableNextColumn()
ImGui.SameLine(0.0, 0.0) -- Reuse line height from previous column
ImGui.Text("Line 1, with SameLine(0,0)")
ImGui.Text("Line 2")
ImGui.EndTable()
end
HelpMarker("Showcase altering CellPadding.y between rows. Note that CellPadding.x is locked for the entire table.")
if ImGui.BeginTable("table_changing_cellpadding_y", 1, 1920 --[[ImGuiTableFlags.Borders]]) then
local style = ImGui.GetStyle()
for row = 0, 8 - 1 do
if (row % 3) == 2 then
ImGui.PushStyleVarY(17 --[[ImGuiStyleVar.CellPadding]], 20.0)
end
ImGui.TableNextRow(0 --[[ImGuiTableRowFlags.None]])
ImGui.TableNextColumn()
ImGui.Text("CellPadding.y = %.2f", style.CellPadding.y)
if (row % 3) == 2 then
ImGui.PopStyleVar()
end
end
ImGui.EndTable()
end
ImGui.TreePop()
end
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Outer size") then
IMGUI_DEMO_MARKER("Tables/Outer size")
-- Showcasing use of ImGuiTableFlags_NoHostExtendX and ImGuiTableFlags_NoHostExtendY
-- Important to that note how the two flags have slightly different behaviors!
ImGui.Text("Using NoHostExtendX and NoHostExtendY:")
PushStyleCompact()
local s = s.OuterSize
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_NoHostExtendX", s.flags, 65536 --[[ImGuiTableFlags.NoHostExtendX]])
ImGui.SameLine(); HelpMarker("Make outer width auto-fit to columns, overriding outer_size.x value.\n\nOnly available when ScrollX/ScrollY are disabled and Stretch columns are not used.")
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_NoHostExtendY", s.flags, 131072 --[[ImGuiTableFlags.NoHostExtendY]])
ImGui.SameLine(); HelpMarker("Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit).\n\nOnly available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible.")
PopStyleCompact()
local outer_size = ImVec2(0.0, TEXT_BASE_HEIGHT * 5.5)
if ImGui.BeginTable("table1", 3, s.flags, outer_size) then
for row = 0, 10 - 1 do
ImGui.TableNextRow()
for column = 0, 3 - 1 do
ImGui.TableNextColumn()
ImGui.Text("Cell %d,%d", column, row)
end
end
ImGui.EndTable()
end
ImGui.SameLine()
ImGui.Text("Hello!")
ImGui.Spacing()
ImGui.Text("Using explicit size:")
if ImGui.BeginTable("table2", 3, bit32.bor(1920 --[[ImGuiTableFlags.Borders]], 64 --[[ImGuiTableFlags.RowBg]]), ImVec2(TEXT_BASE_WIDTH * 30, 0.0)) then
for row = 0, 5 - 1 do
ImGui.TableNextRow()
for column = 0, 3 - 1 do
ImGui.TableNextColumn()
ImGui.Text("Cell %d,%d", column, row)
end
end
ImGui.EndTable()
end
ImGui.SameLine()
if ImGui.BeginTable("table3", 3, bit32.bor(1920 --[[ImGuiTableFlags.Borders]], 64 --[[ImGuiTableFlags.RowBg]]), ImVec2(TEXT_BASE_WIDTH * 30, 0.0)) then
local rows_height = TEXT_BASE_HEIGHT * 1.5 + ImGui.GetStyle().CellPadding.y * 2.0
for row = 0, 3 - 1 do
ImGui.TableNextRow(0, rows_height)
for column = 0, 3 - 1 do
ImGui.TableNextColumn()
ImGui.Text("Cell %d,%d", column, row)
end
end
ImGui.EndTable()
end
ImGui.TreePop()
end
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Background color") then
IMGUI_DEMO_MARKER("Tables/Background color")
local s = s.BackgroundColor
PushStyleCompact()
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_Borders", s.flags, 1920 --[[ImGuiTableFlags.Borders]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_RowBg", s.flags, 64 --[[ImGuiTableFlags.RowBg]])
ImGui.SameLine(); HelpMarker("ImGuiTableFlags_RowBg automatically sets RowBg0 to alternative colors pulled from the Style.")
s.row_bg_type = ImGui.Combo("row bg type", s.row_bg_type, "None\0Red\0Gradient\0")
s.row_bg_target = ImGui.Combo("row bg target", s.row_bg_target, "RowBg0\0RowBg1\0"); ImGui.SameLine(); HelpMarker("Target RowBg0 to override the alternating odd/even colors,\nTarget RowBg1 to blend with them.")
s.cell_bg_type = ImGui.Combo("cell bg type", s.cell_bg_type, "None\0Blue\0"); ImGui.SameLine(); HelpMarker("We are colorizing cells to B1->C2 here.")
assert(s.row_bg_type >= 0 and s.row_bg_type <= 2)
assert(s.row_bg_target >= 0 and s.row_bg_target <= 1)
assert(s.cell_bg_type >= 0 and s.cell_bg_type <= 1)
PopStyleCompact()
if ImGui.BeginTable("table1", 5, s.flags) then
for row = 0, 6 - 1 do
ImGui.TableNextRow()
-- Demonstrate setting a row background color with 'ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBgX, ...)'
-- We use a transparent color so we can see the one behind in case our target is RowBg1 and RowBg0 was already targeted by the ImGuiTableFlags_RowBg flag.
if s.row_bg_type ~= 0 then
local row_bg_color = ImGui.GetColorU32((s.row_bg_type == 1) and ImVec4(0.7, 0.3, 0.3, 0.65) or ImVec4(0.2 + row * 0.1, 0.2, 0.2, 0.65)) -- Flat or Gradient?
ImGui.TableSetBgColor(1 --[[ImGuiTableBgTarget.RowBg0]] + s.row_bg_target, row_bg_color)
end
-- Fill cells
for column = 0, 5 - 1 do
ImGui.TableSetColumnIndex(column)
ImGui.Text("%c%c", string.byte("A") + row, string.byte("0") + column)
-- Change background of Cells B1->C2
-- Demonstrate setting a cell background color with 'ImGui.TableSetBgColor(ImGuiTableBgTarget.CellBg, ...)'
-- (the CellBg color will be blended over the RowBg and ColumnBg colors)
-- We can also pass a column number as a third parameter to TableSetBgColor() and do this outside the column loop.
if row >= 1 and row <= 2 and column >= 1 and column <= 2 and s.cell_bg_type == 1 then
local cell_bg_color = ImGui.GetColorU32(ImVec4(0.3, 0.3, 0.7, 0.65))
ImGui.TableSetBgColor(3 --[[ImGuiTableBgTarget.CellBg]], cell_bg_color)
end
end
end
ImGui.EndTable()
end
ImGui.TreePop()
end
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Tree view") then
IMGUI_DEMO_MARKER("Tables/Tree view")
local s = s.TreeView
_, s.tree_node_flags_base = ImGui.CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", s.tree_node_flags_base, 4096 --[[ImGuiTreeNodeFlags.SpanFullWidth]])
_, s.tree_node_flags_base = ImGui.CheckboxFlags("ImGuiTreeNodeFlags_SpanLabelWidth", s.tree_node_flags_base, 8192 --[[ImGuiTreeNodeFlags.SpanLabelWidth]])
_, s.tree_node_flags_base = ImGui.CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", s.tree_node_flags_base, 16384 --[[ImGuiTreeNodeFlags.SpanAllColumns]])
_, s.tree_node_flags_base = ImGui.CheckboxFlags("ImGuiTreeNodeFlags_LabelSpanAllColumns", s.tree_node_flags_base, 32768 --[[ImGuiTreeNodeFlags.LabelSpanAllColumns]])
ImGui.SameLine(); HelpMarker("Useful if you know that you aren't displaying contents in other columns")
HelpMarker("See \"Columns flags\" section to configure how indentation is applied to individual columns.")
if ImGui.BeginTable("3ways", 3, s.table_flags) then
-- The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On
ImGui.TableSetupColumn("Name", 128 --[[ImGuiTableColumnFlags.NoHide]])
ImGui.TableSetupColumn("Size", 16 --[[ImGuiTableColumnFlags.WidthFixed]], TEXT_BASE_WIDTH * 12.0)
ImGui.TableSetupColumn("Type", 16 --[[ImGuiTableColumnFlags.WidthFixed]], TEXT_BASE_WIDTH * 18.0)
ImGui.TableHeadersRow()
-- Simple storage to output a dummy file-system.
-- struct MyTreeNode { Name, Type, Size, ChildIdx, ChildCount } (see s.nodes)
local MyTreeNode = s.MyTreeNode
if MyTreeNode == nil then -- [Port] local struct with a static method, created once and kept in statics
MyTreeNode = {}
function MyTreeNode.DisplayNode(node, all_nodes)
ImGui.TableNextRow()
ImGui.TableNextColumn()
local is_folder = (node.ChildCount > 0)
local node_flags = s.tree_node_flags_base
if node ~= all_nodes[1] then
node_flags = bit32.band(node_flags, bit32.bnot(32768 --[[ImGuiTreeNodeFlags.LabelSpanAllColumns]])) -- Only demonstrate this on the root node.
end
if is_folder then
local open = ImGui.TreeNodeEx(node.Name, node_flags)
if bit32.band(node_flags, 32768 --[[ImGuiTreeNodeFlags.LabelSpanAllColumns]]) == 0 then
ImGui.TableNextColumn()
ImGui.TextDisabled("--")
ImGui.TableNextColumn()
ImGui.TextUnformatted(node.Type)
end
if open then
for child_n = 0, node.ChildCount - 1 do
MyTreeNode.DisplayNode(all_nodes[node.ChildIdx + child_n + 1], all_nodes)
end
ImGui.TreePop()
end
else
ImGui.TreeNodeEx(node.Name, bit32.bor(node_flags, 256 --[[ImGuiTreeNodeFlags.Leaf]], 512 --[[ImGuiTreeNodeFlags.Bullet]], 8 --[[ImGuiTreeNodeFlags.NoTreePushOnOpen]]))
ImGui.TableNextColumn()
ImGui.Text("%d", node.Size)
ImGui.TableNextColumn()
ImGui.TextUnformatted(node.Type)
end
end
s.MyTreeNode = MyTreeNode
end
local nodes = s.nodes
MyTreeNode.DisplayNode(nodes[1], nodes)
ImGui.EndTable()
end
ImGui.TreePop()
end
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Item width") then
IMGUI_DEMO_MARKER("Tables/Item width")
HelpMarker(
"Showcase using PushItemWidth() and how it is preserved on a per-column basis.\n\n" ..
"Note that on auto-resizing non-resizable fixed columns, querying the content width for " ..
"e.g. right-alignment doesn't make sense.")
if ImGui.BeginTable("table_item_width", 3, 1920 --[[ImGuiTableFlags.Borders]]) then
ImGui.TableSetupColumn("small")
ImGui.TableSetupColumn("half")
ImGui.TableSetupColumn("right-align")
ImGui.TableHeadersRow()
for row = 0, 3 - 1 do
ImGui.TableNextRow()
if row == 0 then
-- Setup ItemWidth once (instead of setting up every time, which is also possible but less efficient)
ImGui.TableSetColumnIndex(0)
ImGui.PushItemWidth(TEXT_BASE_WIDTH * 3.0) -- Small
ImGui.TableSetColumnIndex(1)
ImGui.PushItemWidth(-ImGui.GetContentRegionAvail().x * 0.5)
ImGui.TableSetColumnIndex(2)
ImGui.PushItemWidth(-1.1754943508223e-38 --[[FLT_MIN]]) -- Right-aligned
end
-- Draw our contents
local s = s.ItemWidth
ImGui.PushID(row)
ImGui.TableSetColumnIndex(0)
s.dummy_f = ImGui.SliderFloat("float0", s.dummy_f, 0.0, 1.0)
ImGui.TableSetColumnIndex(1)
s.dummy_f = ImGui.SliderFloat("float1", s.dummy_f, 0.0, 1.0)
ImGui.TableSetColumnIndex(2)
s.dummy_f = ImGui.SliderFloat("##float2", s.dummy_f, 0.0, 1.0) -- No visible label since right-aligned
ImGui.PopID()
end
ImGui.EndTable()
end
ImGui.TreePop()
end
-- Demonstrate using TableHeader() calls instead of TableHeadersRow()
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Custom headers") then
IMGUI_DEMO_MARKER("Tables/Custom headers")
local COLUMNS_COUNT = 3
if ImGui.BeginTable("table_custom_headers", COLUMNS_COUNT, bit32.bor(1920 --[[ImGuiTableFlags.Borders]], 2 --[[ImGuiTableFlags.Reorderable]], 4 --[[ImGuiTableFlags.Hideable]])) then
ImGui.TableSetupColumn("Apricot")
ImGui.TableSetupColumn("Banana")
ImGui.TableSetupColumn("Cherry")
-- Dummy entire-column selection storage
-- FIXME: It would be nice to actually demonstrate full-featured selection using those checkbox.
local column_selected = s.CustomHeaders.column_selected -- 1-based
-- Instead of calling TableHeadersRow() we'll submit custom headers ourselves.
-- (A different approach is also possible:
-- - Specify ImGuiTableColumnFlags_NoHeaderLabel in some TableSetupColumn() call.
-- - Call TableHeadersRow() normally. This will submit TableHeader() with no name.
-- - Then call TableSetColumnIndex() to position yourself in the column and submit your stuff e.g. Checkbox().)
ImGui.TableNextRow(1 --[[ImGuiTableRowFlags.Headers]])
for column = 0, COLUMNS_COUNT - 1 do
ImGui.TableSetColumnIndex(column)
local column_name = ImGui.TableGetColumnName(column) -- Retrieve name passed to TableSetupColumn()
ImGui.PushID(column)
ImGui.PushStyleVar(11 --[[ImGuiStyleVar.FramePadding]], ImVec2(0, 0))
_, column_selected[column + 1] = ImGui.Checkbox("##checkall", column_selected[column + 1])
ImGui.PopStyleVar()
ImGui.SameLine(0.0, ImGui.GetStyle().ItemInnerSpacing.x)
ImGui.TableHeader(column_name)
ImGui.PopID()
end
-- Submit table contents
for row = 0, 5 - 1 do
ImGui.TableNextRow()
for column = 0, 3 - 1 do
local buf = string.format("Cell %d,%d", column, row)
ImGui.TableSetColumnIndex(column)
ImGui.Selectable(buf, column_selected[column + 1])
end
end
ImGui.EndTable()
end
ImGui.TreePop()
end
-- Demonstrate using ImGuiTableColumnFlags_AngledHeader flag to create angled headers
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Angled headers") then
IMGUI_DEMO_MARKER("Tables/Angled headers")
local column_names = { "Track", "cabasa", "ride", "smash", "tom-hi", "tom-mid", "tom-low", "hihat-o", "hihat-c", "snare-s", "snare-c", "clap", "rim", "kick" } -- 1-based
local columns_count = #column_names
local rows_count = 12
local s = s.AngledHeaders
-- static bool bools[columns_count * rows_count] = {} -- Dummy storage selection storage
local bools = s.bools -- 1-based
if #bools == 0 then
for n = 1, columns_count * rows_count do bools[n] = false end
end
_, s.table_flags = ImGui.CheckboxFlags("_ScrollX", s.table_flags, 16777216 --[[ImGuiTableFlags.ScrollX]])
_, s.table_flags = ImGui.CheckboxFlags("_ScrollY", s.table_flags, 33554432 --[[ImGuiTableFlags.ScrollY]])
_, s.table_flags = ImGui.CheckboxFlags("_Resizable", s.table_flags, 1 --[[ImGuiTableFlags.Resizable]])
_, s.table_flags = ImGui.CheckboxFlags("_Sortable", s.table_flags, 8 --[[ImGuiTableFlags.Sortable]])
_, s.table_flags = ImGui.CheckboxFlags("_NoBordersInBody", s.table_flags, 2048 --[[ImGuiTableFlags.NoBordersInBody]])
_, s.table_flags = ImGui.CheckboxFlags("_HighlightHoveredColumn", s.table_flags, 268435456 --[[ImGuiTableFlags.HighlightHoveredColumn]])
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 8)
s.frozen_cols = ImGui.SliderInt("Frozen columns", s.frozen_cols, 0, 2)
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 8)
s.frozen_rows = ImGui.SliderInt("Frozen rows", s.frozen_rows, 0, 2)
_, s.column_flags = ImGui.CheckboxFlags("Disable header contributing to column width", s.column_flags, 8192 --[[ImGuiTableColumnFlags.NoHeaderWidth]])
if ImGui.TreeNode("Style settings") then
ImGui.SameLine()
HelpMarker("Giving access to some ImGuiStyle value in this demo for convenience.")
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 8)
ImGui.GetStyle().TableAngledHeadersAngle = ImGui.SliderAngle("style.TableAngledHeadersAngle", ImGui.GetStyle().TableAngledHeadersAngle, -50.0, 50.0)
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 8)
ImGui.SliderFloat2("style.TableAngledHeadersTextAlign", ImGui.GetStyle().TableAngledHeadersTextAlign, 0.0, 1.0, "%.2f")
ImGui.TreePop()
end
if ImGui.BeginTable("table_angled_headers", columns_count, s.table_flags, ImVec2(0.0, TEXT_BASE_HEIGHT * 12)) then
ImGui.TableSetupColumn(column_names[1], bit32.bor(128 --[[ImGuiTableColumnFlags.NoHide]], 64 --[[ImGuiTableColumnFlags.NoReorder]]))
for n = 1, columns_count - 1 do
ImGui.TableSetupColumn(column_names[n + 1], s.column_flags)
end
ImGui.TableSetupScrollFreeze(s.frozen_cols, s.frozen_rows)
ImGui.TableAngledHeadersRow() -- Draw angled headers for all columns with the ImGuiTableColumnFlags_AngledHeader flag.
ImGui.TableHeadersRow() -- Draw remaining headers and allow access to context-menu and other functions.
for row = 0, rows_count - 1 do
ImGui.PushID(row)
ImGui.TableNextRow()
ImGui.TableSetColumnIndex(0)
ImGui.AlignTextToFramePadding()
ImGui.Text("Track %d", row)
for column = 1, columns_count - 1 do
if ImGui.TableSetColumnIndex(column) then
ImGui.PushID(column)
_, bools[row * columns_count + column + 1] = ImGui.Checkbox("", bools[row * columns_count + column + 1])
ImGui.PopID()
end
end
ImGui.PopID()
end
ImGui.EndTable()
end
ImGui.TreePop()
end
-- Demonstrate creating custom context menus inside columns,
-- while playing it nice with context menus provided by TableHeadersRow()/TableHeader()
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Context menus") then
IMGUI_DEMO_MARKER("Tables/Context menus")
HelpMarker(
"By default, right-clicking over a TableHeadersRow()/TableHeader() line will open the default context-menu.\n" ..
"Using ImGuiTableFlags_ContextMenuInBody we also allow right-clicking over columns body.")
local s = s.ContextMenus
PushStyleCompact()
_, s.flags1 = ImGui.CheckboxFlags("ImGuiTableFlags_ContextMenuInBody", s.flags1, 32 --[[ImGuiTableFlags.ContextMenuInBody]])
PopStyleCompact()
-- Context Menus: first example
-- [1.1] Right-click on the TableHeadersRow() line to open the default table context menu.
-- [1.2] Right-click in columns also open the default table context menu (if ImGuiTableFlags_ContextMenuInBody is set)
local COLUMNS_COUNT = 3
if ImGui.BeginTable("table_context_menu", COLUMNS_COUNT, s.flags1) then
ImGui.TableSetupColumn("One")
ImGui.TableSetupColumn("Two")
ImGui.TableSetupColumn("Three")
-- [1.1]] Right-click on the TableHeadersRow() line to open the default table context menu.
ImGui.TableHeadersRow()
-- Submit dummy contents
for row = 0, 4 - 1 do
ImGui.TableNextRow()
for column = 0, COLUMNS_COUNT - 1 do
ImGui.TableSetColumnIndex(column)
ImGui.Text("Cell %d,%d", column, row)
end
end
ImGui.EndTable()
end
-- Context Menus: second example
-- [2.1] Right-click on the TableHeadersRow() line to open the default table context menu.
-- [2.2] Right-click on the ".." to open a custom popup
-- [2.3] Right-click in columns to open another custom popup
HelpMarker(
"Demonstrate mixing table context menu (over header), item context button (over button) " ..
"and custom per-column context menu (over column body).")
local flags2 = bit32.bor(1 --[[ImGuiTableFlags.Resizable]], 8192 --[[ImGuiTableFlags.SizingFixedFit]], 2 --[[ImGuiTableFlags.Reorderable]], 4 --[[ImGuiTableFlags.Hideable]], 1920 --[[ImGuiTableFlags.Borders]])
if ImGui.BeginTable("table_context_menu_2", COLUMNS_COUNT, flags2) then
ImGui.TableSetupColumn("One")
ImGui.TableSetupColumn("Two")
ImGui.TableSetupColumn("Three")
-- [2.1] Right-click on the TableHeadersRow() line to open the default table context menu.
ImGui.TableHeadersRow()
for row = 0, 4 - 1 do
ImGui.TableNextRow()
for column = 0, COLUMNS_COUNT - 1 do
-- Submit dummy contents
ImGui.TableSetColumnIndex(column)
ImGui.Text("Cell %d,%d", column, row)
ImGui.SameLine()
-- [2.2] Right-click on the ".." to open a custom popup
ImGui.PushID(row * COLUMNS_COUNT + column)
ImGui.SmallButton("..")
if ImGui.BeginPopupContextItem() then
ImGui.Text("This is the popup for Button(\"..\") in Cell %d,%d", column, row)
if ImGui.Button("Close") then
ImGui.CloseCurrentPopup()
end
ImGui.EndPopup()
end
ImGui.PopID()
end
end
-- [2.3] Right-click anywhere in columns to open another custom popup
-- (instead of testing for !IsAnyItemHovered() we could also call OpenPopup() with ImGuiPopupFlags_NoOpenOverExistingPopup
-- to manage popup priority as the popups triggers, here "are we hovering a column" are overlapping)
local hovered_column = -1
for column = 0, COLUMNS_COUNT + 1 - 1 do
ImGui.PushID(column)
if bit32.band(ImGui.TableGetColumnFlags(column), 134217728 --[[ImGuiTableColumnFlags.IsHovered]]) ~= 0 then
hovered_column = column
end
if hovered_column == column and not ImGui.IsAnyItemHovered() and ImGui.IsMouseReleased(1) then
ImGui.OpenPopup("MyPopup")
end
if ImGui.BeginPopup("MyPopup") then
if column == COLUMNS_COUNT then
ImGui.Text("This is a custom popup for unused space after the last column.")
else
ImGui.Text("This is a custom popup for Column %d", column)
end
if ImGui.Button("Close") then
ImGui.CloseCurrentPopup()
end
ImGui.EndPopup()
end
ImGui.PopID()
end
ImGui.EndTable()
ImGui.Text("Hovered column: %d", hovered_column)
end
ImGui.TreePop()
end
-- Demonstrate creating multiple tables with the same ID
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Synced instances") then
IMGUI_DEMO_MARKER("Tables/Synced instances")
HelpMarker("Multiple tables with the same identifier will share their settings, width, visibility, order etc.")
local s = s.SyncedInstances
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_Resizable", s.flags, 1 --[[ImGuiTableFlags.Resizable]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_ScrollY", s.flags, 33554432 --[[ImGuiTableFlags.ScrollY]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_SizingFixedFit", s.flags, 8192 --[[ImGuiTableFlags.SizingFixedFit]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", s.flags, 268435456 --[[ImGuiTableFlags.HighlightHoveredColumn]])
for n = 0, 3 - 1 do
local buf = string.format("Synced Table %d", n)
local open = ImGui.CollapsingHeader(buf, 32 --[[ImGuiTreeNodeFlags.DefaultOpen]])
if open and ImGui.BeginTable("Table", 3, s.flags, ImVec2(0.0, ImGui.GetTextLineHeightWithSpacing() * 5)) then
ImGui.TableSetupColumn("One")
ImGui.TableSetupColumn("Two")
ImGui.TableSetupColumn("Three")
ImGui.TableHeadersRow()
local cell_count = (n == 1) and 27 or 9 -- Make second table have a scrollbar to verify that additional decoration is not affecting column positions.
for cell = 0, cell_count - 1 do
ImGui.TableNextColumn()
ImGui.Text("this cell %d", cell)
end
ImGui.EndTable()
end
end
ImGui.TreePop()
end
-- Demonstrate using Sorting facilities
-- This is a simplified version of the "Advanced" example, where we mostly focus on the code necessary to handle sorting.
-- Note that the "Advanced" example also showcase manually triggering a sort (e.g. if item quantities have been modified)
local template_items_names = s.template_items_names -- 1-based
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Sorting") then
IMGUI_DEMO_MARKER("Tables/Sorting")
local s = s.Sorting
-- Create item list
local items = s.items
if items.Size == 0 then
items:resize(50)
for n = 0, items.Size - 1 do
local template_n = n % #template_items_names
local item = MyItem(); items.Data[n + 1] = item -- [Port] resize(50, MyItem()) would share one table
item.ID = n
item.Name = template_items_names[template_n + 1]
item.Quantity = (n * n - n) % 20 -- Assign default quantities
end
end
-- Options
PushStyleCompact()
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_SortMulti", s.flags, 67108864 --[[ImGuiTableFlags.SortMulti]])
ImGui.SameLine(); HelpMarker("When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).")
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_SortTristate", s.flags, 134217728 --[[ImGuiTableFlags.SortTristate]])
ImGui.SameLine(); HelpMarker("When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).")
PopStyleCompact()
if ImGui.BeginTable("table_sorting", 4, s.flags, ImVec2(0.0, TEXT_BASE_HEIGHT * 15), 0.0) then
-- Declare columns
-- We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications.
-- This is so our sort function can identify a column given our own identifier. We could also identify them based on their index!
-- Demonstrate using a mixture of flags among available sort-related flags:
-- - ImGuiTableColumnFlags_DefaultSort
-- - ImGuiTableColumnFlags_NoSort / ImGuiTableColumnFlags_NoSortAscending / ImGuiTableColumnFlags_NoSortDescending
-- - ImGuiTableColumnFlags_PreferSortAscending / ImGuiTableColumnFlags_PreferSortDescending
ImGui.TableSetupColumn("ID", bit32.bor(4 --[[ImGuiTableColumnFlags.DefaultSort]], 16 --[[ImGuiTableColumnFlags.WidthFixed]]), 0.0, MyItemColumnID_ID)
ImGui.TableSetupColumn("Name", 16 --[[ImGuiTableColumnFlags.WidthFixed]], 0.0, MyItemColumnID_Name)
ImGui.TableSetupColumn("Action", bit32.bor(512 --[[ImGuiTableColumnFlags.NoSort]], 16 --[[ImGuiTableColumnFlags.WidthFixed]]), 0.0, MyItemColumnID_Action)
ImGui.TableSetupColumn("Quantity", bit32.bor(32768 --[[ImGuiTableColumnFlags.PreferSortDescending]], 8 --[[ImGuiTableColumnFlags.WidthStretch]]), 0.0, MyItemColumnID_Quantity)
ImGui.TableSetupScrollFreeze(0, 1) -- Make row always visible
ImGui.TableHeadersRow()
-- Sort our data if sort specs have been changed!
local sort_specs = ImGui.TableGetSortSpecs()
if sort_specs then
if sort_specs.SpecsDirty then
MyItem_SortWithSortSpecs(sort_specs, items.Data, items.Size)
sort_specs.SpecsDirty = false
end
end
-- Demonstrate using clipper for large vertical lists
local clipper = ImGuiListClipper()
clipper:Begin(items.Size)
while clipper:Step() do
for row_n = clipper.DisplayStart, clipper.DisplayEnd - 1 do
-- Display a data item
local item = items.Data[row_n + 1]
ImGui.PushID(item.ID)
ImGui.TableNextRow()
ImGui.TableNextColumn()
ImGui.Text("%04d", item.ID)
ImGui.TableNextColumn()
ImGui.TextUnformatted(item.Name)
ImGui.TableNextColumn()
ImGui.SmallButton("None")
ImGui.TableNextColumn()
ImGui.Text("%d", item.Quantity)
ImGui.PopID()
end
end
ImGui.EndTable()
end
ImGui.TreePop()
end
-- In this example we'll expose most table flags and settings.
-- For specific flags and settings refer to the corresponding section for more detailed explanation.
-- This section is mostly useful to experiment with combining certain flags or settings with each others.
--ImGui.SetNextItemOpen(true, ImGuiCond.Once) -- [DEBUG]
if open_action ~= -1 then
ImGui.SetNextItemOpen(open_action ~= 0)
end
if ImGui.TreeNode("Advanced") then
IMGUI_DEMO_MARKER("Tables/Advanced")
local s = s.Advanced
local CT_Text, CT_Button, CT_SmallButton, CT_FillButton, CT_Selectable, CT_SelectableSpanRow = 0, 1, 2, 3, 4, 5 -- enum ContentsType
local contents_type_names = { "Text", "Button", "SmallButton", "FillButton", "Selectable", "Selectable (span row)" }
if s.outer_size_value == nil then s.outer_size_value = ImVec2(0.0, TEXT_BASE_HEIGHT * 12) end
-- static float row_min_height = 0.0f -- Auto
-- static float inner_width_with_scroll = 0.0f -- Auto-extend
--static ImGuiTextFilter filter
--ImGui.SetNextItemOpen(true, ImGuiCond.Once) -- FIXME-TABLE: Enabling this results in initial clipped first pass on table which tend to affect column sizing
if ImGui.TreeNode("Options") then
-- Make the UI compact because there are so many fields
PushStyleCompact()
ImGui.PushItemWidth(TEXT_BASE_WIDTH * 28.0)
if ImGui.TreeNodeEx("Features:", 32 --[[ImGuiTreeNodeFlags.DefaultOpen]]) then
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_Resizable", s.flags, 1 --[[ImGuiTableFlags.Resizable]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_Reorderable", s.flags, 2 --[[ImGuiTableFlags.Reorderable]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_Hideable", s.flags, 4 --[[ImGuiTableFlags.Hideable]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_Sortable", s.flags, 8 --[[ImGuiTableFlags.Sortable]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_NoSavedSettings", s.flags, 16 --[[ImGuiTableFlags.NoSavedSettings]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_ContextMenuInBody", s.flags, 32 --[[ImGuiTableFlags.ContextMenuInBody]])
ImGui.TreePop()
end
if ImGui.TreeNodeEx("Decorations:", 32 --[[ImGuiTreeNodeFlags.DefaultOpen]]) then
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_RowBg", s.flags, 64 --[[ImGuiTableFlags.RowBg]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_BordersV", s.flags, 1536 --[[ImGuiTableFlags.BordersV]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_BordersOuterV", s.flags, 1024 --[[ImGuiTableFlags.BordersOuterV]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_BordersInnerV", s.flags, 512 --[[ImGuiTableFlags.BordersInnerV]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_BordersH", s.flags, 384 --[[ImGuiTableFlags.BordersH]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_BordersOuterH", s.flags, 256 --[[ImGuiTableFlags.BordersOuterH]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_BordersInnerH", s.flags, 128 --[[ImGuiTableFlags.BordersInnerH]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_NoBordersInBody", s.flags, 2048 --[[ImGuiTableFlags.NoBordersInBody]]); ImGui.SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers)")
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", s.flags, 4096 --[[ImGuiTableFlags.NoBordersInBodyUntilResize]]); ImGui.SameLine(); HelpMarker("Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)")
ImGui.TreePop()
end
if ImGui.TreeNodeEx("Sizing:", 32 --[[ImGuiTreeNodeFlags.DefaultOpen]]) then
s.flags = EditTableSizingFlags(s.flags)
ImGui.SameLine(); HelpMarker("In the Advanced demo we override the policy of each column so those table-wide settings have less effect that typical.")
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_NoHostExtendX", s.flags, 65536 --[[ImGuiTableFlags.NoHostExtendX]])
ImGui.SameLine(); HelpMarker("Make outer width auto-fit to columns, overriding outer_size.x value.\n\nOnly available when ScrollX/ScrollY are disabled and Stretch columns are not used.")
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_NoHostExtendY", s.flags, 131072 --[[ImGuiTableFlags.NoHostExtendY]])
ImGui.SameLine(); HelpMarker("Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit).\n\nOnly available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible.")
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", s.flags, 262144 --[[ImGuiTableFlags.NoKeepColumnsVisible]])
ImGui.SameLine(); HelpMarker("Only available if ScrollX is disabled.")
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_PreciseWidths", s.flags, 524288 --[[ImGuiTableFlags.PreciseWidths]])
ImGui.SameLine(); HelpMarker("Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth.")
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_NoClip", s.flags, 1048576 --[[ImGuiTableFlags.NoClip]])
ImGui.SameLine(); HelpMarker("Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with ScrollFreeze options.")
ImGui.TreePop()
end
if ImGui.TreeNodeEx("Padding:", 32 --[[ImGuiTreeNodeFlags.DefaultOpen]]) then
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_PadOuterX", s.flags, 2097152 --[[ImGuiTableFlags.PadOuterX]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_NoPadOuterX", s.flags, 4194304 --[[ImGuiTableFlags.NoPadOuterX]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_NoPadInnerX", s.flags, 8388608 --[[ImGuiTableFlags.NoPadInnerX]])
ImGui.TreePop()
end
if ImGui.TreeNodeEx("Scrolling:", 32 --[[ImGuiTreeNodeFlags.DefaultOpen]]) then
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_ScrollX", s.flags, 16777216 --[[ImGuiTableFlags.ScrollX]])
ImGui.SameLine()
ImGui.SetNextItemWidth(ImGui.GetFrameHeight())
s.freeze_cols = ImGui.DragInt("freeze_cols", s.freeze_cols, 0.2, 0, 9, nil, 128 --[[ImGuiSliderFlags.NoInput]])
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_ScrollY", s.flags, 33554432 --[[ImGuiTableFlags.ScrollY]])
ImGui.SameLine()
ImGui.SetNextItemWidth(ImGui.GetFrameHeight())
s.freeze_rows = ImGui.DragInt("freeze_rows", s.freeze_rows, 0.2, 0, 9, nil, 128 --[[ImGuiSliderFlags.NoInput]])
ImGui.TreePop()
end
if ImGui.TreeNodeEx("Sorting:", 32 --[[ImGuiTreeNodeFlags.DefaultOpen]]) then
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_SortMulti", s.flags, 67108864 --[[ImGuiTableFlags.SortMulti]])
ImGui.SameLine(); HelpMarker("When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).")
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_SortTristate", s.flags, 134217728 --[[ImGuiTableFlags.SortTristate]])
ImGui.SameLine(); HelpMarker("When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).")
ImGui.TreePop()
end
if ImGui.TreeNodeEx("Headers:", 32 --[[ImGuiTreeNodeFlags.DefaultOpen]]) then
_, s.show_headers = ImGui.Checkbox("show_headers", s.show_headers)
_, s.flags = ImGui.CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", s.flags, 268435456 --[[ImGuiTableFlags.HighlightHoveredColumn]])
_, s.columns_base_flags = ImGui.CheckboxFlags("ImGuiTableColumnFlags_AngledHeader", s.columns_base_flags, 262144 --[[ImGuiTableColumnFlags.AngledHeader]])
ImGui.SameLine(); HelpMarker("Enable AngledHeader on all columns. Best enabled on selected narrow columns (see \"Angled headers\" section of the demo).")
ImGui.TreePop()
end
if ImGui.TreeNodeEx("Other:", 32 --[[ImGuiTreeNodeFlags.DefaultOpen]]) then
_, s.show_wrapped_text = ImGui.Checkbox("show_wrapped_text", s.show_wrapped_text)
ImGui.DragFloat2("##OuterSize", s.outer_size_value)
ImGui.SameLine(0.0, ImGui.GetStyle().ItemInnerSpacing.x)
_, s.outer_size_enabled = ImGui.Checkbox("outer_size", s.outer_size_enabled)
ImGui.SameLine()
HelpMarker("If scrolling is disabled (ScrollX and ScrollY not set):\n" ..
"- The table is output directly in the parent window.\n" ..
"- OuterSize.x < 0.0f will right-align the table.\n" ..
"- OuterSize.x = 0.0f will narrow fit the table unless there are any Stretch columns.\n" ..
"- OuterSize.y then becomes the minimum size for the table, which will extend vertically if there are more rows (unless NoHostExtendY is set).")
-- From a user point of view we will tend to use 'inner_width' differently depending on whether our table is embedding scrolling.
-- To facilitate toying with this demo we will actually pass 0.0f to the BeginTable() when ScrollX is disabled.
s.inner_width_with_scroll = ImGui.DragFloat("inner_width (when ScrollX active)", s.inner_width_with_scroll, 1.0, 0.0, 3.4028234663853e+38 --[[FLT_MAX]])
s.row_min_height = ImGui.DragFloat("row_min_height", s.row_min_height, 1.0, 0.0, 3.4028234663853e+38 --[[FLT_MAX]])
ImGui.SameLine(); HelpMarker("Specify height of the Selectable item.")
s.items_count = ImGui.DragInt("items_count", s.items_count, 0.1, 0, 9999)
s.contents_type = ImGui.Combo("items_type (first column)", s.contents_type, contents_type_names, #contents_type_names)
--filter.Draw("filter")
ImGui.TreePop()
end
ImGui.PopItemWidth()
PopStyleCompact()
ImGui.Spacing()
ImGui.TreePop()
end
-- Update item list if we changed the number of items
local items = s.items
local selection = s.selection
if items.Size ~= s.items_count then
items:resize(s.items_count)
for n = 0, s.items_count - 1 do
local template_n = n % #template_items_names
local item = items.Data[n + 1]
if item == nil then item = MyItem(); items.Data[n + 1] = item end -- [Port] resize(n, MyItem()) would share one table
item.ID = n
item.Name = template_items_names[template_n + 1]
item.Quantity = (template_n == 3) and 10 or (template_n == 4) and 20 or 0 -- Assign default quantities
end
end
local parent_draw_list = ImGui.GetWindowDrawList()
local parent_draw_list_draw_cmd_count = parent_draw_list.CmdBuffer.Size
local table_scroll_cur, table_scroll_max -- For debug display
local table_draw_list = nil -- "
-- Submit table
local flags = s.flags
local columns_base_flags = s.columns_base_flags
local inner_width_to_use = (bit32.band(flags, 16777216 --[[ImGuiTableFlags.ScrollX]]) ~= 0) and s.inner_width_with_scroll or 0.0
if ImGui.BeginTable("table_advanced", 6, flags, s.outer_size_enabled and s.outer_size_value or ImVec2(0, 0), inner_width_to_use) then
-- Declare columns
-- We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications.
-- This is so our sort function can identify a column given our own identifier. We could also identify them based on their index!
ImGui.TableSetupColumn("ID", bit32.bor(columns_base_flags, 4 --[[ImGuiTableColumnFlags.DefaultSort]], 16 --[[ImGuiTableColumnFlags.WidthFixed]], 128 --[[ImGuiTableColumnFlags.NoHide]]), 0.0, MyItemColumnID_ID)
ImGui.TableSetupColumn("Name", bit32.bor(columns_base_flags, 16 --[[ImGuiTableColumnFlags.WidthFixed]]), 0.0, MyItemColumnID_Name)
ImGui.TableSetupColumn("Action", bit32.bor(columns_base_flags, 512 --[[ImGuiTableColumnFlags.NoSort]], 16 --[[ImGuiTableColumnFlags.WidthFixed]]), 0.0, MyItemColumnID_Action)
ImGui.TableSetupColumn("Quantity", bit32.bor(columns_base_flags, 32768 --[[ImGuiTableColumnFlags.PreferSortDescending]]), 0.0, MyItemColumnID_Quantity)
ImGui.TableSetupColumn("Description", bit32.bor(columns_base_flags, (bit32.band(flags, 65536 --[[ImGuiTableFlags.NoHostExtendX]]) ~= 0) and 0 or 8 --[[ImGuiTableColumnFlags.WidthStretch]]), 0.0, MyItemColumnID_Description)
ImGui.TableSetupColumn("Hidden", bit32.bor(columns_base_flags, 2 --[[ImGuiTableColumnFlags.DefaultHide]], 512 --[[ImGuiTableColumnFlags.NoSort]]))
ImGui.TableSetupScrollFreeze(s.freeze_cols, s.freeze_rows)
-- Sort our data if sort specs have been changed!
local sort_specs = ImGui.TableGetSortSpecs()
if sort_specs and sort_specs.SpecsDirty then
s.items_need_sort = true
end
if sort_specs and s.items_need_sort and items.Size > 1 then
MyItem_SortWithSortSpecs(sort_specs, items.Data, items.Size)
sort_specs.SpecsDirty = false
end
s.items_need_sort = false
-- Take note of whether we are currently sorting based on the Quantity field,
-- we will use this to trigger sorting when we know the data of this column has been modified.
local sorts_specs_using_quantity = bit32.band(ImGui.TableGetColumnFlags(3), 67108864 --[[ImGuiTableColumnFlags.IsSorted]]) ~= 0
-- Show headers
if s.show_headers and bit32.band(columns_base_flags, 262144 --[[ImGuiTableColumnFlags.AngledHeader]]) ~= 0 then
ImGui.TableAngledHeadersRow()
end
if s.show_headers then
ImGui.TableHeadersRow()
end
-- Show data
-- FIXME-TABLE FIXME-NAV: How we can get decent up/down even though we have the buttons here?
-- Demonstrate using clipper for large vertical lists
local clipper = ImGuiListClipper()
clipper:Begin(items.Size)
while clipper:Step() do
for row_n = clipper.DisplayStart, clipper.DisplayEnd - 1 do
local item = items.Data[row_n + 1]
--if not filter:PassFilter(item.Name) then
-- continue
local item_is_selected = selection:contains(item.ID)
ImGui.PushID(item.ID)
ImGui.TableNextRow(0 --[[ImGuiTableRowFlags.None]], s.row_min_height)
-- For the demo purpose we can select among different type of items submitted in the first column
ImGui.TableSetColumnIndex(0)
local label = string.format("%04d", item.ID)
local contents_type = s.contents_type
if contents_type == CT_Text then
ImGui.TextUnformatted(label)
elseif contents_type == CT_Button then
ImGui.Button(label)
elseif contents_type == CT_SmallButton then
ImGui.SmallButton(label)
elseif contents_type == CT_FillButton then
ImGui.Button(label, ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], 0.0))
elseif contents_type == CT_Selectable or contents_type == CT_SelectableSpanRow then
local selectable_flags = (contents_type == CT_SelectableSpanRow) and bit32.bor(2 --[[ImGuiSelectableFlags.SpanAllColumns]], 16 --[[ImGuiSelectableFlags.AllowOverlap]]) or 0 --[[ImGuiSelectableFlags.None]]
if ImGui.Selectable(label, item_is_selected, selectable_flags, ImVec2(0, s.row_min_height)) then
if ImGui.GetIO().KeyCtrl then
if item_is_selected then
selection:find_erase_unsorted(item.ID)
else
selection:push_back(item.ID)
end
else
selection:clear()
selection:push_back(item.ID)
end
end
end
if ImGui.TableSetColumnIndex(1) then
ImGui.TextUnformatted(item.Name)
end
-- Here we demonstrate marking our data set as needing to be sorted again if we modified a quantity,
-- and we are currently sorting on the column showing the Quantity.
-- To avoid triggering a sort while holding the button, we only trigger it when the button has been released.
-- You will probably need some extra logic if you want to automatically sort when a specific entry changes.
if ImGui.TableSetColumnIndex(2) then
if ImGui.SmallButton("Chop") then item.Quantity = item.Quantity + 1 end
if sorts_specs_using_quantity and ImGui.IsItemDeactivated() then s.items_need_sort = true end
ImGui.SameLine()
if ImGui.SmallButton("Eat") then item.Quantity = item.Quantity - 1 end
if sorts_specs_using_quantity and ImGui.IsItemDeactivated() then s.items_need_sort = true end
end
if ImGui.TableSetColumnIndex(3) then
ImGui.Text("%d", item.Quantity)
end
ImGui.TableSetColumnIndex(4)
if s.show_wrapped_text then
ImGui.TextWrapped("Lorem ipsum dolor sit amet")
else
ImGui.Text("Lorem ipsum dolor sit amet")
end
if ImGui.TableSetColumnIndex(5) then
ImGui.Text("1234")
end
ImGui.PopID()
end
end
-- Store some info to display debug details below
table_scroll_cur = ImVec2(ImGui.GetScrollX(), ImGui.GetScrollY())
table_scroll_max = ImVec2(ImGui.GetScrollMaxX(), ImGui.GetScrollMaxY())
table_draw_list = ImGui.GetWindowDrawList()
ImGui.EndTable()
end
_, s.show_debug_details = ImGui.Checkbox("Debug details", s.show_debug_details)
if s.show_debug_details and table_draw_list then
ImGui.SameLine(0.0, 0.0)
local table_draw_list_draw_cmd_count = table_draw_list.CmdBuffer.Size
if table_draw_list == parent_draw_list then
ImGui.Text(": DrawCmd: +%d (in same window)",
table_draw_list_draw_cmd_count - parent_draw_list_draw_cmd_count)
else
ImGui.Text(": DrawCmd: +%d (in child window), Scroll: (%.f/%.f) (%.f/%.f)",
table_draw_list_draw_cmd_count - 1, table_scroll_cur.x, table_scroll_max.x, table_scroll_cur.y, table_scroll_max.y)
end
end
ImGui.TreePop()
end
ImGui.PopID()
DemoWindowColumns()
if s.disable_indent then
ImGui.PopStyleVar()
end
end
S.DemoWindowColumns = {
selected = -1,
h_borders = true,
v_borders = true,
columns_count = 4,
foo = 1.0,
bar = 1.0,
}
function DemoWindowColumns()
local s = S.DemoWindowColumns
local open = ImGui.TreeNode("Legacy Columns API")
ImGui.SameLine()
HelpMarker("Columns() is an old API! Prefer using the more flexible and powerful BeginTable() API!")
if not open then
return
end
-- Basic columns
if ImGui.TreeNode("Basic") then
IMGUI_DEMO_MARKER("Columns (legacy API)/Basic")
ImGui.Text("Without border:")
ImGui.Columns(3, "mycolumns3", false) -- 3-ways, no border
ImGui.Separator()
for n = 0, 13 do
local label = string.format("Item %d", n)
if ImGui.Selectable(label) then end
--if (ImGui.Button(label, ImVec2(-FLT_MIN,0.0))) {}
ImGui.NextColumn()
end
ImGui.Columns(1)
ImGui.Separator()
ImGui.Text("With border:")
ImGui.Columns(4, "mycolumns") -- 4-ways, with border
ImGui.Separator()
ImGui.Text("ID"); ImGui.NextColumn()
ImGui.Text("Name"); ImGui.NextColumn()
ImGui.Text("Path"); ImGui.NextColumn()
ImGui.Text("Hovered"); ImGui.NextColumn()
ImGui.Separator()
local names = { "One", "Two", "Three" }
local paths = { "/path/one", "/path/two", "/path/three" }
for i = 0, 2 do
local label = string.format("%04d", i)
if ImGui.Selectable(label, s.selected == i, 2 --[[ImGuiSelectableFlags.SpanAllColumns]]) then
s.selected = i
end
local hovered = ImGui.IsItemHovered()
ImGui.NextColumn()
ImGui.Text(names[i + 1]); ImGui.NextColumn()
ImGui.Text(paths[i + 1]); ImGui.NextColumn()
ImGui.Text("%d", hovered and 1 or 0); ImGui.NextColumn()
end
ImGui.Columns(1)
ImGui.Separator()
ImGui.TreePop()
end
if ImGui.TreeNode("Borders") then
IMGUI_DEMO_MARKER("Columns (legacy API)/Borders")
-- NB: Future columns API should allow automatic horizontal borders.
local lines_count = 3
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 8)
s.columns_count = ImGui.DragInt("##columns_count", s.columns_count, 0.1, 2, 10, "%d columns")
if s.columns_count < 2 then
s.columns_count = 2
end
ImGui.SameLine()
_, s.h_borders = ImGui.Checkbox("horizontal", s.h_borders)
ImGui.SameLine()
_, s.v_borders = ImGui.Checkbox("vertical", s.v_borders)
ImGui.Columns(s.columns_count, nil, s.v_borders)
for i = 0, s.columns_count * lines_count - 1 do
if s.h_borders and ImGui.GetColumnIndex() == 0 then
ImGui.Separator()
end
ImGui.PushID(i)
local c = string.char(string.byte("a") + i)
ImGui.Text("%s%s%s", c, c, c)
ImGui.Text("Width %.2f", ImGui.GetColumnWidth())
ImGui.Text("Avail %.2f", ImGui.GetContentRegionAvail().x)
ImGui.Text("Offset %.2f", ImGui.GetColumnOffset())
ImGui.Text("Long text that is likely to clip")
ImGui.Button("Button", ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], 0.0))
ImGui.PopID()
ImGui.NextColumn()
end
ImGui.Columns(1)
if s.h_borders then
ImGui.Separator()
end
ImGui.TreePop()
end
-- Create multiple items in a same cell before switching to next column
if ImGui.TreeNode("Mixed items") then
IMGUI_DEMO_MARKER("Columns (legacy API)/Mixed items")
ImGui.Columns(3, "mixed")
ImGui.Separator()
ImGui.Text("Hello")
ImGui.Button("Banana")
ImGui.NextColumn()
ImGui.Text("ImGui")
ImGui.Button("Apple")
s.foo = ImGui.InputFloat("red", s.foo, 0.05, 0, "%.3f")
ImGui.Text("An extra line here.")
ImGui.NextColumn()
ImGui.Text("Sailor")
ImGui.Button("Corniflower")
s.bar = ImGui.InputFloat("blue", s.bar, 0.05, 0, "%.3f")
ImGui.NextColumn()
if ImGui.CollapsingHeader("Category A") then ImGui.Text("Blah blah blah") end ImGui.NextColumn()
if ImGui.CollapsingHeader("Category B") then ImGui.Text("Blah blah blah") end ImGui.NextColumn()
if ImGui.CollapsingHeader("Category C") then ImGui.Text("Blah blah blah") end ImGui.NextColumn()
ImGui.Columns(1)
ImGui.Separator()
ImGui.TreePop()
end
-- Word wrapping
if ImGui.TreeNode("Word-wrapping") then
IMGUI_DEMO_MARKER("Columns (legacy API)/Word-wrapping")
ImGui.Columns(2, "word-wrapping")
ImGui.Separator()
ImGui.TextWrapped("The quick brown fox jumps over the lazy dog.")
ImGui.TextWrapped("Hello Left")
ImGui.NextColumn()
ImGui.TextWrapped("The quick brown fox jumps over the lazy dog.")
ImGui.TextWrapped("Hello Right")
ImGui.Columns(1)
ImGui.Separator()
ImGui.TreePop()
end
if ImGui.TreeNode("Horizontal Scrolling") then
IMGUI_DEMO_MARKER("Columns (legacy API)/Horizontal Scrolling")
ImGui.SetNextWindowContentSize(ImVec2(1500.0, 0.0))
local child_size = ImVec2(0, ImGui.GetFontSize() * 20.0)
ImGui.BeginChild("##ScrollingRegion", child_size, 0 --[[ImGuiChildFlags.None]], 2048 --[[ImGuiWindowFlags.HorizontalScrollbar]])
ImGui.Columns(10)
-- Also demonstrate using clipper for large vertical lists
local ITEMS_COUNT = 2000
local clipper = ImGuiListClipper()
clipper:Begin(ITEMS_COUNT)
while clipper:Step() do
for i = clipper.DisplayStart, clipper.DisplayEnd - 1 do
for j = 0, 9 do
ImGui.Text("Line %d Column %d...", i, j)
ImGui.NextColumn()
end
end
end
ImGui.Columns(1)
ImGui.EndChild()
ImGui.TreePop()
end
if ImGui.TreeNode("Tree") then
IMGUI_DEMO_MARKER("Columns (legacy API)/Tree")
ImGui.Columns(2, "tree", true)
for x = 0, 2 do
local open1 = ImGui.TreeNode(x, "Node%d", x)
ImGui.NextColumn()
ImGui.Text("Node contents")
ImGui.NextColumn()
if open1 then
for y = 0, 2 do
local open2 = ImGui.TreeNode(y, "Node%d.%d", x, y)
ImGui.NextColumn()
ImGui.Text("Node contents")
if open2 then
ImGui.Text("Even more contents")
if ImGui.TreeNode("Tree in column") then
ImGui.Text("The quick brown fox jumps over the lazy dog")
ImGui.TreePop()
end
end
ImGui.NextColumn()
if open2 then
ImGui.TreePop()
end
end
ImGui.TreePop()
end
end
ImGui.Columns(1)
ImGui.TreePop()
end
ImGui.TreePop()
end
-------------------------------------------------------------------------------
-- [SECTION] DemoWindowInputs()
-------------------------------------------------------------------------------
S.DemoWindowInputs = {
funcs = { IsLegacyNativeDupe = function(key) return false end }, -- struct funcs (hoisted to avoid a per-frame closure) -- [Port]
capture_override_mouse = -1,
capture_override_keyboard = -1,
route_options = ImGuiInputFlags.Repeat,
route_type = ImGuiInputFlags.RouteFocused,
f = 0.5,
tabbing_buf = ImGui.NewTextBuffer("hello", 32),
focus_buf = ImGui.NewTextBuffer("click on a button to set focus", 128),
f3 = { 0.0, 0.0, 0.0 },
}
S.DemoWindowInputs.mouse_cursors_names = { "Arrow", "TextInput", "ResizeAll", "ResizeNS", "ResizeEW", "ResizeNESW", "ResizeNWSE", "Hand", "Wait", "Progress", "NotAllowed" }
function DemoWindowInputs()
local s = S.DemoWindowInputs
if ImGui.CollapsingHeader("Inputs & Focus") then
local io = ImGui.GetIO()
-- Display inputs submitted to ImGuiIO
ImGui.SetNextItemOpen(true, 2 --[[ImGuiCond.Once]])
local inputs_opened = ImGui.TreeNode("Inputs")
ImGui.SameLine()
HelpMarker(
"This is a simplified view. See more detailed input state:\n" ..
"- in 'Tools->Metrics/Debugger->Inputs'.\n" ..
"- in 'Tools->Debug Log->IO'.")
if inputs_opened then
IMGUI_DEMO_MARKER("Inputs & Focus/Inputs")
if ImGui.IsMousePosValid() then
ImGui.Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y)
else
ImGui.Text("Mouse pos: <INVALID>")
end
ImGui.Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y)
ImGui.Text("Mouse down:")
for i = 0, 2 do if ImGui.IsMouseDown(i) then ImGui.SameLine(); ImGui.Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]) end end -- IM_COUNTOF(io.MouseDown) == 3, 0-based
ImGui.Text("Mouse wheel: %.1f", io.MouseWheel)
ImGui.Text("Mouse clicked count:")
for i = 0, 2 do if io.MouseClickedCount[i] > 0 then ImGui.SameLine(); ImGui.Text("b%d: %d", i, io.MouseClickedCount[i]) end end
-- We iterate both legacy native range and named ImGuiKey ranges. This is a little unusual/odd but this allows
-- displaying the data for old/new backends.
-- User code should never have to go through such hoops!
-- You can generally iterate between ImGuiKey.NamedKey_BEGIN and ImGuiKey.NamedKey_END.
local funcs = s.funcs
local start_key = 512 --[[ImGuiKey.NamedKey_BEGIN]]
ImGui.Text("Keys down:") for key = start_key, 667 --[[ImGuiKey.NamedKey_END]] - 1 do repeat if funcs.IsLegacyNativeDupe(key) or not ImGui.IsKeyDown(key) then break end ImGui.SameLine(); if key < 512 --[[ImGuiKey.NamedKey_BEGIN]] then ImGui.Text("\"%s\"", ImGui.GetKeyName(key)) else ImGui.Text("\"%s\" %d", ImGui.GetKeyName(key), key) end until true end
ImGui.Text("Keys mods: %s%s%s%s", io.KeyCtrl and "CTRL " or "", io.KeyShift and "SHIFT " or "", io.KeyAlt and "ALT " or "", io.KeySuper and "SUPER " or "")
ImGui.Text("Chars queue:") for i = 0, io.InputQueueCharacters.Size - 1 do local c = io.InputQueueCharacters.Data[i + 1]; ImGui.SameLine(); ImGui.Text("\'%s\' (0x%04X)", (c > 32 and c <= 255) and string.char(c) or '?', c) end -- FIXME: We should convert 'c' to UTF-8 here but the functions are not public.
ImGui.TreePop()
end
-- Display ImGuiIO output flags
ImGui.SetNextItemOpen(true, 2 --[[ImGuiCond.Once]])
local outputs_opened = ImGui.TreeNode("Outputs")
ImGui.SameLine()
HelpMarker(
"The value of io.WantCaptureMouse and io.WantCaptureKeyboard are normally set by Dear ImGui " ..
"to instruct your application of how to route inputs. Typically, when a value is true, it means " ..
"Dear ImGui wants the corresponding inputs and we expect the underlying application to ignore them.\n\n" ..
"The most typical case is: when hovering a window, Dear ImGui set io.WantCaptureMouse to true, " ..
"and underlying application should ignore mouse inputs (in practice there are many and more subtle " ..
"rules leading to how those flags are set).")
if outputs_opened then
IMGUI_DEMO_MARKER("Inputs & Focus/Outputs")
ImGui.Text("io.WantCaptureMouse: %d", io.WantCaptureMouse and 1 or 0)
ImGui.Text("io.WantCaptureMouseUnlessPopupClose: %d", io.WantCaptureMouseUnlessPopupClose and 1 or 0)
ImGui.Text("io.WantCaptureKeyboard: %d", io.WantCaptureKeyboard and 1 or 0)
ImGui.Text("io.WantTextInput: %d", io.WantTextInput and 1 or 0)
ImGui.Text("io.WantSetMousePos: %d", io.WantSetMousePos and 1 or 0)
ImGui.Text("io.NavActive: %d, io.NavVisible: %d", io.NavActive and 1 or 0, io.NavVisible and 1 or 0)
IMGUI_DEMO_MARKER("Inputs & Focus/Outputs/WantCapture override")
if ImGui.TreeNode("WantCapture override") then
HelpMarker(
"Hovering the colored canvas will override io.WantCaptureXXX fields.\n" ..
"Notice how normally (when set to none), the value of io.WantCaptureKeyboard would be false when hovering " ..
"and true when clicking.")
local capture_override_desc = { "None", "Set to false", "Set to true" }
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 15)
s.capture_override_mouse = ImGui.SliderInt("SetNextFrameWantCaptureMouse() on hover", s.capture_override_mouse, -1, 1, capture_override_desc[s.capture_override_mouse + 2], 1536 --[[ImGuiSliderFlags.AlwaysClamp]])
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 15)
s.capture_override_keyboard = ImGui.SliderInt("SetNextFrameWantCaptureKeyboard() on hover", s.capture_override_keyboard, -1, 1, capture_override_desc[s.capture_override_keyboard + 2], 1536 --[[ImGuiSliderFlags.AlwaysClamp]])
ImGui.ColorButton("##panel", ImVec4(0.7, 0.1, 0.7, 1.0), bit32.bor(64 --[[ImGuiColorEditFlags.NoTooltip]], 512 --[[ImGuiColorEditFlags.NoDragDrop]]), ImVec2(128.0, 96.0)) -- Dummy item
if ImGui.IsItemHovered() and s.capture_override_mouse ~= -1 then
ImGui.SetNextFrameWantCaptureMouse(s.capture_override_mouse == 1)
end
if ImGui.IsItemHovered() and s.capture_override_keyboard ~= -1 then
ImGui.SetNextFrameWantCaptureKeyboard(s.capture_override_keyboard == 1)
end
ImGui.TreePop()
end
ImGui.TreePop()
end
-- Demonstrate using Shortcut() and Routing Policies.
-- The general flow is:
-- - Code interested in a chord (e.g. "Ctrl+A") declares their intent.
-- - Multiple locations may be interested in same chord! Routing helps find a winner.
-- - Every frame, we resolve all claims and assign one owner if the modifiers are matching.
-- - The lower-level function is 'bool SetShortcutRouting()', returns true when caller got the route.
-- - Most of the times, SetShortcutRouting() is not called directly. User mostly calls Shortcut() with routing flags.
-- - If you call Shortcut() WITHOUT any routing option, it uses ImGuiInputFlags.RouteFocused.
-- TL;DR: Most uses will simply be:
-- - Shortcut(ImGuiMod_Ctrl | ImGuiKey.A); -- Use ImGuiInputFlags.RouteFocused policy.
if ImGui.TreeNode("Shortcuts") then
IMGUI_DEMO_MARKER("Inputs & Focus/Shortcuts")
_, s.route_options = ImGui.CheckboxFlags("ImGuiInputFlags_Repeat", s.route_options, 1 --[[ImGuiInputFlags.Repeat]])
_, s.route_type = ImGui.RadioButton("ImGuiInputFlags_RouteActive", s.route_type, 1024 --[[ImGuiInputFlags.RouteActive]])
_, s.route_type = ImGui.RadioButton("ImGuiInputFlags_RouteFocused (default)", s.route_type, 2048 --[[ImGuiInputFlags.RouteFocused]])
ImGui.Indent()
ImGui.BeginDisabled(s.route_type ~= 2048 --[[ImGuiInputFlags.RouteFocused]])
_, s.route_options = ImGui.CheckboxFlags("ImGuiInputFlags_RouteOverActive##0", s.route_options, 32768 --[[ImGuiInputFlags.RouteOverActive]])
ImGui.EndDisabled()
ImGui.Unindent()
_, s.route_type = ImGui.RadioButton("ImGuiInputFlags_RouteGlobal", s.route_type, 4096 --[[ImGuiInputFlags.RouteGlobal]])
ImGui.Indent()
ImGui.BeginDisabled(s.route_type ~= 4096 --[[ImGuiInputFlags.RouteGlobal]])
_, s.route_options = ImGui.CheckboxFlags("ImGuiInputFlags_RouteOverFocused", s.route_options, 16384 --[[ImGuiInputFlags.RouteOverFocused]])
_, s.route_options = ImGui.CheckboxFlags("ImGuiInputFlags_RouteOverActive", s.route_options, 32768 --[[ImGuiInputFlags.RouteOverActive]])
_, s.route_options = ImGui.CheckboxFlags("ImGuiInputFlags_RouteUnlessBgFocused", s.route_options, 65536 --[[ImGuiInputFlags.RouteUnlessBgFocused]])
ImGui.EndDisabled()
ImGui.Unindent()
_, s.route_type = ImGui.RadioButton("ImGuiInputFlags_RouteAlways", s.route_type, 8192 --[[ImGuiInputFlags.RouteAlways]])
local flags = bit32.bor(s.route_type, s.route_options) -- Merged flags
if s.route_type ~= 4096 --[[ImGuiInputFlags.RouteGlobal]] then
flags = bit32.band(flags, bit32.bnot(bit32.bor(16384 --[[ImGuiInputFlags.RouteOverFocused]], 32768 --[[ImGuiInputFlags.RouteOverActive]], 65536 --[[ImGuiInputFlags.RouteUnlessBgFocused]])))
end
ImGui.SeparatorText("Using SetNextItemShortcut()")
ImGui.Text("Ctrl+S")
ImGui.SetNextItemShortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 564 --[[ImGuiKey.S]]), bit32.bor(flags, 262144 --[[ImGuiInputFlags.Tooltip]]))
ImGui.Button("Save")
ImGui.Text("Alt+F")
ImGui.SetNextItemShortcut(bit32.bor(16384 --[[ImGuiMod_Alt]], 551 --[[ImGuiKey.F]]), bit32.bor(flags, 262144 --[[ImGuiInputFlags.Tooltip]]))
s.f = ImGui.SliderFloat("Factor", s.f, 0.0, 1.0)
ImGui.SeparatorText("Using Shortcut()")
local line_height = ImGui.GetTextLineHeightWithSpacing()
local key_chord = bit32.bor(4096 --[[ImGuiMod_Ctrl]], 546 --[[ImGuiKey.A]])
ImGui.Text("Ctrl+A")
ImGui.Text("IsWindowFocused: %d, Shortcut: %s", ImGui.IsWindowFocused() and 1 or 0, ImGui.Shortcut(key_chord, flags) and "PRESSED" or "...")
ImGui.PushStyleColor(3 --[[ImGuiCol.ChildBg]], ImVec4(1.0, 0.0, 1.0, 0.1))
ImGui.BeginChild("WindowA", ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], line_height * 14), true)
ImGui.Text("Press Ctrl+A and see who receives it!")
ImGui.Separator()
-- 1: Window polling for Ctrl+A
ImGui.Text("(in WindowA)")
ImGui.Text("IsWindowFocused: %d, Shortcut: %s", ImGui.IsWindowFocused() and 1 or 0, ImGui.Shortcut(key_chord, flags) and "PRESSED" or "...")
-- 2: InputText also polling for Ctrl+A: it always uses _RouteFocused internally (gets priority when active)
-- (Commented because the owner-aware version of Shortcut() is still in imgui_internal.h)
--char str[16] = "Press Ctrl+A";
--ImGui.Spacing();
--ImGui.InputText("InputTextB", str, IM_COUNTOF(str), ImGuiInputTextFlags.ReadOnly);
--ImGuiID item_id = ImGui.GetItemID();
--ImGui.SameLine(); HelpMarker("Internal widgets always use _RouteFocused");
--ImGui.Text("IsWindowFocused: %d, Shortcut: %s", ImGui.IsWindowFocused(), ImGui.Shortcut(key_chord, flags, item_id) ? "PRESSED" : "...");
-- 3: Dummy child is not claiming the route: focusing them shouldn't steal route away from WindowA
ImGui.BeginChild("ChildD", ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], line_height * 4), true)
ImGui.Text("(in ChildD: not using same Shortcut)")
ImGui.Text("IsWindowFocused: %d", ImGui.IsWindowFocused() and 1 or 0)
ImGui.EndChild()
-- 4: Child window polling for Ctrl+A. It is deeper than WindowA and gets priority when focused.
ImGui.BeginChild("ChildE", ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], line_height * 4), true)
ImGui.Text("(in ChildE: using same Shortcut)")
ImGui.Text("IsWindowFocused: %d, Shortcut: %s", ImGui.IsWindowFocused() and 1 or 0, ImGui.Shortcut(key_chord, flags) and "PRESSED" or "...")
ImGui.EndChild()
-- 5: In a popup
if ImGui.Button("Open Popup") then
ImGui.OpenPopup("PopupF")
end
if ImGui.BeginPopup("PopupF") then
ImGui.Text("(in PopupF)")
ImGui.Text("IsWindowFocused: %d, Shortcut: %s", ImGui.IsWindowFocused() and 1 or 0, ImGui.Shortcut(key_chord, flags) and "PRESSED" or "...")
-- (Commented because the owner-aware version of Shortcut() is still in imgui_internal.h)
--ImGui.InputText("InputTextG", str, IM_COUNTOF(str), ImGuiInputTextFlags.ReadOnly);
--ImGui.Text("IsWindowFocused: %d, Shortcut: %s", ImGui.IsWindowFocused(), ImGui.Shortcut(key_chord, flags, ImGui.GetItemID()) ? "PRESSED" : "...");
ImGui.EndPopup()
end
ImGui.EndChild()
ImGui.PopStyleColor()
ImGui.TreePop()
end
-- Display mouse cursors
if ImGui.TreeNode("Mouse Cursors") then
IMGUI_DEMO_MARKER("Inputs & Focus/Mouse Cursors")
local mouse_cursors_names = s.mouse_cursors_names -- 1-based
assert(#mouse_cursors_names == 11 --[[ImGuiMouseCursor.COUNT]])
local current = ImGui.GetMouseCursor()
local cursor_name = "N/A"
if (current >= 0 --[[ImGuiMouseCursor.Arrow]]) and (current < 11 --[[ImGuiMouseCursor.COUNT]]) then cursor_name = mouse_cursors_names[current + 1] end
ImGui.Text("Current mouse cursor = %d: %s", current, cursor_name)
ImGui.BeginDisabled(true)
_, io.BackendFlags = ImGui.CheckboxFlags("io.BackendFlags: HasMouseCursors", io.BackendFlags, 2 --[[ImGuiBackendFlags.HasMouseCursors]])
ImGui.EndDisabled()
ImGui.Text("Hover to see mouse cursors:")
ImGui.SameLine(); HelpMarker(
"Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. " ..
"If software cursor rendering (io.MouseDrawCursor) is set ImGui will draw the right cursor for you, " ..
"otherwise your backend needs to handle it.")
for i = 0, 11 --[[ImGuiMouseCursor.COUNT]] - 1 do
local label = string.format("Mouse cursor %d: %s", i, mouse_cursors_names[i + 1])
ImGui.Bullet(); ImGui.Selectable(label, false)
if ImGui.IsItemHovered() then
ImGui.SetMouseCursor(i)
end
end
ImGui.TreePop()
end
if ImGui.TreeNode("Tabbing") then
IMGUI_DEMO_MARKER("Inputs & Focus/Tabbing")
ImGui.Text("Use Tab/Shift+Tab to cycle through keyboard editable fields.")
local buf = s.tabbing_buf
ImGui.InputText("1", buf, 32)
ImGui.InputText("2", buf, 32)
ImGui.InputText("3", buf, 32)
ImGui.PushItemFlag(1 --[[ImGuiItemFlags.NoTabStop]], true)
ImGui.InputText("4 (tab skip)", buf, 32)
ImGui.SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab.")
ImGui.PopItemFlag()
ImGui.InputText("5", buf, 32)
ImGui.TreePop()
end
if ImGui.TreeNode("Focus from code") then
IMGUI_DEMO_MARKER("Inputs & Focus/Focus from code")
local focus_1 = ImGui.Button("Focus on 1"); ImGui.SameLine()
local focus_2 = ImGui.Button("Focus on 2"); ImGui.SameLine()
local focus_3 = ImGui.Button("Focus on 3")
local has_focus = 0
local buf = s.focus_buf
if focus_1 then ImGui.SetKeyboardFocusHere() end
ImGui.InputText("1", buf, 128)
if ImGui.IsItemActive() then has_focus = 1 end
if focus_2 then ImGui.SetKeyboardFocusHere() end
ImGui.InputText("2", buf, 128)
if ImGui.IsItemActive() then has_focus = 2 end
ImGui.PushItemFlag(1 --[[ImGuiItemFlags.NoTabStop]], true)
if focus_3 then ImGui.SetKeyboardFocusHere() end
ImGui.InputText("3 (tab skip)", buf, 128)
if ImGui.IsItemActive() then has_focus = 3 end
ImGui.SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab.")
ImGui.PopItemFlag()
if has_focus ~= 0 then
ImGui.Text("Item with focus: %d", has_focus)
else
ImGui.Text("Item with focus: <none>")
end
-- Use >= 0 parameter to SetKeyboardFocusHere() to focus an upcoming item
local f3 = s.f3
local focus_ahead = -1
if ImGui.Button("Focus on X") then focus_ahead = 0 end ImGui.SameLine()
if ImGui.Button("Focus on Y") then focus_ahead = 1 end ImGui.SameLine()
if ImGui.Button("Focus on Z") then focus_ahead = 2 end
if focus_ahead ~= -1 then ImGui.SetKeyboardFocusHere(focus_ahead) end
ImGui.SliderFloat3("Float3", f3, 0.0, 1.0)
ImGui.TextWrapped("NB: Cursor & selection are preserved when refocusing last used item in code.")
ImGui.TreePop()
end
if ImGui.TreeNode("Dragging") then
IMGUI_DEMO_MARKER("Inputs & Focus/Dragging")
ImGui.TextWrapped("You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget.")
for button = 0, 2 do
ImGui.Text("IsMouseDragging(%d):", button)
ImGui.Text(" w/ default threshold: %d,", ImGui.IsMouseDragging(button) and 1 or 0)
ImGui.Text(" w/ zero threshold: %d,", ImGui.IsMouseDragging(button, 0.0) and 1 or 0)
ImGui.Text(" w/ large threshold: %d,", ImGui.IsMouseDragging(button, 20.0) and 1 or 0)
end
ImGui.Button("Drag Me")
if ImGui.IsItemActive() then
ImGui.GetForegroundDrawList():AddLine(io.MouseClickedPos[0], io.MousePos, ImGui.GetColorU32(22 --[[ImGuiCol.Button]]), 4.0) -- Draw a line between the button and the mouse cursor
end
-- Drag operations gets "unlocked" when the mouse has moved past a certain threshold
-- (the default threshold is stored in io.MouseDragThreshold). You can request a lower or higher
-- threshold using the second parameter of IsMouseDragging() and GetMouseDragDelta().
local value_raw = ImGui.GetMouseDragDelta(0, 0.0)
local value_with_lock_threshold = ImGui.GetMouseDragDelta(0)
local mouse_delta = io.MouseDelta
ImGui.Text("GetMouseDragDelta(0):")
ImGui.Text(" w/ default threshold: (%.1f, %.1f)", value_with_lock_threshold.x, value_with_lock_threshold.y)
ImGui.Text(" w/ zero threshold: (%.1f, %.1f)", value_raw.x, value_raw.y)
ImGui.Text("io.MouseDelta: (%.1f, %.1f)", mouse_delta.x, mouse_delta.y)
ImGui.TreePop()
end
end
end
-------------------------------------------------------------------------------
-- [SECTION] About Window / ShowAboutWindow()
-- Access from Dear ImGui Demo -> Tools -> About
-------------------------------------------------------------------------------
S.ShowAboutWindow = {
show_config_info = false,
}
function ImGui.ShowAboutWindow(p_open)
local s = S.ShowAboutWindow
local _, visible
p_open, visible = ImGui.Begin("About Dear ImGui", p_open, 64 --[[ImGuiWindowFlags.AlwaysAutoResize]])
if not visible then
ImGui.End()
return p_open
end
IMGUI_DEMO_MARKER("Tools/About Dear ImGui")
ImGui.Text("Dear ImGui %s (%d)", IMGUI_VERSION, 19298 --[[IMGUI_VERSION_NUM]])
ImGui.TextLinkOpenURL("Homepage", "https://github.com/ocornut/imgui")
ImGui.SameLine()
ImGui.TextLinkOpenURL("FAQ", "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md")
ImGui.SameLine()
ImGui.TextLinkOpenURL("Wiki", "https://github.com/ocornut/imgui/wiki")
ImGui.SameLine()
ImGui.TextLinkOpenURL("Extensions", "https://github.com/ocornut/imgui/wiki/Useful-Extensions")
ImGui.SameLine()
ImGui.TextLinkOpenURL("Releases", "https://github.com/ocornut/imgui/releases")
ImGui.SameLine()
ImGui.TextLinkOpenURL("Funding", "https://github.com/ocornut/imgui/wiki/Funding")
ImGui.Separator()
ImGui.Text("(c) 2014-2026 Omar Cornut")
ImGui.Text("Developed by Omar Cornut and all Dear ImGui contributors.")
ImGui.Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information.")
ImGui.Text("If your company uses this, please consider funding the project.")
_, s.show_config_info = ImGui.Checkbox("Config/Build Information", s.show_config_info)
if s.show_config_info then
local io = ImGui.GetIO()
local style = ImGui.GetStyle()
local copy_to_clipboard = ImGui.Button("Copy to clipboard")
local child_size = ImVec2(0, ImGui.GetTextLineHeightWithSpacing() * 18)
ImGui.BeginChild(ImGui.GetID("cfg_infos"), child_size, 128 --[[ImGuiChildFlags.FrameStyle]])
if copy_to_clipboard then
ImGui.LogToClipboard()
ImGui.LogText("// (Copy from the next line. Keep the ``` markers for formatting.)\n")
ImGui.LogText("```cpp\n") -- Back quotes will make text appears without formatting when pasting on GitHub
end
ImGui.Text("Dear ImGui %s (%d)", IMGUI_VERSION, 19298 --[[IMGUI_VERSION_NUM]])
ImGui.Separator()
-- [Port] no sizeof()/__cplusplus/compiler/OS #defines in Lua: report the Lua runtime instead
ImGui.Text("runtime: %s", tostring(_VERSION))
ImGui.Text("define: IMGUI_HAS_VIEWPORT")
ImGui.Text("define: IMGUI_HAS_DOCK")
-- Heuristic to detect no-op IM_ASSERT() macros
-- - This is designed so people opening bug reports would convey and notice that they have disabled asserts for Dear ImGui code.
-- - 16 is > strlen("((void)(_EXPR))") which we suggested in our imconfig.h template as a possible way to disable.
local assert_runs_expression = 0
assert((function() assert_runs_expression = assert_runs_expression + 1; return assert_runs_expression end)()) -- [Port] no ++ in expressions
local assert_expand_len = 17 -- [Port] IM_ASSERT is a function, not a macro: nothing to stringify, it cannot be expanded to a no-op
local assert_maybe_disabled = (assert_runs_expression == 0 or assert_expand_len <= 16)
ImGui.Text("IM_ASSERT: runs expression: %s. expand size: %s%s",
(assert_runs_expression ~= 0) and "OK" or "KO", (assert_expand_len > 16) and "OK" or "KO", assert_maybe_disabled and " (MAYBE DISABLED?!)" or "")
if assert_maybe_disabled then
ImGui.SameLine()
HelpMarker("IM_ASSERT() calls assert() by default. Compiling with NDEBUG will usually strip out assert() to nothing, which is NOT recommended because we use asserts to notify of programmer mistakes!")
end
ImGui.Separator()
ImGui.Text("io.BackendPlatformName: %s", io.BackendPlatformName or "NULL")
ImGui.Text("io.BackendRendererName: %s", io.BackendRendererName or "NULL")
ImGui.Text("io.ConfigFlags: 0x%08X", io.ConfigFlags)
if bit32.band(io.ConfigFlags, 1 --[[ImGuiConfigFlags.NavEnableKeyboard]]) ~= 0 then ImGui.Text(" NavEnableKeyboard") end
if bit32.band(io.ConfigFlags, 2 --[[ImGuiConfigFlags.NavEnableGamepad]]) ~= 0 then ImGui.Text(" NavEnableGamepad") end
if bit32.band(io.ConfigFlags, 16 --[[ImGuiConfigFlags.NoMouse]]) ~= 0 then ImGui.Text(" NoMouse") end
if bit32.band(io.ConfigFlags, 32 --[[ImGuiConfigFlags.NoMouseCursorChange]]) ~= 0 then ImGui.Text(" NoMouseCursorChange") end
if bit32.band(io.ConfigFlags, 64 --[[ImGuiConfigFlags.NoKeyboard]]) ~= 0 then ImGui.Text(" NoKeyboard") end
if bit32.band(io.ConfigFlags, 128 --[[ImGuiConfigFlags.DockingEnable]]) ~= 0 then ImGui.Text(" DockingEnable") end
if bit32.band(io.ConfigFlags, 1024 --[[ImGuiConfigFlags.ViewportsEnable]]) ~= 0 then ImGui.Text(" ViewportsEnable") end
if io.MouseDrawCursor then ImGui.Text("io.MouseDrawCursor") end
if io.ConfigDpiScaleFonts then ImGui.Text("io.ConfigDpiScaleFonts") end
if io.ConfigDpiScaleViewports then ImGui.Text("io.ConfigDpiScaleViewports") end
if io.ConfigViewportsNoAutoMerge then ImGui.Text("io.ConfigViewportsNoAutoMerge") end
if io.ConfigViewportsNoTaskBarIcon then ImGui.Text("io.ConfigViewportsNoTaskBarIcon") end
if io.ConfigViewportsNoDecoration then ImGui.Text("io.ConfigViewportsNoDecoration") end
if io.ConfigViewportsNoDefaultParent then ImGui.Text("io.ConfigViewportsNoDefaultParent") end
if io.ConfigDockingNoSplit then ImGui.Text("io.ConfigDockingNoSplit") end
if io.ConfigDockingNoDockingOver then ImGui.Text("io.ConfigDockingNoDockingOver") end
if io.ConfigDockingWithShift then ImGui.Text("io.ConfigDockingWithShift") end
if io.ConfigDockingAlwaysTabBar then ImGui.Text("io.ConfigDockingAlwaysTabBar") end
if io.ConfigDockingTransparentPayload then ImGui.Text("io.ConfigDockingTransparentPayload") end
if io.ConfigMacOSXBehaviors then ImGui.Text("io.ConfigMacOSXBehaviors") end
if io.ConfigNavMoveSetMousePos then ImGui.Text("io.ConfigNavMoveSetMousePos") end
if io.ConfigNavCaptureKeyboard then ImGui.Text("io.ConfigNavCaptureKeyboard") end
if io.ConfigInputTextCursorBlink then ImGui.Text("io.ConfigInputTextCursorBlink") end
if io.ConfigWindowsResizeFromEdges then ImGui.Text("io.ConfigWindowsResizeFromEdges") end
if io.ConfigWindowsMoveFromTitleBarOnly then ImGui.Text("io.ConfigWindowsMoveFromTitleBarOnly") end
if io.ConfigMemoryCompactTimer >= 0.0 then ImGui.Text("io.ConfigMemoryCompactTimer = %.1f", io.ConfigMemoryCompactTimer) end
ImGui.Text("io.BackendFlags: 0x%08X", io.BackendFlags)
if bit32.band(io.BackendFlags, 1 --[[ImGuiBackendFlags.HasGamepad]]) ~= 0 then ImGui.Text(" HasGamepad") end
if bit32.band(io.BackendFlags, 2 --[[ImGuiBackendFlags.HasMouseCursors]]) ~= 0 then ImGui.Text(" HasMouseCursors") end
if bit32.band(io.BackendFlags, 4 --[[ImGuiBackendFlags.HasSetMousePos]]) ~= 0 then ImGui.Text(" HasSetMousePos") end
if bit32.band(io.BackendFlags, 2048 --[[ImGuiBackendFlags.PlatformHasViewports]]) ~= 0 then ImGui.Text(" PlatformHasViewports") end
if bit32.band(io.BackendFlags, 4096 --[[ImGuiBackendFlags.HasMouseHoveredViewport]]) ~= 0 then ImGui.Text(" HasMouseHoveredViewport") end
if bit32.band(io.BackendFlags, 8192 --[[ImGuiBackendFlags.HasParentViewport]]) ~= 0 then ImGui.Text(" HasParentViewport") end
if bit32.band(io.BackendFlags, 8 --[[ImGuiBackendFlags.RendererHasVtxOffset]]) ~= 0 then ImGui.Text(" RendererHasVtxOffset") end
if bit32.band(io.BackendFlags, 16 --[[ImGuiBackendFlags.RendererHasTextures]]) ~= 0 then ImGui.Text(" RendererHasTextures") end
if bit32.band(io.BackendFlags, 1024 --[[ImGuiBackendFlags.RendererHasViewports]]) ~= 0 then ImGui.Text(" RendererHasViewports") end
ImGui.Separator()
ImGui.Text("io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts.Fonts.Size, io.Fonts.Flags, io.Fonts.TexData.Width, io.Fonts.TexData.Height)
ImGui.Text("io.Fonts->FontLoaderName: %s", io.Fonts.FontLoaderName or "NULL")
ImGui.Text("io.DisplaySize: %.2f,%.2f", io.DisplaySize.x, io.DisplaySize.y)
ImGui.Text("io.DisplayFramebufferScale: %.2f,%.2f", io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y)
ImGui.Separator()
ImGui.Text("style.WindowPadding: %.2f,%.2f", style.WindowPadding.x, style.WindowPadding.y)
ImGui.Text("style.WindowBorderSize: %.2f", style.WindowBorderSize)
ImGui.Text("style.FramePadding: %.2f,%.2f", style.FramePadding.x, style.FramePadding.y)
ImGui.Text("style.FrameRounding: %.2f", style.FrameRounding)
ImGui.Text("style.FrameBorderSize: %.2f", style.FrameBorderSize)
ImGui.Text("style.ItemSpacing: %.2f,%.2f", style.ItemSpacing.x, style.ItemSpacing.y)
ImGui.Text("style.ItemInnerSpacing: %.2f,%.2f", style.ItemInnerSpacing.x, style.ItemInnerSpacing.y)
if copy_to_clipboard then
ImGui.LogText("\n```\n")
ImGui.LogFinish()
end
ImGui.EndChild()
end
ImGui.End()
return p_open
end
-------------------------------------------------------------------------------
-- [SECTION] Style Editor / ShowStyleEditor()
-------------------------------------------------------------------------------
-- - ShowStyleSelector()
-- - ShowStyleEditor()
-------------------------------------------------------------------------------
-- [Port] C++ copies ImGuiStyle by value (`dst = src`). Copy fields in place so ImVec2/ImVec4 objects of `dst` keep
-- their identity (the live style's vectors may be referenced elsewhere) and are never aliased with `src`.
function ImGuiStyle_Copy(dst, src)
for k, v in pairs(src) do
if k == "Colors" then
local dst_colors = dst.Colors
for i, col in pairs(v) do
if dst_colors[i] then ImVec4_Copy(dst_colors[i], col) else dst_colors[i] = ImVec4(col.x, col.y, col.z, col.w) end
end
elseif type(v) == "table" then -- ImVec2
if dst[k] then ImVec2_Copy(dst[k], v) else dst[k] = ImVec2(v.x, v.y) end
else
dst[k] = v
end
end
return dst
end
S.ShowStyleSelector = {
style_idx = -1,
}
-- Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options.
function ImGui.ShowStyleSelector(label)
-- FIXME: This is a bit tricky to get right as style are functions, they don't register a name nor the fact that one is active.
-- So we keep track of last active one among our limited selection.
local s = S.ShowStyleSelector
local style_names = { "Dark", "Light", "Classic" }
local ret = false
if ImGui.BeginCombo(label, (s.style_idx >= 0 and s.style_idx < #style_names) and style_names[s.style_idx + 1] or "") then
for n = 0, #style_names - 1 do
if ImGui.Selectable(style_names[n + 1], s.style_idx == n, 64 --[[ImGuiSelectableFlags.SelectOnNav]]) then
s.style_idx = n
ret = true
if s.style_idx == 0 then ImGui.StyleColorsDark()
elseif s.style_idx == 1 then ImGui.StyleColorsLight()
elseif s.style_idx == 2 then ImGui.StyleColorsClassic()
end
elseif s.style_idx == n then
ImGui.SetItemDefaultFocus()
end
end
ImGui.EndCombo()
end
return ret
end
function GetTreeLinesFlagsName(flags)
if flags == 262144 --[[ImGuiTreeNodeFlags.DrawLinesNone]] then return "DrawLinesNone" end
if flags == 524288 --[[ImGuiTreeNodeFlags.DrawLinesFull]] then return "DrawLinesFull" end
if flags == 1048576 --[[ImGuiTreeNodeFlags.DrawLinesToNodes]] then return "DrawLinesToNodes" end
return ""
end
S.ShowStyleEditor = {
ref_saved_style = nil, -- static ImGuiStyle ref_saved_style; (created lazily)
init = true,
output_dest = 0,
output_only_modified = true,
alpha_flags = 0,
filter = nil, -- static ImGuiTextFilter filter; (created lazily)
}
-- We omit the ImGui:: prefix in this function, as we don't expect user to be copy and pasting this code.
-- [Port] Lua has no `using namespace`: calls keep the ImGui. prefix
function ImGui.ShowStyleEditor(ref)
local s = S.ShowStyleEditor
local _, changed
IMGUI_DEMO_MARKER("Tools/Style Editor")
-- You can pass in a reference ImGuiStyle structure to compare to, revert to and save to
-- (without a reference style pointer, we will use one compared locally as a reference)
local style = ImGui.GetStyle()
if s.ref_saved_style == nil then s.ref_saved_style = ImGuiStyle() end
-- Default to using internal storage as reference
if s.init and ref == nil then
ImGuiStyle_Copy(s.ref_saved_style, style)
end
s.init = false
if ref == nil then
ref = s.ref_saved_style
end
-- The logic behind dynamically changing 'max_border_size' is to not encourage people to increase border size too much: it'll likely reveal lots of subtle rendering artifacts and this isn't a priority right now.
-- Note that _MainScale is currently internal PLEASE DO NOT USE IN YOUR CODE.
local default_border_size = math.floor(style._MainScale)
local max_border_size = ImMax(default_border_size, 2.0)
ImGui.PushItemWidth(ImGui.GetWindowWidth() * 0.50)
do
-- General
ImGui.SeparatorText("General")
if bit32.band(ImGui.GetIO().BackendFlags, 16 --[[ImGuiBackendFlags.RendererHasTextures]]) == 0 then
ImGui.BulletText("Warning: Font scaling will NOT be smooth, because\nImGuiBackendFlags_RendererHasTextures is not set!")
ImGui.BulletText("For instructions, see:")
ImGui.SameLine()
ImGui.TextLinkOpenURL("docs/BACKENDS.md", "https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md")
end
if ImGui.ShowStyleSelector("Colors##Selector") then
ImGuiStyle_Copy(s.ref_saved_style, style)
end
ImGui.ShowFontSelector("Fonts##Selector")
style.FontSizeBase, changed = ImGui.DragFloat("FontSizeBase", style.FontSizeBase, 0.20, 5.0, 100.0, "%.0f")
if changed then
style._NextFrameFontSizeBase = style.FontSizeBase -- FIXME: Temporary hack until we finish remaining work.
end
ImGui.SameLine(0.0, 0.0); ImGui.Text(" (out %.2f)", ImGui.GetFontSize())
style.FontScaleMain = ImGui.DragFloat("FontScaleMain", style.FontScaleMain, 0.02, 0.5, 4.0)
ImGui.BeginDisabled(ImGui.GetIO().ConfigDpiScaleFonts)
style.FontScaleDpi = ImGui.DragFloat("FontScaleDpi", style.FontScaleDpi, 0.02, 0.5, 4.0)
ImGui.SetItemTooltip("When io.ConfigDpiScaleFonts is set, this value is automatically overwritten.")
ImGui.EndDisabled()
-- Simplified Settings (expose floating-pointer border sizes as boolean representing 0.0f or 1.0f)
style.FrameRounding, changed = ImGui.SliderFloat("FrameRounding", style.FrameRounding, 0.0, 12.0, "%.0f")
if changed then
style.GrabRounding = style.FrameRounding -- Make GrabRounding always the same value as FrameRounding
end
do local pressed, border = ImGui.Checkbox("WindowBorder", style.WindowBorderSize > 0.0); if pressed then if border then style.WindowBorderSize = default_border_size else style.WindowBorderSize = 0.0 end end end
ImGui.SameLine()
do local pressed, border = ImGui.Checkbox("FrameBorder", style.FrameBorderSize > 0.0); if pressed then if border then style.FrameBorderSize = default_border_size else style.FrameBorderSize = 0.0 end end end
ImGui.SameLine()
do local pressed, border = ImGui.Checkbox("PopupBorder", style.PopupBorderSize > 0.0); if pressed then if border then style.PopupBorderSize = default_border_size else style.PopupBorderSize = 0.0 end end end
end
-- Save/Revert button
if ImGui.Button("Save Ref") then
ImGuiStyle_Copy(ref, ImGuiStyle_Copy(s.ref_saved_style, style))
end
ImGui.SameLine()
if ImGui.Button("Revert Ref") then
ImGuiStyle_Copy(style, ref)
end
ImGui.SameLine()
HelpMarker(
"Save/Revert in local non-persistent storage. Default Colors definition are not affected. " ..
"Use \"Export\" below to save them somewhere.")
ImGui.SeparatorText("Details")
if ImGui.BeginTabBar("##tabs", 0 --[[ImGuiTabBarFlags.None]]) then
if ImGui.BeginTabItem("Sizes") then
ImGui.SeparatorText("Main")
ImGui.SliderFloat2("WindowPadding", style.WindowPadding, 0.0, 20.0, "%.0f")
ImGui.SliderFloat2("FramePadding", style.FramePadding, 0.0, 20.0, "%.0f")
ImGui.SliderFloat2("ItemSpacing", style.ItemSpacing, 0.0, 20.0, "%.0f")
ImGui.SliderFloat2("ItemInnerSpacing", style.ItemInnerSpacing, 0.0, 20.0, "%.0f")
ImGui.SliderFloat2("TouchExtraPadding", style.TouchExtraPadding, 0.0, 10.0, "%.0f")
style.IndentSpacing = ImGui.SliderFloat("IndentSpacing", style.IndentSpacing, 0.0, 30.0, "%.0f")
style.GrabMinSize = ImGui.SliderFloat("GrabMinSize", style.GrabMinSize, 1.0, 20.0, "%.0f")
ImGui.SeparatorText("Borders")
style.WindowBorderSize = ImGui.SliderFloat("WindowBorderSize", style.WindowBorderSize, 0.0, max_border_size, "%.0f")
style.ChildBorderSize = ImGui.SliderFloat("ChildBorderSize", style.ChildBorderSize, 0.0, max_border_size, "%.0f")
style.PopupBorderSize = ImGui.SliderFloat("PopupBorderSize", style.PopupBorderSize, 0.0, max_border_size, "%.0f")
style.FrameBorderSize = ImGui.SliderFloat("FrameBorderSize", style.FrameBorderSize, 0.0, max_border_size, "%.0f")
ImGui.SeparatorText("Rounding")
style.WindowRounding = ImGui.SliderFloat("WindowRounding", style.WindowRounding, 0.0, 12.0, "%.0f")
style.ChildRounding = ImGui.SliderFloat("ChildRounding", style.ChildRounding, 0.0, 12.0, "%.0f")
style.FrameRounding = ImGui.SliderFloat("FrameRounding", style.FrameRounding, 0.0, 12.0, "%.0f")
style.PopupRounding = ImGui.SliderFloat("PopupRounding", style.PopupRounding, 0.0, 12.0, "%.0f")
style.GrabRounding = ImGui.SliderFloat("GrabRounding", style.GrabRounding, 0.0, 12.0, "%.0f")
style.MenuItemRounding = ImGui.SliderFloat("MenuItemRounding", style.MenuItemRounding, 0.0, 12.0, "%.0f")
-- NB: SelectableRounding is intentionally NOT made visible here. We don't want to encourage people using that.
ImGui.SeparatorText("Scrollbar")
style.ScrollbarSize = ImGui.SliderFloat("ScrollbarSize", style.ScrollbarSize, 1.0, 20.0, "%.0f")
style.ScrollbarRounding = ImGui.SliderFloat("ScrollbarRounding", style.ScrollbarRounding, 0.0, 12.0, "%.0f")
style.ScrollbarPadding = ImGui.SliderFloat("ScrollbarPadding", style.ScrollbarPadding, 0.0, 10.0, "%.0f")
ImGui.SeparatorText("Tabs")
style.TabBorderSize = ImGui.SliderFloat("TabBorderSize", style.TabBorderSize, 0.0, max_border_size, "%.0f")
style.TabBarBorderSize = ImGui.SliderFloat("TabBarBorderSize", style.TabBarBorderSize, 0.0, max_border_size, "%.0f")
style.TabBarOverlineSize = ImGui.SliderFloat("TabBarOverlineSize", style.TabBarOverlineSize, 0.0, ImMax(3.0, max_border_size), "%.0f")
ImGui.SameLine(); HelpMarker("Overline is only drawn over the selected tab when ImGuiTabBarFlags_DrawSelectedOverline is set.")
style.TabMinWidthBase = ImGui.DragFloat("TabMinWidthBase", style.TabMinWidthBase, 0.5, 1.0, 500.0, "%.0f")
style.TabMinWidthShrink = ImGui.DragFloat("TabMinWidthShrink", style.TabMinWidthShrink, 0.5, 1.0, 500.0, "%0.f")
style.TabCloseButtonMinWidthSelected = ImGui.DragFloat("TabCloseButtonMinWidthSelected", style.TabCloseButtonMinWidthSelected, 0.5, -1.0, 100.0, (style.TabCloseButtonMinWidthSelected < 0.0) and "%.0f (Always)" or "%.0f")
style.TabCloseButtonMinWidthUnselected = ImGui.DragFloat("TabCloseButtonMinWidthUnselected", style.TabCloseButtonMinWidthUnselected, 0.5, -1.0, 100.0, (style.TabCloseButtonMinWidthUnselected < 0.0) and "%.0f (Always)" or "%.0f")
style.TabRounding = ImGui.SliderFloat("TabRounding", style.TabRounding, 0.0, 12.0, "%.0f")
ImGui.SeparatorText("Tables")
ImGui.SliderFloat2("CellPadding", style.CellPadding, 0.0, 20.0, "%.0f")
style.TableAngledHeadersAngle = ImGui.SliderAngle("TableAngledHeadersAngle", style.TableAngledHeadersAngle, -50.0, 50.0)
ImGui.SliderFloat2("TableAngledHeadersTextAlign", style.TableAngledHeadersTextAlign, 0.0, 1.0, "%.2f")
ImGui.SeparatorText("Trees")
local combo_open = ImGui.BeginCombo("TreeLinesFlags", GetTreeLinesFlagsName(style.TreeLinesFlags))
ImGui.SameLine()
HelpMarker("[Experimental] Tree lines may not work in all situations (e.g. using a clipper) and may incurs slight traversal overhead.\n\nImGuiTreeNodeFlags_DrawLinesFull is faster than ImGuiTreeNodeFlags_DrawLinesToNode.")
if combo_open then
local options = { 262144 --[[ImGuiTreeNodeFlags.DrawLinesNone]], 524288 --[[ImGuiTreeNodeFlags.DrawLinesFull]], 1048576 --[[ImGuiTreeNodeFlags.DrawLinesToNodes]] }
for _, option in ipairs(options) do
if ImGui.Selectable(GetTreeLinesFlagsName(option), style.TreeLinesFlags == option) then
style.TreeLinesFlags = option
end
end
ImGui.EndCombo()
end
style.TreeLinesSize = ImGui.SliderFloat("TreeLinesSize", style.TreeLinesSize, 0.0, max_border_size, "%.0f")
style.TreeLinesRounding = ImGui.SliderFloat("TreeLinesRounding", style.TreeLinesRounding, 0.0, 12.0, "%.0f")
ImGui.SeparatorText("Windows")
ImGui.SliderFloat2("WindowTitleAlign", style.WindowTitleAlign, 0.0, 1.0, "%.2f")
style.WindowBorderHoverPadding = ImGui.SliderFloat("WindowBorderHoverPadding", style.WindowBorderHoverPadding, 1.0, 20.0, "%.0f")
local window_menu_button_position = style.WindowMenuButtonPosition + 1
window_menu_button_position, changed = ImGui.Combo("WindowMenuButtonPosition", window_menu_button_position, "None\0Left\0Right\0")
if changed then
style.WindowMenuButtonPosition = window_menu_button_position - 1
end
ImGui.SeparatorText("Widgets")
style.ColorMarkerSize = ImGui.SliderFloat("ColorMarkerSize", style.ColorMarkerSize, 0.0, 8.0, "%.0f")
style.ColorButtonPosition = ImGui.Combo("ColorButtonPosition", style.ColorButtonPosition, "Left\0Right\0")
ImGui.SliderFloat2("ButtonTextAlign", style.ButtonTextAlign, 0.0, 1.0, "%.2f")
ImGui.SameLine(); HelpMarker("Alignment applies when a button is larger than its text content.")
ImGui.SliderFloat2("SelectableTextAlign", style.SelectableTextAlign, 0.0, 1.0, "%.2f")
ImGui.SameLine(); HelpMarker("Alignment applies when a selectable is larger than its text content.")
style.SeparatorSize = ImGui.SliderFloat("SeparatorSize", style.SeparatorSize, 0.0, 10.0, "%.0f")
style.SeparatorTextBorderSize = ImGui.SliderFloat("SeparatorTextBorderSize", style.SeparatorTextBorderSize, 0.0, 10.0, "%.0f")
ImGui.SliderFloat2("SeparatorTextAlign", style.SeparatorTextAlign, 0.0, 1.0, "%.2f")
ImGui.SliderFloat2("SeparatorTextPadding", style.SeparatorTextPadding, 0.0, 40.0, "%.0f")
style.LogSliderDeadzone = ImGui.SliderFloat("LogSliderDeadzone", style.LogSliderDeadzone, 0.0, 12.0, "%.0f")
style.ImageRounding = ImGui.SliderFloat("ImageRounding", style.ImageRounding, 0.0, 12.0, "%.0f")
style.ImageBorderSize = ImGui.SliderFloat("ImageBorderSize", style.ImageBorderSize, 0.0, max_border_size, "%.0f")
ImGui.SeparatorText("Docking")
--SetCursorPosX(GetCursorPosX() + CalcItemWidth() - GetFrameHeight());
_, style.DockingNodeHasCloseButton = ImGui.Checkbox("DockingNodeHasCloseButton", style.DockingNodeHasCloseButton)
style.DockingSeparatorSize = ImGui.SliderFloat("DockingSeparatorSize", style.DockingSeparatorSize, 0.0, 12.0, "%.0f")
ImGui.SeparatorText("Tooltips")
for n = 0, 1 do
if ImGui.TreeNodeEx(n == 0 and "HoverFlagsForTooltipMouse" or "HoverFlagsForTooltipNav") then
local p = (n == 0) and "HoverFlagsForTooltipMouse" or "HoverFlagsForTooltipNav" -- [Port] pointer -> field name
_, style[p] = ImGui.CheckboxFlags("ImGuiHoveredFlags_DelayNone", style[p], 16384 --[[ImGuiHoveredFlags.DelayNone]])
_, style[p] = ImGui.CheckboxFlags("ImGuiHoveredFlags_DelayShort", style[p], 32768 --[[ImGuiHoveredFlags.DelayShort]])
_, style[p] = ImGui.CheckboxFlags("ImGuiHoveredFlags_DelayNormal", style[p], 65536 --[[ImGuiHoveredFlags.DelayNormal]])
_, style[p] = ImGui.CheckboxFlags("ImGuiHoveredFlags_Stationary", style[p], 8192 --[[ImGuiHoveredFlags.Stationary]])
_, style[p] = ImGui.CheckboxFlags("ImGuiHoveredFlags_NoSharedDelay", style[p], 131072 --[[ImGuiHoveredFlags.NoSharedDelay]])
ImGui.TreePop()
end
end
ImGui.SeparatorText("Misc")
ImGui.SliderFloat2("DisplayWindowPadding", style.DisplayWindowPadding, 0.0, 30.0, "%.0f"); ImGui.SameLine(); HelpMarker("Apply to regular windows: amount which we enforce to keep visible when moving near edges of your screen.")
ImGui.SliderFloat2("DisplaySafeAreaPadding", style.DisplaySafeAreaPadding, 0.0, 30.0, "%.0f"); ImGui.SameLine(); HelpMarker("Apply to every windows, menus, popups, tooltips: amount where we avoid displaying contents. Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured).")
ImGui.EndTabItem()
end
if ImGui.BeginTabItem("Colors") then
if ImGui.Button("Export") then
if s.output_dest == 0 then
ImGui.LogToClipboard()
else
ImGui.LogToTTY()
end
ImGui.LogText("ImVec4* colors = GetStyle().Colors;" .. "\n") -- [Port] IM_NEWLINE == "\n"
for i = 0, 63 --[[ImGuiCol.COUNT]] - 1 do
local col = style.Colors[i]
local name = ImGui.GetStyleColorName(i)
if not s.output_only_modified or col ~= ref.Colors[i] then -- [Port] memcmp -> ImVec4 __eq
ImGui.LogText("colors[ImGuiCol_%s]%s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);" .. "\n",
name, string.rep(" ", 23 - #name), col.x, col.y, col.z, col.w) -- [Port] "%*s" -> string.rep
end
end
ImGui.LogFinish()
end
ImGui.SameLine(); ImGui.SetNextItemWidth(ImGui.GetFontSize() * 10); s.output_dest = ImGui.Combo("##output_type", s.output_dest, "To Clipboard\0To TTY\0")
ImGui.SameLine(); _, s.output_only_modified = ImGui.Checkbox("Only Modified Colors", s.output_only_modified)
-- [Port] RadioButton(label, bool active) overload is RadioButtonEx() in the port
if ImGui.RadioButtonEx("Opaque", s.alpha_flags == 4096 --[[ImGuiColorEditFlags.AlphaOpaque]]) then s.alpha_flags = 4096 --[[ImGuiColorEditFlags.AlphaOpaque]] end ImGui.SameLine()
if ImGui.RadioButtonEx("Alpha", s.alpha_flags == 0 --[[ImGuiColorEditFlags.None]]) then s.alpha_flags = 0 --[[ImGuiColorEditFlags.None]] end ImGui.SameLine()
if ImGui.RadioButtonEx("Both", s.alpha_flags == 16384 --[[ImGuiColorEditFlags.AlphaPreviewHalf]]) then s.alpha_flags = 16384 --[[ImGuiColorEditFlags.AlphaPreviewHalf]] end ImGui.SameLine()
HelpMarker(
"In the color list:\n" ..
"Left-click on color square to open color picker,\n" ..
"Right-click to open edit options menu.")
if s.filter == nil then s.filter = ImGuiTextFilter() end
ImGui.SetNextItemWidth(-1.1754943508223e-38 --[[FLT_MIN]])
s.filter:DrawWithHint("##FilterColors", "Filter Colors (incl -excl)")
ImGui.SetNextWindowSizeConstraints(ImVec2(0.0, ImGui.GetTextLineHeightWithSpacing() * 10), ImVec2(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]]))
ImGui.BeginChild("##colors", ImVec2(0, 0), bit32.bor(1 --[[ImGuiChildFlags.Borders]], 256 --[[ImGuiChildFlags.NavFlattened]]), bit32.bor(16384 --[[ImGuiWindowFlags.AlwaysVerticalScrollbar]], 32768 --[[ImGuiWindowFlags.AlwaysHorizontalScrollbar]]))
ImGui.PushItemWidth(ImGui.GetFontSize() * -12)
for i = 0, 63 --[[ImGuiCol.COUNT]] - 1 do repeat
local name = ImGui.GetStyleColorName(i)
if not s.filter:PassFilter(name) then
break -- continue
end
ImGui.PushID(i)
if ImGui.Button("?") then
ImGui.DebugFlashStyleColor(i)
end
ImGui.SetItemTooltip("Flash given color to identify places where it is used.")
ImGui.SameLine()
ImGui.ColorEdit4("##color", style.Colors[i], bit32.bor(262144 --[[ImGuiColorEditFlags.AlphaBar]], s.alpha_flags))
if style.Colors[i] ~= ref.Colors[i] then -- [Port] memcmp -> ImVec4 __eq
-- Tips: in a real user application, you may want to merge and use an icon font into the main font,
-- so instead of "Save"/"Revert" you'd use icons!
-- Read the FAQ and docs/FONTS.md about using icon fonts. It's really easy and super convenient!
ImGui.SameLine(0.0, style.ItemInnerSpacing.x); if ImGui.Button("Save") then ImVec4_Copy(ref.Colors[i], style.Colors[i]) end
ImGui.SameLine(0.0, style.ItemInnerSpacing.x); if ImGui.Button("Revert") then ImVec4_Copy(style.Colors[i], ref.Colors[i]) end
end
ImGui.SameLine(0.0, style.ItemInnerSpacing.x)
ImGui.TextUnformatted(name)
ImGui.PopID()
until true end
ImGui.PopItemWidth()
ImGui.EndChild()
ImGui.EndTabItem()
end
if ImGui.BeginTabItem("Fonts") then
local io = ImGui.GetIO()
local atlas = io.Fonts
ImGui.ShowFontAtlas(atlas)
-- Post-baking font scaling. Note that this is NOT the nice way of scaling fonts, read below.
-- (we enforce hard clamping manually as by default DragFloat/SliderFloat allows Ctrl+Click text to get out of bounds).
--[[
SeparatorText("Legacy Scaling");
const float MIN_SCALE = 0.3f;
const float MAX_SCALE = 2.0f;
HelpMarker(
"Those are old settings provided for convenience.\n"
"However, the _correct_ way of scaling your UI is currently to reload your font at the designed size, "
"rebuild the font atlas, and call style.ScaleAllSizes() on a reference ImGuiStyle structure.\n"
"Using those settings here will give you poor quality results.");
PushItemWidth(GetFontSize() * 8);
DragFloat("global scale", &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp); // Scale everything
//static float window_scale = 1.0f;
//if (DragFloat("window scale", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp)) // Scale only this window
// SetWindowFontScale(window_scale);
PopItemWidth();
]]
ImGui.EndTabItem()
end
if ImGui.BeginTabItem("Rendering") then
_, style.AntiAliasedLines = ImGui.Checkbox("Anti-aliased lines", style.AntiAliasedLines)
ImGui.SameLine()
HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well.")
ImGui.BeginDisabled(style.AntiAliasedLines == false)
_, style.AntiAliasedLineEnds = ImGui.Checkbox("Anti-aliased line ends", style.AntiAliasedLineEnds)
ImGui.EndDisabled()
ImGui.BeginDisabled()
_, style.AntiAliasedLinesUseTex = ImGui.Checkbox("Anti-aliased lines use texture", style.AntiAliasedLinesUseTex)
ImGui.SameLine()
HelpMarker("Faster lines using texture data. Require backend to render with bilinear filtering (not point/nearest filtering).")
ImGui.EndDisabled()
_, style.AntiAliasedFill = ImGui.Checkbox("Anti-aliased fill", style.AntiAliasedFill)
local io = ImGui.GetIO()
_, io.ConfigDebugDrawListDefaultsToStrokeLegacy = ImGui.Checkbox("Debug: DrawList Defaults to _StrokeLegacy", io.ConfigDebugDrawListDefaultsToStrokeLegacy)
ImGui.PushItemWidth(ImGui.GetFontSize() * 8)
style.CurveTessellationMaxError = ImGui.DragFloat("Curve Tessellation Max Error", style.CurveTessellationMaxError, 0.02, 0.10, 10.0, "%.2f")
if style.CurveTessellationMaxError < 0.10 then
style.CurveTessellationMaxError = 0.10
end
-- When editing the "Circle Segment Max Error" value, draw a preview of its effect on auto-tessellated circles.
style.CircleTessellationMaxError = ImGui.DragFloat("Circle Tessellation Max Error", style.CircleTessellationMaxError, 0.005, 0.10, 5.0, "%.2f", 1536 --[[ImGuiSliderFlags.AlwaysClamp]])
local show_samples = ImGui.IsItemActive()
if show_samples then
ImGui.SetNextWindowPos(ImGui.GetCursorScreenPos())
end
if show_samples and ImGui.BeginTooltip() then
ImGui.TextUnformatted("(R = radius, N = approx number of segments)")
ImGui.Spacing()
local draw_list = ImGui.GetWindowDrawList()
local min_widget_width = ImGui.CalcTextSize("R: MMM\nN: MMM").x
for n = 0, 7 do
local RAD_MIN = 5.0
local RAD_MAX = 70.0
local rad = RAD_MIN + (RAD_MAX - RAD_MIN) * n / (8.0 - 1.0)
ImGui.BeginGroup()
-- N is not always exact here due to how PathArcTo() function work internally
ImGui.Text("R: %.f\nN: %d", rad, draw_list:_CalcCircleAutoSegmentCount(rad))
local canvas_width = ImMax(min_widget_width, rad * 2.0)
local offset_x = math.floor(canvas_width * 0.5)
local offset_y = math.floor(RAD_MAX)
local p1 = ImGui.GetCursorScreenPos()
draw_list:AddCircle(ImVec2(p1.x + offset_x, p1.y + offset_y), rad, ImGui.GetColorU32(0 --[[ImGuiCol.Text]]))
ImGui.Dummy(ImVec2(canvas_width, RAD_MAX * 2))
--[[
const ImVec2 p2 = GetCursorScreenPos();
draw_list->AddCircleFilled(ImVec2(p2.x + offset_x, p2.y + offset_y), rad, GetColorU32(ImGuiCol_Text));
Dummy(ImVec2(canvas_width, RAD_MAX * 2));
]]
ImGui.EndGroup()
ImGui.SameLine()
end
ImGui.EndTooltip()
end
ImGui.SameLine()
HelpMarker("When drawing circle primitives with \"num_segments == 0\" tessellation will be calculated automatically.")
style.Alpha = ImGui.DragFloat("Global Alpha", style.Alpha, 0.005, 0.20, 1.0, "%.2f") -- Not exposing zero here so user doesn't "lose" the UI (zero alpha clips all widgets). But application code could have a toggle to switch between zero and non-zero.
style.DisabledAlpha = ImGui.DragFloat("Disabled Alpha", style.DisabledAlpha, 0.005, 0.0, 1.0, "%.2f"); ImGui.SameLine(); HelpMarker("Additional alpha multiplier for disabled items (multiply over current value of Alpha).")
ImGui.PopItemWidth()
ImGui.EndTabItem()
end
ImGui.EndTabBar()
end
ImGui.PopItemWidth()
end
-------------------------------------------------------------------------------
-- [SECTION] User Guide / ShowUserGuide()
-------------------------------------------------------------------------------
-- We omit the ImGui:: prefix in this function, as we don't expect user to be copy and pasting this code.
function ImGui.ShowUserGuide()
local io = ImGui.GetIO()
ImGui.BulletText("Double-click on title bar to collapse window.")
ImGui.BulletText(
"Click and drag on lower corner or border to resize window.\n" ..
"(double-click to auto fit window to its contents)")
ImGui.BulletText("Ctrl+Click on a slider or drag box to input value as text.")
ImGui.BulletText("Tab/Shift+Tab to cycle through keyboard editable fields.")
ImGui.BulletText("Ctrl+Tab/Ctrl+Shift+Tab to focus windows.")
if io.FontAllowUserScaling then
ImGui.BulletText("Ctrl+Mouse Wheel to zoom window contents.")
end
ImGui.BulletText("While inputting text:\n")
ImGui.Indent()
ImGui.BulletText("Ctrl+Left/Right to word jump.")
ImGui.BulletText("Ctrl+A or double-click to select all.")
ImGui.BulletText("Ctrl+X/C/V to use clipboard cut/copy/paste.")
ImGui.BulletText("Ctrl+Z to undo, Ctrl+Y/Ctrl+Shift+Z to redo.")
ImGui.BulletText("Escape to revert.")
ImGui.Unindent()
ImGui.BulletText("With Keyboard controls enabled:")
ImGui.Indent()
ImGui.BulletText("Arrow keys or Home/End/PageUp/PageDown to navigate.")
ImGui.BulletText("Space to activate a widget.")
ImGui.BulletText("Return to input text into a widget.")
ImGui.BulletText("Escape to deactivate a widget, close popup,\nexit a child window or the menu layer, clear focus.")
ImGui.BulletText("Alt to jump to the menu layer of a window.")
ImGui.BulletText("Menu or Shift+F10 to open a context menu.")
ImGui.Unindent()
ImGui.BulletText("With Gamepad controls enabled:")
ImGui.Indent()
ImGui.BulletText("D-Pad: Navigate / Tweak / Resize (in Windowing mode).")
ImGui.BulletText("%s Face button: Activate / Open / Toggle. Hold: activate with text input.", io.ConfigNavSwapGamepadButtons and "East" or "South")
ImGui.BulletText("%s Face button: Cancel / Close / Exit.", io.ConfigNavSwapGamepadButtons and "South" or "East")
ImGui.BulletText("West Face button: Toggle Menu. Hold for Windowing mode (Focus/Move/Resize windows).")
ImGui.BulletText("North Face button: Open Context Menu.")
ImGui.BulletText("L1/R1: Tweak Slower/Faster, Focus Previous/Next (in Windowing Mode).")
ImGui.Unindent()
end
-------------------------------------------------------------------------------
-- [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()
-------------------------------------------------------------------------------
-- - ShowExampleAppMainMenuBar()
-- - ShowExampleMenuFile()
-------------------------------------------------------------------------------
-- Demonstrate creating a "main" fullscreen menu bar and populating it.
-- Note the difference between BeginMainMenuBar() and BeginMenuBar():
-- - BeginMenuBar() = menu-bar inside current window (which needs the ImGuiWindowFlags.MenuBar flag!)
-- - BeginMainMenuBar() = helper to create menu-bar-sized window at the top of the main viewport + call BeginMenuBar() into it.
function ShowExampleAppMainMenuBar()
if ImGui.BeginMainMenuBar() then
if ImGui.BeginMenu("File") then
IMGUI_DEMO_MARKER("Menu/File")
ShowExampleMenuFile()
ImGui.EndMenu()
end
if ImGui.BeginMenu("Edit") then
IMGUI_DEMO_MARKER("Menu/Edit")
if ImGui.MenuItem("Undo", "Ctrl+Z") then end
if ImGui.MenuItem("Redo", "Ctrl+Y", false, false) then end -- Disabled item
ImGui.Separator()
if ImGui.MenuItem("Cut", "Ctrl+X") then end
if ImGui.MenuItem("Copy", "Ctrl+C") then end
if ImGui.MenuItem("Paste", "Ctrl+V") then end
ImGui.EndMenu()
end
ImGui.EndMainMenuBar()
end
end
-- Note that shortcuts are currently provided for display only
-- (future version will add explicit flags to BeginMenu() to request processing shortcuts)
S.ShowExampleMenuFile = {
enabled = true,
f = 0.5,
n = 0,
b = true,
}
function ShowExampleMenuFile()
local s = S.ShowExampleMenuFile
IMGUI_DEMO_MARKER("Examples/Menu")
ImGui.MenuItem("(demo menu)", nil, false, false)
if ImGui.MenuItem("New") then end
if ImGui.MenuItem("Open", "Ctrl+O") then end
if ImGui.BeginMenu("Open Recent") then
ImGui.MenuItem("fish_hat.c")
ImGui.MenuItem("fish_hat.inl")
ImGui.MenuItem("fish_hat.h")
if ImGui.BeginMenu("More..") then
ImGui.MenuItem("Hello")
ImGui.MenuItem("Sailor")
if ImGui.BeginMenu("Recurse..") then
ShowExampleMenuFile()
ImGui.EndMenu()
end
ImGui.EndMenu()
end
ImGui.EndMenu()
end
if ImGui.MenuItem("Save", "Ctrl+S") then end
if ImGui.MenuItem("Save As..") then end
ImGui.Separator()
if ImGui.BeginMenu("Options") then
IMGUI_DEMO_MARKER("Examples/Menu/Options")
_, s.enabled = ImGui.MenuItem("Enabled", "", s.enabled)
ImGui.BeginChild("child", ImVec2(0, ImGui.GetTextLineHeightWithSpacing() * 5.0), 1 --[[ImGuiChildFlags.Borders]])
for i = 0, 10 - 1 do
ImGui.Text("Scrolling Text %d", i)
end
ImGui.EndChild()
s.f = ImGui.SliderFloat("Value", s.f, 0.0, 1.0)
s.f = ImGui.InputFloat("Input", s.f, 0.1)
s.n = ImGui.Combo("Combo", s.n, "Yes\0No\0Maybe\0\0")
ImGui.EndMenu()
end
if ImGui.BeginMenu("Colors") then
IMGUI_DEMO_MARKER("Examples/Menu/Colors")
local sz = ImGui.GetTextLineHeight()
for i = 0, 63 --[[ImGuiCol.COUNT]] - 1 do
local name = ImGui.GetStyleColorName(i)
local p = ImGui.GetCursorScreenPos()
ImGui.GetWindowDrawList():AddRectFilled(p, ImVec2(p.x + sz, p.y + sz), ImGui.GetColorU32(i))
ImGui.Dummy(ImVec2(sz, sz))
ImGui.SameLine()
ImGui.MenuItem(name)
end
ImGui.EndMenu()
end
-- Here we demonstrate appending again to the "Options" menu (which we already created above)
-- Of course in this demo it is a little bit silly that this function calls BeginMenu("Options") twice.
-- In a real code-base using it would make senses to use this feature from very different code locations.
if ImGui.BeginMenu("Options") then -- <-- Append!
IMGUI_DEMO_MARKER("Examples/Menu/Append to an existing menu")
_, s.b = ImGui.Checkbox("SomeOption", s.b)
ImGui.EndMenu()
end
if ImGui.BeginMenu("Disabled", false) then -- Disabled
assert(false)
end
if ImGui.MenuItem("Checked", nil, true) then end
ImGui.Separator()
if ImGui.MenuItem("Quit", "Alt+F4") then end
end
-------------------------------------------------------------------------------
-- [SECTION] Example App: Debug Console / ShowExampleAppConsole()
-------------------------------------------------------------------------------
-- Demonstrate creating a simple console window, with scrolling, filtering, completion and history.
-- For the console example, we are using a more C++ like approach of declaring a class to hold both data and functions.
ExampleAppConsoleMT = {}
ExampleAppConsoleMT.__index = ExampleAppConsoleMT
function ExampleAppConsole()
local self = setmetatable({}, ExampleAppConsoleMT)
self.InputBuf = nil -- char InputBuf[256]
self.Items = ImVector() -- ImVector<char*>
self.Commands = ImVector() -- ImVector<const char*>
self.History = ImVector() -- ImVector<char*>
self.HistoryPos = 0 -- -1: new line, 0..History.Size-1 browsing history.
self.Filter = ImGuiTextFilter()
self.AutoScroll = false
self.ScrollToBottom = false
self:ClearLog()
self.InputBuf = ImGui.NewTextBuffer("", 256) -- memset(InputBuf, 0, sizeof(InputBuf))
self.HistoryPos = -1
-- "CLASSIFY" is here to provide the test case where "C"+[tab] completes to "CL" and display multiple matches.
self.Commands:push_back("HELP")
self.Commands:push_back("HISTORY")
self.Commands:push_back("CLEAR")
self.Commands:push_back("CLASSIFY")
self.AutoScroll = true
self.ScrollToBottom = false
self:AddLog("Welcome to Dear ImGui!")
return self
end
-- [Port] ~ExampleAppConsole(): nothing to free (GC)
-- Portable helpers
do
local function toupper(c) if c >= 97 and c <= 122 then return c - 32 end return c end -- [Port] C toupper() on bytes
function ExampleAppConsoleMT.Stricmp(s1, s2) local i = 1; local d; while true do local c1 = string.byte(s1, i) or 0; d = toupper(string.byte(s2, i) or 0) - toupper(c1); if d ~= 0 or c1 == 0 then break end; i = i + 1 end; return d end
function ExampleAppConsoleMT.Strnicmp(s1, s2, n) local i = 1; local d = 0; while n > 0 do local c1 = string.byte(s1, i) or 0; d = toupper(string.byte(s2, i) or 0) - toupper(c1); if d ~= 0 or c1 == 0 then break end; i = i + 1; n = n - 1 end; return d end
function ExampleAppConsoleMT.Strdup(s) assert(s); return s end -- [Port] Lua strings are immutable values
function ExampleAppConsoleMT.Strtrimblanks(s) local str_end = 0; while s[str_end + 1] ~= nil and s[str_end + 1] ~= 0 do str_end = str_end + 1 end; while str_end > 0 and s[str_end] == 32 do str_end = str_end - 1 end; s[str_end + 1] = 0 end
ExampleAppConsoleMT.toupper = toupper -- [Port] used by TextEditCallback()
end
function ExampleAppConsoleMT:ClearLog()
-- [Port] ImGui::MemFree(Items[i]) not needed (GC)
self.Items:clear()
end
function ExampleAppConsoleMT:AddLog(fmt, ...)
-- FIXME-OPT
local buf = string.format(fmt, ...)
buf = string.sub(buf, 1, 1024 - 1) -- [Port] vsnprintf() into char buf[1024]
self.Items:push_back(ExampleAppConsoleMT.Strdup(buf))
end
function ExampleAppConsoleMT:Draw(title, p_open)
ImGui.SetNextWindowSize(ImVec2(520, 600), 4 --[[ImGuiCond.FirstUseEver]])
local visible
p_open, visible = ImGui.Begin(title, p_open)
if not visible then
ImGui.End()
return p_open
end
IMGUI_DEMO_MARKER("Examples/Console")
-- As a specific feature guaranteed by the library, after calling Begin() the last Item represent the title bar.
-- So e.g. IsItemHovered() will return true when hovering the title bar.
-- Here we create a context menu only available from the title bar.
if ImGui.BeginPopupContextItem() then
if ImGui.MenuItem("Close Console") then
p_open = false
end
ImGui.EndPopup()
end
ImGui.TextWrapped(
"This example implements a console with basic coloring, completion (TAB key) and history (Up/Down keys). A more elaborate " ..
"implementation may want to store entries along with extra data such as timestamp, emitter, etc.")
ImGui.TextWrapped("Enter 'HELP' for help.")
-- TODO: display items starting from the bottom
if ImGui.SmallButton("Add Debug Text") then self:AddLog("%d some text", self.Items.Size); self:AddLog("some more text"); self:AddLog("display very important message here!") end
ImGui.SameLine()
if ImGui.SmallButton("Add Debug Error") then self:AddLog("[error] something went wrong") end
ImGui.SameLine()
if ImGui.SmallButton("Clear") then self:ClearLog() end
ImGui.SameLine()
local copy_to_clipboard = ImGui.SmallButton("Copy")
--static float t = 0.0f; if (ImGui.GetTime() - t > 0.02f) { t = ImGui.GetTime(); AddLog("Spam %f", t); }
ImGui.Separator()
-- Options menu
if ImGui.BeginPopup("Options") then
_, self.AutoScroll = ImGui.Checkbox("Auto-scroll", self.AutoScroll)
ImGui.EndPopup()
end
-- Options, Filter
ImGui.SetNextItemShortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 560 --[[ImGuiKey.O]]), 262144 --[[ImGuiInputFlags.Tooltip]])
if ImGui.Button("Options") then
ImGui.OpenPopup("Options")
end
ImGui.SameLine()
ImGui.SetNextItemShortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 551 --[[ImGuiKey.F]]), 262144 --[[ImGuiInputFlags.Tooltip]])
ImGui.SetNextItemWidth(-1.1754943508223e-38 --[[FLT_MIN]])
self.Filter:DrawWithHint("##Filter", "Filter (incl -excl)")
ImGui.Separator()
-- Reserve enough left-over height for 1 separator + 1 input text
local style = ImGui.GetStyle()
local footer_height_to_reserve = style.SeparatorSize + style.ItemSpacing.y + ImGui.GetFrameHeightWithSpacing()
if ImGui.BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), 256 --[[ImGuiChildFlags.NavFlattened]], 2048 --[[ImGuiWindowFlags.HorizontalScrollbar]]) then
if ImGui.BeginPopupContextWindow() then
if ImGui.Selectable("Clear") then self:ClearLog() end
ImGui.EndPopup()
end
-- Display every line as a separate entry so we can change their color or add custom widgets.
-- If you only want raw text you can use ImGui.TextUnformatted(log.begin(), log.end());
-- NB- if you have thousands of entries this approach may be too inefficient and may require user-side clipping
-- to only process visible items. The clipper will automatically measure the height of your first item and then
-- "seek" to display only items in the visible area.
-- To use the clipper we can replace your standard loop:
-- for (int i = 0; i < Items.Size; i++)
-- With:
-- ImGuiListClipper clipper;
-- clipper.Begin(Items.Size);
-- while (clipper.Step())
-- for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
-- - That your items are evenly spaced (same height)
-- - That you have cheap random access to your elements (you can access them given their index,
-- without processing all the ones before)
-- You cannot this code as-is if a filter is active because it breaks the 'cheap random-access' property.
-- We would need random-access on the post-filtered list.
-- A typical application wanting coarse clipping and filtering may want to pre-compute an array of indices
-- or offsets of items that passed the filtering test, recomputing this array when user changes the filter,
-- and appending newly elements as they are inserted. This is left as a task to the user until we can manage
-- to improve this example code!
-- If your items are of variable height:
-- - Split them into same height items would be simpler and facilitate random-seeking into your list.
-- - Consider using manual call to IsRectVisible() and skipping extraneous decoration from your items.
ImGui.PushStyleVar(14 --[[ImGuiStyleVar.ItemSpacing]], ImVec2(4, 1)) -- Tighten spacing
if copy_to_clipboard then
ImGui.LogToClipboard()
end
for _, item in self.Items:iter() do repeat
if not self.Filter:PassFilter(item) then
break -- continue
end
-- Normally you would store more information in your item than just a string.
-- (e.g. make Items[] an array of structure, store color/type etc.)
local color
local has_color = false
if string.find(item, "[error]", 1, true) then color = ImVec4(1.0, 0.4, 0.4, 1.0); has_color = true
elseif string.sub(item, 1, 2) == "# " then color = ImVec4(1.0, 0.8, 0.6, 1.0); has_color = true end
if has_color then
ImGui.PushStyleColor(0 --[[ImGuiCol.Text]], color)
end
ImGui.TextUnformatted(item)
if has_color then
ImGui.PopStyleColor()
end
until true end
if copy_to_clipboard then
ImGui.LogFinish()
end
-- Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.
-- Using a scrollbar or mouse-wheel will take away from the bottom edge.
if self.ScrollToBottom or (self.AutoScroll and ImGui.GetScrollY() >= ImGui.GetScrollMaxY()) then
ImGui.SetScrollHereY(1.0)
end
self.ScrollToBottom = false
ImGui.PopStyleVar()
end
ImGui.EndChild()
ImGui.Separator()
-- Command-line
local reclaim_focus = false
local input_text_flags = bit32.bor(64 --[[ImGuiInputTextFlags.EnterReturnsTrue]], 128 --[[ImGuiInputTextFlags.EscapeClearsAll]], 262144 --[[ImGuiInputTextFlags.CallbackCompletion]], 524288 --[[ImGuiInputTextFlags.CallbackHistory]])
if ImGui.InputText("Input", self.InputBuf, 256, input_text_flags, ExampleAppConsoleMT.TextEditCallbackStub, self) then
local s = self.InputBuf
ExampleAppConsoleMT.Strtrimblanks(s)
if s[1] ~= 0 then
self:ExecCommand(ImGui.TextBufferToString(s))
end
s[1] = 0
reclaim_focus = true
end
-- Auto-focus on window apparition
ImGui.SetItemDefaultFocus()
if reclaim_focus then
ImGui.SetKeyboardFocusHere(-1) -- Auto focus previous widget
end
ImGui.End()
return p_open
end
function ExampleAppConsoleMT:ExecCommand(command_line)
local Stricmp = ExampleAppConsoleMT.Stricmp
self:AddLog("# %s\n", command_line)
-- Insert into history. First find match and delete it so it can be pushed to the back.
-- This isn't trying to be smart or optimal.
self.HistoryPos = -1
for i = self.History.Size - 1, 0, -1 do
if Stricmp(self.History.Data[i + 1], command_line) == 0 then
self.History:erase(i + 1)
break
end
end
self.History:push_back(ExampleAppConsoleMT.Strdup(command_line))
-- Process command
if Stricmp(command_line, "CLEAR") == 0 then
self:ClearLog()
elseif Stricmp(command_line, "HELP") == 0 then
self:AddLog("Commands:")
for i = 0, self.Commands.Size - 1 do
self:AddLog("- %s", self.Commands.Data[i + 1])
end
elseif Stricmp(command_line, "HISTORY") == 0 then
local first = self.History.Size - 10
for i = (first > 0) and first or 0, self.History.Size - 1 do
self:AddLog("%3d: %s\n", i, self.History.Data[i + 1])
end
else
self:AddLog("Unknown command: '%s'\n", command_line)
end
-- On command input, we scroll to bottom even if AutoScroll==false
self.ScrollToBottom = true
end
-- In C++11 you'd be better off using lambdas for this sort of forwarding callbacks
function ExampleAppConsoleMT.TextEditCallbackStub(data)
local console = data.UserData
return console:TextEditCallback(data)
end
-- [Port] data.Buf is a 1-based byte table; positions (CursorPos, word_start, word_end) are 0-based offsets like upstream.
function ExampleAppConsoleMT:TextEditCallback(data)
local toupper = ExampleAppConsoleMT.toupper
--AddLog("cursor: %d, selection: %d-%d", data.CursorPos, data.SelectionStart, data.SelectionEnd)
if data.EventFlag == 262144 --[[ImGuiInputTextFlags.CallbackCompletion]] then
-- Example of TEXT COMPLETION
-- Locate beginning of current word
local word_end = data.CursorPos
local word_start = word_end
while word_start > 0 do
local c = data.Buf[word_start] -- word_start[-1]
if c == 32 or c == 9 or c == 44 or c == 59 then -- ' ', '\t', ',', ';'
break
end
word_start = word_start - 1
end
local word = string.char(table.unpack(data.Buf, word_start + 1, word_end)) -- [Port] word_start..word_end as a string
-- Build a list of candidates
local candidates = ImVector()
for i = 0, self.Commands.Size - 1 do
if ExampleAppConsoleMT.Strnicmp(self.Commands.Data[i + 1], word, word_end - word_start) == 0 then
candidates:push_back(self.Commands.Data[i + 1])
end
end
if candidates.Size == 0 then
-- No match
self:AddLog("No match for \"%s\"!\n", word) -- [Port] "%.*s" -> "%s"
elseif candidates.Size == 1 then
-- Single match. Delete the beginning of the word and replace it entirely so we've got nice casing.
data:DeleteChars(word_start, word_end - word_start)
data:InsertChars(data.CursorPos, candidates.Data[1])
data:InsertChars(data.CursorPos, " ")
else
-- Multiple matches. Complete as much as we can..
-- So inputting "C"+Tab will complete to "CL" then display "CLEAR" and "CLASSIFY" as matches.
local match_len = word_end - word_start
while true do
local c = 0
local all_candidates_matches = true
local i = 0
while i < candidates.Size and all_candidates_matches do
if i == 0 then
c = toupper(string.byte(candidates.Data[i + 1], match_len + 1) or 0)
elseif c == 0 or c ~= toupper(string.byte(candidates.Data[i + 1], match_len + 1) or 0) then
all_candidates_matches = false
end
i = i + 1
end
if not all_candidates_matches then
break
end
match_len = match_len + 1
end
if match_len > 0 then
data:DeleteChars(word_start, word_end - word_start)
data:InsertChars(data.CursorPos, string.sub(candidates.Data[1], 1, match_len)) -- [Port] InsertChars() has no text_end parameter
end
-- List matches
self:AddLog("Possible matches:\n")
for i = 0, candidates.Size - 1 do
self:AddLog("- %s\n", candidates.Data[i + 1])
end
end
elseif data.EventFlag == 524288 --[[ImGuiInputTextFlags.CallbackHistory]] then
-- Example of HISTORY
local prev_history_pos = self.HistoryPos
if data.EventKey == 515 --[[ImGuiKey.UpArrow]] then
if self.HistoryPos == -1 then
self.HistoryPos = self.History.Size - 1
elseif self.HistoryPos > 0 then
self.HistoryPos = self.HistoryPos - 1
end
elseif data.EventKey == 516 --[[ImGuiKey.DownArrow]] then
if self.HistoryPos ~= -1 then
self.HistoryPos = self.HistoryPos + 1
if self.HistoryPos >= self.History.Size then
self.HistoryPos = -1
end
end
end
-- A better implementation would preserve the data on the current input line along with cursor position.
if prev_history_pos ~= self.HistoryPos then
local history_str = ""
if self.HistoryPos >= 0 then history_str = self.History.Data[self.HistoryPos + 1] end
data:DeleteChars(0, data.BufTextLen)
data:InsertChars(0, history_str)
end
end
return 0
end
S.ShowExampleAppConsole = {
console = nil, -- ExampleAppConsole, created on first use
}
function ShowExampleAppConsole(p_open)
local s = S.ShowExampleAppConsole
if s.console == nil then s.console = ExampleAppConsole() end
return s.console:Draw("Example: Console", p_open)
end
-------------------------------------------------------------------------------
-- [SECTION] Example App: Image Viewer / ShowExampleAppImageViewer()
-------------------------------------------------------------------------------
S.ShowExampleAppImageViewer = {
image_viewer = nil, -- ExampleImageViewerData, created on first use
}
function ShowExampleAppImageViewer(p_open)
local s = S.ShowExampleAppImageViewer
local atlas = ImGui.GetIO().Fonts
local tex_ref = atlas.TexRef -- We don't have access to other textures in this demo!
local tex_w = atlas.TexData.Width
local tex_h = atlas.TexData.Height
local visible
p_open, visible = ImGui.Begin("Example: Image Viewer", p_open)
if visible then
if s.image_viewer == nil then s.image_viewer = ExampleImageViewerData() end
ExampleImageViewer_DrawOptions(s.image_viewer)
local canvas_size = ImGui.GetContentRegionAvail()
local canvas_min_size
if ImGui.IsWindowAppearing() then canvas_min_size = ImVec2(3.0 * tex_w, 4.0 * tex_h) else canvas_min_size = ImVec2(1.0, 1.0) end
canvas_size = ImVec2(ImMax(canvas_size.x, canvas_min_size.x), ImMax(canvas_size.y, canvas_min_size.y))
ExampleImageViewer_DrawCanvas(s.image_viewer, canvas_size, tex_ref, tex_w, tex_h)
end
ImGui.End()
return p_open
end
-------------------------------------------------------------------------------
-- [SECTION] Example App: Debug Log / ShowExampleAppLog()
-------------------------------------------------------------------------------
-- Usage:
-- static ExampleAppLog my_log;
-- my_log.AddLog("Hello %d world\n", 123);
-- my_log.Draw("title");
ExampleAppLogMT = {}
ExampleAppLogMT.__index = ExampleAppLogMT
function ExampleAppLog()
local self = setmetatable({}, ExampleAppLogMT)
self.Buf = ImGuiTextBuffer()
self.Filter = ImGuiTextFilter()
self.LineOffsets = ImVector() -- Index to lines offset. We maintain this with AddLog() calls.
self.AutoScroll = false -- Keep scrolling if already at the bottom.
self.AutoScroll = true
self:Clear()
return self
end
function ExampleAppLogMT:Clear()
self.Buf:clear()
self.LineOffsets:clear()
self.LineOffsets:push_back(0)
end
function ExampleAppLogMT:AddLog(fmt, ...)
local old_size = self.Buf:size()
self.Buf:appendf(fmt, ...)
local buf = self.Buf:c_str() -- [Port] Buf[] byte access
local new_size = #buf
while old_size < new_size do
if string.byte(buf, old_size + 1) == 10 then -- '\n'
self.LineOffsets:push_back(old_size + 1)
end
old_size = old_size + 1
end
end
-- [Port] line offsets are 0-based like upstream; a line is string.sub(buf, line_start + 1, line_end) (TextUnformatted()/PassFilter() have no text_begin)
function ExampleAppLogMT:Draw(title, p_open)
local visible
p_open, visible = ImGui.Begin(title, p_open)
if not visible then
ImGui.End()
return p_open
end
-- Options menu
if ImGui.BeginPopup("Options") then
_, self.AutoScroll = ImGui.Checkbox("Auto-scroll", self.AutoScroll)
ImGui.EndPopup()
end
-- Main window
if ImGui.Button("Options") then
ImGui.OpenPopup("Options")
end
ImGui.SameLine()
local clear = ImGui.Button("Clear")
ImGui.SameLine()
local copy = ImGui.Button("Copy")
ImGui.SameLine()
ImGui.SetNextItemWidth(-1.1754943508223e-38 --[[FLT_MIN]])
self.Filter:DrawWithHint("##Filter", "Filter (incl -excl)")
ImGui.Separator()
if ImGui.BeginChild("scrolling", ImVec2(0, 0), 0 --[[ImGuiChildFlags.None]], 2048 --[[ImGuiWindowFlags.HorizontalScrollbar]]) then
if clear then
self:Clear()
end
if copy then
ImGui.LogToClipboard()
end
ImGui.PushStyleVar(14 --[[ImGuiStyleVar.ItemSpacing]], ImVec2(0, 0))
local buf = self.Buf:c_str()
local buf_end = #buf
local LineOffsets = self.LineOffsets
if self.Filter:IsActive() then
-- In this example we don't use the clipper when Filter is enabled.
-- This is because we don't have random access to the result of our filter.
-- A real application processing logs with ten of thousands of entries may want to store the result of
-- search/filter.. especially if the filtering function is not trivial (e.g. reg-exp).
for line_no = 0, LineOffsets.Size - 1 do
local line_start = LineOffsets.Data[line_no + 1]
local line_end
if line_no + 1 < LineOffsets.Size then line_end = LineOffsets.Data[line_no + 2] - 1 else line_end = buf_end end
local line = string.sub(buf, line_start + 1, line_end)
if self.Filter:PassFilter(line) then
ImGui.TextUnformatted(line)
end
end
else
-- The simplest and easy way to display the entire buffer:
-- ImGui.TextUnformatted(buf_begin, buf_end);
-- And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward
-- to skip non-visible lines. Here we instead demonstrate using the clipper to only process lines that are
-- within the visible area.
-- If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them
-- on your side is recommended. Using ImGuiListClipper requires
-- - A) random access into your data
-- - B) items all being the same height,
-- both of which we can handle since we have an array pointing to the beginning of each line of text.
-- When using the filter (in the block of code above) we don't have random access into the data to display
-- anymore, which is why we don't use the clipper. Storing or skimming through the search result would make
-- it possible (and would be recommended if you want to search through tens of thousands of entries).
local clipper = ImGuiListClipper()
clipper:Begin(LineOffsets.Size)
while clipper:Step() do
for line_no = clipper.DisplayStart, clipper.DisplayEnd - 1 do
local line_start = LineOffsets.Data[line_no + 1]
local line_end
if line_no + 1 < LineOffsets.Size then line_end = LineOffsets.Data[line_no + 2] - 1 else line_end = buf_end end
ImGui.TextUnformatted(string.sub(buf, line_start + 1, line_end))
end
end
clipper:End()
end
ImGui.PopStyleVar()
-- Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.
-- Using a scrollbar or mouse-wheel will take away from the bottom edge.
if self.AutoScroll and ImGui.GetScrollY() >= ImGui.GetScrollMaxY() then
ImGui.SetScrollHereY(1.0)
end
end
ImGui.EndChild()
ImGui.End()
return p_open
end
-- Demonstrate creating a simple log window with basic filtering.
S.ShowExampleAppLog = {
log = nil, -- ExampleAppLog, created on first use
counter = 0,
}
function ShowExampleAppLog(p_open)
local s = S.ShowExampleAppLog
if s.log == nil then s.log = ExampleAppLog() end
-- For the demo: add a debug button _BEFORE_ the normal log window contents
-- We take advantage of a rarely used feature: multiple calls to Begin()/End() are appending to the _same_ window.
-- Most of the contents of the window will be added by the log.Draw() call.
ImGui.SetNextWindowSize(ImVec2(500, 400), 4 --[[ImGuiCond.FirstUseEver]])
p_open = ImGui.Begin("Example: Log", p_open)
IMGUI_DEMO_MARKER("Examples/Log")
if ImGui.SmallButton("[Debug] Add 5 entries") then
local categories = { "info", "warn", "error" }
local words = { "Bumfuzzled", "Cattywampus", "Snickersnee", "Abibliophobia", "Absquatulate", "Nincompoop", "Pauciloquent" }
for n = 0, 5 - 1 do
local category = categories[s.counter % #categories + 1]
local word = words[s.counter % #words + 1]
s.log:AddLog("[%05d] [%s] Hello, current time is %.1f, here's a word: '%s'\n",
ImGui.GetFrameCount(), category, ImGui.GetTime(), word)
s.counter = s.counter + 1
end
end
ImGui.End()
-- Actually call in the regular Log helper (which will Begin() into the same window as we just did)
return s.log:Draw("Example: Log", p_open)
end
-------------------------------------------------------------------------------
-- [SECTION] Example App: Simple Layout / ShowExampleAppLayout()
-------------------------------------------------------------------------------
-- Demonstrate create a window with multiple child windows.
S.ShowExampleAppLayout = {
selected = 0,
}
function ShowExampleAppLayout(p_open)
local s = S.ShowExampleAppLayout
ImGui.SetNextWindowSize(ImVec2(500, 440), 4 --[[ImGuiCond.FirstUseEver]])
local visible
p_open, visible = ImGui.Begin("Example: Simple layout", p_open, 1024 --[[ImGuiWindowFlags.MenuBar]])
if visible then
IMGUI_DEMO_MARKER("Examples/Simple layout")
if ImGui.BeginMenuBar() then
if ImGui.BeginMenu("File") then
if ImGui.MenuItem("Close", "Ctrl+W") then p_open = false end
ImGui.EndMenu()
end
ImGui.EndMenuBar()
end
-- Left
do
ImGui.BeginChild("left pane", ImVec2(150, 0), bit32.bor(1 --[[ImGuiChildFlags.Borders]], 4 --[[ImGuiChildFlags.ResizeX]]))
for i = 0, 100 - 1 do
local label = string.format("MyObject %d", i)
if ImGui.Selectable(label, s.selected == i, 64 --[[ImGuiSelectableFlags.SelectOnNav]]) then
s.selected = i
end
end
ImGui.EndChild()
end
ImGui.SameLine()
-- Right
do
ImGui.BeginGroup()
ImGui.BeginChild("item view", ImVec2(0, -ImGui.GetFrameHeightWithSpacing())) -- Leave room for 1 line below us
ImGui.Text("MyObject: %d", s.selected)
ImGui.Separator()
if ImGui.BeginTabBar("##Tabs", 0 --[[ImGuiTabBarFlags.None]]) then
if ImGui.BeginTabItem("Description") then
ImGui.TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ")
ImGui.EndTabItem()
end
if ImGui.BeginTabItem("Details") then
ImGui.Text("ID: 0123456789")
ImGui.EndTabItem()
end
ImGui.EndTabBar()
end
ImGui.EndChild()
if ImGui.Button("Revert") then end
ImGui.SameLine()
if ImGui.Button("Save") then end
ImGui.EndGroup()
end
end
ImGui.End()
return p_open
end
-------------------------------------------------------------------------------
-- [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()
-------------------------------------------------------------------------------
-- Some of the interactions are a bit lack-luster:
-- - We would want pressing validating or leaving the filter to somehow restore focus.
-- - We may want more advanced filtering (child nodes) and clipper support: both will need extra work.
-- - We would want to customize some keyboard interactions to easily keyboard navigate between the tree and the properties.
-------------------------------------------------------------------------------
ExampleAppPropertyEditorMT = {}
ExampleAppPropertyEditorMT.__index = ExampleAppPropertyEditorMT
function ExampleAppPropertyEditor()
local self = setmetatable({}, ExampleAppPropertyEditorMT)
self.Filter = ImGuiTextFilter()
self.SelectedNode = nil
self.UseClipper = false
self.StringBufs = setmetatable({}, { __mode = "k" }) -- [Port] node -> 28-byte edit buffer for ImGuiDataType.String fields (node.Name is a Lua string)
return self
end
function ExampleAppPropertyEditorMT:Draw(root_node)
IMGUI_DEMO_MARKER("Examples/Property editor")
-- Left side: draw tree
-- - Currently using a table to benefit from RowBg feature
-- - Our tree node are all of equal height, facilitating the use of a clipper.
if ImGui.BeginChild("##tree", ImVec2(300, 0), bit32.bor(4 --[[ImGuiChildFlags.ResizeX]], 1 --[[ImGuiChildFlags.Borders]], 256 --[[ImGuiChildFlags.NavFlattened]])) then
ImGui.PushItemFlag(4 --[[ImGuiItemFlags.NoNavDefaultFocus]], true)
_, self.UseClipper = ImGui.Checkbox("Use Clipper", self.UseClipper)
ImGui.SameLine()
ImGui.Text("(%d root nodes)", #root_node.Childs)
ImGui.SetNextItemWidth(-1.1754943508223e-38 --[[FLT_MIN]])
ImGui.SetNextItemShortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 551 --[[ImGuiKey.F]]), 262144 --[[ImGuiInputFlags.Tooltip]])
if ImGui.InputTextWithHint("##Filter", "incl -excl", self.Filter.InputBuf, 256, 128 --[[ImGuiInputTextFlags.EscapeClearsAll]]) then
self.Filter:Build()
end
ImGui.PopItemFlag()
if ImGui.BeginTable("##list", 1, 64 --[[ImGuiTableFlags.RowBg]]) then
if self.UseClipper then
self:DrawClippedTree(root_node)
else
self:DrawTree(root_node)
end
ImGui.EndTable()
end
end
ImGui.EndChild()
-- Right side: draw properties
ImGui.SameLine()
ImGui.BeginGroup() -- Lock X position
local node = self.SelectedNode
if node then
ImGui.Text("%s", node.Name)
ImGui.TextDisabled("UID: 0x%08X", node.UID)
ImGui.Separator()
if ImGui.BeginTable("##properties", 2, bit32.bor(1 --[[ImGuiTableFlags.Resizable]], 33554432 --[[ImGuiTableFlags.ScrollY]])) then
-- Push object ID after we entered the table, so table is shared for all objects
ImGui.PushID(node.UID)
ImGui.TableSetupColumn("", 16 --[[ImGuiTableColumnFlags.WidthFixed]])
ImGui.TableSetupColumn("", 8 --[[ImGuiTableColumnFlags.WidthStretch]], 2.0) -- Default twice larger
if node.HasData then
-- In a typical application, the structure description would be derived from a data-driven system.
-- - We try to mimic this with our ExampleMemberInfo structure and the ExampleTreeNodeMemberInfos[] array.
-- - Limits and some details are hard-coded to simplify the demo.
for _, field_desc in ipairs(ExampleTreeNodeMemberInfos) do
ImGui.TableNextRow()
ImGui.PushID(field_desc.Name)
ImGui.TableNextColumn()
ImGui.AlignTextToFramePadding()
ImGui.TextUnformatted(field_desc.Name)
ImGui.TableNextColumn()
local field_ptr = field_desc.Offset -- [Port] Offset is the field name inside ExampleTreeNode
if field_desc.DataType == 8 --[[ImGuiDataType.Bool]] then
assert(field_desc.DataCount == 1)
_, node[field_ptr] = ImGui.Checkbox("##Editor", node[field_ptr])
elseif field_desc.DataType == 4 --[[ImGuiDataType.S32]] then
local v_min, v_max = (-2147483648) --[[INT_MIN]], 2147483647 --[[INT_MAX]]
ImGui.SetNextItemWidth(-1.1754943508223e-38 --[[FLT_MIN]])
if field_desc.DataCount == 1 then -- [Port] scalar fields are plain Lua values: DragScalar() returns the new value
node[field_ptr] = ImGui.DragScalar("##Editor", field_desc.DataType, node[field_ptr], 1.0, v_min, v_max)
else
ImGui.DragScalarN("##Editor", field_desc.DataType, node[field_ptr], field_desc.DataCount, 1.0, v_min, v_max)
end
elseif field_desc.DataType == 6 --[[ImGuiDataType.Float]] then
local v_min, v_max = 0.0, 1.0
ImGui.SetNextItemWidth(-1.1754943508223e-38 --[[FLT_MIN]])
ImGui.SliderScalarN("##Editor", field_desc.DataType, node[field_ptr], field_desc.DataCount, v_min, v_max)
elseif field_desc.DataType == 9 --[[ImGuiDataType.String]] then
local buf = self.StringBufs[node] -- [Port] edit the Lua string through a 28-byte buffer
if buf == nil then buf = ImGui.NewTextBuffer(node[field_ptr], 28); self.StringBufs[node] = buf end
if ImGui.InputText("##Editor", buf, 28) then node[field_ptr] = ImGui.TextBufferToString(buf) end
end
ImGui.PopID()
end
end
ImGui.PopID()
ImGui.EndTable()
end
end
ImGui.EndGroup()
end
-- Custom search filter
-- - Here we apply on root node only.
-- - This does a case insensitive stristr which is pretty heavy. In a real large-scale app you would likely store a filtered list which in turns would be trivial to linearize.
function ExampleAppPropertyEditorMT:IsNodePassingFilter(node)
return node.Parent.Parent ~= nil or self.Filter:PassFilter(node.Name)
end
-- Basic version, recursive. This is how you would generally draw a tree.
-- - Simple but going to be noticeably costly if you have a large amount of nodes as DrawTreeNode() is called for all of them.
-- - On my desktop PC (2020), for 10K nodes in an optimized build this takes ~1.2 ms
-- - Unlike arrays or grids which are very easy to clip, trees are currently more difficult to clip.
function ExampleAppPropertyEditorMT:DrawTree(node)
for _, child in ipairs(node.Childs) do
if self:IsNodePassingFilter(child) and self:DrawTreeNode(child) then
self:DrawTree(child)
ImGui.TreePop()
end
end
end
-- More advanced version. Use a alternative clipping technique: fast-forwarding through non-visible chunks.
-- - On my desktop PC (2020), for 10K nodes in an optimized build this takes ~0.1 ms
-- (in ExampleTree_CreateDemoTree(), change 'int ROOT_ITEMS_COUNT = 10000' to try with this amount of root nodes).
-- - 1. Use clipper with indeterminate count (items_count = INT_MAX): we need to call SeekCursorForItem() at the end once we know the count.
-- - 2. Use SetNextItemStorageID() to specify ID used for open/close storage, making it easy to call TreeNodeGetOpen() on any arbitrary node.
-- - 3. Linearize tree during traversal: our tree data structure makes it easy to access sibling and parents.
-- - Unlike clipping for a regular array or grid which may be done using random access limited to visible areas,
-- this technique requires traversing most accessible nodes. This could be made more optimal with extra work,
-- but this is a decent simplicity<>speed trade-off.
-- See https://github.com/ocornut/imgui/issues/3823 for discussions about this.
function ExampleAppPropertyEditorMT:DrawClippedTree(root_node)
local node = root_node.Childs[1] -- First node
local clipper = ImGuiListClipper()
clipper:Begin(2147483647 --[[INT_MAX]])
while clipper:Step() do
while clipper.UserIndex < clipper.DisplayEnd and node ~= nil do
node = self:DrawClippedTreeNodeAndAdvanceToNext(clipper, node)
end
end
-- Keep going to count nodes and submit final count so we have a reliable scrollbar.
-- - One could consider caching this value and only refreshing it occasionally e.g. window is focused and an action occurs.
-- - Incorrect but cheap approximation would be to use 'clipper_current_idx = IM_MAX(clipper_current_idx, root_node.Childs.Size)' instead.
-- - If either of those is implemented, the general cost will approach zero when scrolling is at the top of the tree.
while node ~= nil do
node = self:DrawClippedTreeNodeAndAdvanceToNext(clipper, node)
end
--clipper.UserIndex = IM_MAX(clipper.UserIndex, root_node.Childs.Size); // <-- Cheap approximation instead of while() loop above.
clipper:SeekCursorForItem(clipper.UserIndex)
end
function ExampleAppPropertyEditorMT:DrawClippedTreeNodeAndAdvanceToNext(clipper, node)
if self:IsNodePassingFilter(node) then
-- Draw node if within visible range
local is_open = false
if clipper.UserIndex >= clipper.DisplayStart and clipper.UserIndex < clipper.DisplayEnd then
is_open = self:DrawTreeNode(node)
else
is_open = (#node.Childs > 0 and ImGui.TreeNodeGetOpen(node.UID))
if is_open then
ImGui.TreePush(node.Name)
end
end
clipper.UserIndex = clipper.UserIndex + 1
-- Next node: recurse into childs
if is_open then
return node.Childs[1]
end
end
-- Next node: next sibling, otherwise move back to parent
while node ~= nil do
if node.IndexInParent + 1 < #node.Parent.Childs then
return node.Parent.Childs[node.IndexInParent + 1 + 1] -- [Port] Childs is 1-based, IndexInParent 0-based
end
node = node.Parent
if node.Parent == nil then
break
end
ImGui.TreePop()
end
return nil
end
-- To support node with same name we incorporate node.UID into the item ID.
-- (this would more naturally be done using PushID(node.UID) + TreeNodeEx(node.Name, tree_flags),
-- but it would require in DrawClippedTreeNodeAndAdvanceToNext() to add PushID() before TreePush(), and PopID() after TreePop(),
-- so instead we use TreeNodeEx(node.UID, tree_flags, "%s", node.Name) here)
function ExampleAppPropertyEditorMT:DrawTreeNode(node)
ImGui.TableNextRow()
ImGui.TableNextColumn()
local tree_flags = 0 --[[ImGuiTreeNodeFlags.None]]
tree_flags = bit32.bor(tree_flags, 128 --[[ImGuiTreeNodeFlags.OpenOnArrow]], 64 --[[ImGuiTreeNodeFlags.OpenOnDoubleClick]]) -- Standard opening mode as we are likely to want to add selection afterwards
tree_flags = bit32.bor(tree_flags, 131072 --[[ImGuiTreeNodeFlags.NavLeftJumpsToParent]]) -- Left arrow support
tree_flags = bit32.bor(tree_flags, 4096 --[[ImGuiTreeNodeFlags.SpanFullWidth]]) -- Span full width for easier mouse reach
tree_flags = bit32.bor(tree_flags, 1048576 --[[ImGuiTreeNodeFlags.DrawLinesToNodes]]) -- Always draw hierarchy outlines
if node == self.SelectedNode then
tree_flags = bit32.bor(tree_flags, 1 --[[ImGuiTreeNodeFlags.Selected]]) -- Draw selection highlight
end
if #node.Childs == 0 then
tree_flags = bit32.bor(tree_flags, 256 --[[ImGuiTreeNodeFlags.Leaf]], 512 --[[ImGuiTreeNodeFlags.Bullet]], 8 --[[ImGuiTreeNodeFlags.NoTreePushOnOpen]]) -- Use _NoTreePushOnOpen + set is_open=false to avoid unnecessarily push/pop on leaves.
end
if node.DataMyBool == false then
ImGui.PushStyleColor(0 --[[ImGuiCol.Text]], ImGui.GetStyle().Colors[1 --[[ImGuiCol.TextDisabled]]])
end
ImGui.SetNextItemStorageID(node.UID) -- Use node.UID as storage id
local is_open = ImGui.TreeNodeEx(node.UID, tree_flags, "%s", node.Name)
if #node.Childs == 0 then
is_open = false
end
if node.DataMyBool == false then
ImGui.PopStyleColor()
end
if ImGui.IsItemFocused() then
self.SelectedNode = node
end
return is_open
end
-- Demonstrate creating a simple property editor.
S.ShowExampleAppPropertyEditor = {
property_editor = nil, -- ExampleAppPropertyEditor, created on first use
demo_data = nil, -- [Port] fallback when called without demo_data
}
function ShowExampleAppPropertyEditor(p_open, demo_data)
local s = S.ShowExampleAppPropertyEditor
ImGui.SetNextWindowSize(ImVec2(430, 450), 4 --[[ImGuiCond.FirstUseEver]])
local visible
p_open, visible = ImGui.Begin("Example: Property editor", p_open)
if not visible then
ImGui.End()
return p_open
end
IMGUI_DEMO_MARKER("Examples/Property Editor")
if s.property_editor == nil then s.property_editor = ExampleAppPropertyEditor() end
if demo_data == nil then -- [Port] tolerate callers without demo_data (test harness)
if s.demo_data == nil then s.demo_data = {} end
demo_data = s.demo_data
end
if demo_data.DemoTree == nil then
demo_data.DemoTree = ExampleTree_CreateDemoTree()
end
s.property_editor:Draw(demo_data.DemoTree)
ImGui.End()
return p_open
end
-------------------------------------------------------------------------------
-- [SECTION] Example App: Long Text / ShowExampleAppLongText()
-------------------------------------------------------------------------------
-- Demonstrate/test rendering huge amount of text, and the incidence of clipping.
S.ShowExampleAppLongText = {
test_type = 0,
log = ImGuiTextBuffer(),
lines = 0,
}
function ShowExampleAppLongText(p_open)
local s = S.ShowExampleAppLongText
ImGui.SetNextWindowSize(ImVec2(520, 600), 4 --[[ImGuiCond.FirstUseEver]])
local visible
p_open, visible = ImGui.Begin("Example: Long text display", p_open)
if not visible then
ImGui.End()
return p_open
end
IMGUI_DEMO_MARKER("Examples/Long text display")
ImGui.Text("Printing unusually long amount of text.")
s.test_type = ImGui.Combo("Test type", s.test_type,
"Single call to TextUnformatted()\0" ..
"Multiple calls to Text(), clipped\0" ..
"Multiple calls to Text(), not clipped (slow)\0")
ImGui.Text("Buffer contents: %d lines, %d bytes", s.lines, s.log:size())
if ImGui.Button("Clear") then s.log:clear(); s.lines = 0 end
ImGui.SameLine()
if ImGui.Button("Add 1000 lines") then
for i = 0, 1000 - 1 do
s.log:appendf("%i The quick brown fox jumps over the lazy dog\n", s.lines + i)
end
s.lines = s.lines + 1000
end
ImGui.BeginChild("Log")
if s.test_type == 0 then
-- Single call to TextUnformatted() with a big buffer
ImGui.TextUnformatted(s.log:c_str()) -- [Port] ImGuiTextBuffer is a chunk list: c_str() instead of begin()/end()
elseif s.test_type == 1 then
-- Multiple calls to Text(), manually coarsely clipped - demonstrate how to use the ImGuiListClipper helper.
ImGui.PushStyleVar(14 --[[ImGuiStyleVar.ItemSpacing]], ImVec2(0, 0))
local clipper = ImGuiListClipper()
clipper:Begin(s.lines)
while clipper:Step() do
for i = clipper.DisplayStart, clipper.DisplayEnd - 1 do
ImGui.Text("%i The quick brown fox jumps over the lazy dog", i)
end
end
ImGui.PopStyleVar()
elseif s.test_type == 2 then
-- Multiple calls to Text(), not clipped (slow)
ImGui.PushStyleVar(14 --[[ImGuiStyleVar.ItemSpacing]], ImVec2(0, 0))
for i = 0, s.lines - 1 do
ImGui.Text("%i The quick brown fox jumps over the lazy dog", i)
end
ImGui.PopStyleVar()
end
ImGui.EndChild()
ImGui.End()
return p_open
end
-------------------------------------------------------------------------------
-- [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize()
-------------------------------------------------------------------------------
-- Demonstrate creating a window which gets auto-resized according to its content.
S.ShowExampleAppAutoResize = {
lines = 10,
}
function ShowExampleAppAutoResize(p_open)
local s = S.ShowExampleAppAutoResize
local visible
p_open, visible = ImGui.Begin("Example: Auto-resizing window", p_open, 64 --[[ImGuiWindowFlags.AlwaysAutoResize]])
if not visible then
ImGui.End()
return p_open
end
IMGUI_DEMO_MARKER("Examples/Auto-resizing window")
ImGui.TextUnformatted(
"Window will resize every-frame to the size of its content.\n" ..
"Note that you probably don't want to query the window size to\n" ..
"output your content because that would create a feedback loop.")
s.lines = ImGui.SliderInt("Number of lines", s.lines, 1, 20)
for i = 0, s.lines - 1 do
ImGui.Text("%sThis is line %d", string.rep(" ", i * 4), i) -- Pad with space to extend size horizontally -- [Port] "%*s" -> string.rep
end
ImGui.End()
return p_open
end
-------------------------------------------------------------------------------
-- [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize()
-------------------------------------------------------------------------------
-- Demonstrate creating a window with custom resize constraints.
-- Note that size constraints currently don't work on a docked window (when in 'docking' branch)
CustomConstraints = {}
-- Helper functions to demonstrate programmatic constraints
-- FIXME: This doesn't take account of decoration size (e.g. title bar), library should make this easier.
-- FIXME: None of the three demos works consistently when resizing from borders.
function CustomConstraints.AspectRatio(data)
local aspect_ratio = data.UserData -- [Port] UserData is the number itself (no float*)
data.DesiredSize.y = math.floor(data.DesiredSize.x / aspect_ratio)
end
function CustomConstraints.Square(data)
local m = ImMax(data.DesiredSize.x, data.DesiredSize.y) -- [Port] IM_MAX -> ImMax
data.DesiredSize.x = m; data.DesiredSize.y = m
end
function CustomConstraints.Step(data)
local step = data.UserData
data.DesiredSize = ImVec2(math.floor(data.DesiredSize.x / step + 0.5) * step, math.floor(data.DesiredSize.y / step + 0.5) * step)
end
S.ShowExampleAppConstrainedResize = {
-- Options
auto_resize = false,
window_padding = true,
type = 6, -- Aspect Ratio
display_lines = 10,
}
function ShowExampleAppConstrainedResize(p_open)
local s = S.ShowExampleAppConstrainedResize
local test_desc =
{
"Between 100x100 and 500x500",
"At least 100x100",
"Resize vertical + lock current width",
"Resize horizontal + lock current height",
"Width Between 400 and 500",
"Height at least 400",
"Custom: Aspect Ratio 16:9",
"Custom: Always Square",
"Custom: Fixed Steps (100)",
}
-- Submit constraint
local aspect_ratio = 16.0 / 9.0
local fixed_step = 100.0
if s.type == 0 then ImGui.SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(500, 500)) end -- Between 100x100 and 500x500
if s.type == 1 then ImGui.SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]])) end -- Width > 100, Height > 100
if s.type == 2 then ImGui.SetNextWindowSizeConstraints(ImVec2(-1, 0), ImVec2(-1, 3.4028234663853e+38 --[[FLT_MAX]])) end -- Resize vertical + lock current width
if s.type == 3 then ImGui.SetNextWindowSizeConstraints(ImVec2(0, -1), ImVec2(3.4028234663853e+38 --[[FLT_MAX]], -1)) end -- Resize horizontal + lock current height
if s.type == 4 then ImGui.SetNextWindowSizeConstraints(ImVec2(400, -1), ImVec2(500, -1)) end -- Width Between and 400 and 500
if s.type == 5 then ImGui.SetNextWindowSizeConstraints(ImVec2(-1, 400), ImVec2(-1, 3.4028234663853e+38 --[[FLT_MAX]])) end -- Height at least 400
if s.type == 6 then ImGui.SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]]), CustomConstraints.AspectRatio, aspect_ratio) end -- Aspect ratio
if s.type == 7 then ImGui.SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]]), CustomConstraints.Square) end -- Always Square
if s.type == 8 then ImGui.SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]]), CustomConstraints.Step, fixed_step) end -- Fixed Step
-- Submit window
if not s.window_padding then
ImGui.PushStyleVar(2 --[[ImGuiStyleVar.WindowPadding]], ImVec2(0.0, 0.0))
end
local window_flags = s.auto_resize and 64 --[[ImGuiWindowFlags.AlwaysAutoResize]] or 0
local window_open
p_open, window_open = ImGui.Begin("Example: Constrained Resize", p_open, window_flags)
if not s.window_padding then
ImGui.PopStyleVar()
end
IMGUI_DEMO_MARKER("Examples/Constrained Resizing window")
if window_open then
if ImGui.GetIO().KeyShift then
-- Display a dummy viewport (in your real app you would likely use ImageButton() to display a texture)
local avail_size = ImGui.GetContentRegionAvail()
local pos = ImGui.GetCursorScreenPos()
ImGui.ColorButton("viewport", ImVec4(0.5, 0.2, 0.5, 1.0), bit32.bor(64 --[[ImGuiColorEditFlags.NoTooltip]], 512 --[[ImGuiColorEditFlags.NoDragDrop]]), avail_size)
ImGui.SetCursorScreenPos(ImVec2(pos.x + 10, pos.y + 10))
ImGui.Text("%.2f x %.2f", avail_size.x, avail_size.y)
else
ImGui.Text("(Hold Shift to display a dummy viewport)")
if ImGui.IsWindowDocked() then
ImGui.Text("Warning: Sizing Constraints won't work if the window is docked!")
end
if ImGui.Button("Set 200x200") then ImGui.SetWindowSize(ImVec2(200, 200)) end ImGui.SameLine()
if ImGui.Button("Set 500x500") then ImGui.SetWindowSize(ImVec2(500, 500)) end ImGui.SameLine()
if ImGui.Button("Set 800x200") then ImGui.SetWindowSize(ImVec2(800, 200)) end
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 20)
s.type = ImGui.Combo("Constraint", s.type, test_desc, #test_desc)
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 20)
s.display_lines = ImGui.DragInt("Lines", s.display_lines, 0.2, 1, 100)
_, s.auto_resize = ImGui.Checkbox("Auto-resize", s.auto_resize)
_, s.window_padding = ImGui.Checkbox("Window padding", s.window_padding)
for i = 0, s.display_lines - 1 do
ImGui.Text("%sHello, sailor! Making this line long enough for the example.", string.rep(" ", i * 4)) -- [Port] "%*s" -> string.rep
end
end
end
ImGui.End()
return p_open
end
-------------------------------------------------------------------------------
-- [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay()
-------------------------------------------------------------------------------
-- Demonstrate creating a simple static window with no decoration
-- + a context-menu to choose which corner of the screen to use.
S.ShowExampleAppSimpleOverlay = {
location = 0,
}
function ShowExampleAppSimpleOverlay(p_open)
local s = S.ShowExampleAppSimpleOverlay
local io = ImGui.GetIO()
local window_flags = bit32.bor(43 --[[ImGuiWindowFlags.NoDecoration]], 524288 --[[ImGuiWindowFlags.NoDocking]], 64 --[[ImGuiWindowFlags.AlwaysAutoResize]], 256 --[[ImGuiWindowFlags.NoSavedSettings]], 4096 --[[ImGuiWindowFlags.NoFocusOnAppearing]], 196608 --[[ImGuiWindowFlags.NoNav]])
if s.location >= 0 then
local PAD = 10.0
local viewport = ImGui.GetMainViewport()
local work_pos = viewport.WorkPos -- Use work area to avoid menu-bar/task-bar, if any!
local work_size = viewport.WorkSize
local window_pos, window_pos_pivot = ImVec2(), ImVec2()
window_pos.x = (bit32.band(s.location, 1) ~= 0) and (work_pos.x + work_size.x - PAD) or (work_pos.x + PAD)
window_pos.y = (bit32.band(s.location, 2) ~= 0) and (work_pos.y + work_size.y - PAD) or (work_pos.y + PAD)
window_pos_pivot.x = (bit32.band(s.location, 1) ~= 0) and 1.0 or 0.0
window_pos_pivot.y = (bit32.band(s.location, 2) ~= 0) and 1.0 or 0.0
ImGui.SetNextWindowPos(window_pos, 1 --[[ImGuiCond.Always]], window_pos_pivot)
ImGui.SetNextWindowViewport(viewport.ID)
window_flags = bit32.bor(window_flags, 4 --[[ImGuiWindowFlags.NoMove]])
elseif s.location == -2 then
-- Center window
ImGui.SetNextWindowPos(ImGui.GetMainViewport():GetCenter(), 1 --[[ImGuiCond.Always]], ImVec2(0.5, 0.5))
window_flags = bit32.bor(window_flags, 4 --[[ImGuiWindowFlags.NoMove]])
end
ImGui.SetNextWindowBgAlpha(0.35) -- Transparent background
local visible
p_open, visible = ImGui.Begin("Example: Simple overlay", p_open, window_flags)
if visible then
IMGUI_DEMO_MARKER("Examples/Simple overlay") -- Scroll up to the beginning of this function to see overlay flags
ImGui.Text("Simple overlay\n" .. "(right-click to change position)")
ImGui.Separator()
if ImGui.IsMousePosValid() then
ImGui.Text("Mouse Position: (%.1f,%.1f)", io.MousePos.x, io.MousePos.y)
else
ImGui.Text("Mouse Position: <invalid>")
end
if ImGui.BeginPopupContextWindow() then
if ImGui.MenuItem("Custom", nil, s.location == -1) then s.location = -1 end
if ImGui.MenuItem("Center", nil, s.location == -2) then s.location = -2 end
if ImGui.MenuItem("Top-left", nil, s.location == 0) then s.location = 0 end
if ImGui.MenuItem("Top-right", nil, s.location == 1) then s.location = 1 end
if ImGui.MenuItem("Bottom-left", nil, s.location == 2) then s.location = 2 end
if ImGui.MenuItem("Bottom-right", nil, s.location == 3) then s.location = 3 end
if p_open ~= nil and ImGui.MenuItem("Close") then p_open = false end
ImGui.EndPopup()
end
end
ImGui.End()
return p_open
end
-------------------------------------------------------------------------------
-- [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen()
-------------------------------------------------------------------------------
-- Demonstrate creating a window covering the entire screen/viewport
S.ShowExampleAppFullscreen = {
use_work_area = true,
flags = bit32.bor(ImGuiWindowFlags.NoDecoration, ImGuiWindowFlags.NoMove, ImGuiWindowFlags.NoSavedSettings),
}
function ShowExampleAppFullscreen(p_open)
local s = S.ShowExampleAppFullscreen
-- We demonstrate using the full viewport area or the work area (without menu-bars, task-bars etc.)
-- Based on your use case you may want one or the other.
local viewport = ImGui.GetMainViewport()
ImGui.SetNextWindowPos(s.use_work_area and viewport.WorkPos or viewport.Pos)
ImGui.SetNextWindowSize(s.use_work_area and viewport.WorkSize or viewport.Size)
local visible
p_open, visible = ImGui.Begin("Example: Fullscreen window", p_open, s.flags)
if visible then
IMGUI_DEMO_MARKER("Examples/Fullscreen window")
_, s.use_work_area = ImGui.Checkbox("Use work area instead of main area", s.use_work_area)
ImGui.SameLine()
HelpMarker("Main Area = entire viewport,\nWork Area = entire viewport minus sections used by the main menu bars, task bars etc.\n\nEnable the main-menu bar in Examples menu to see the difference.")
_, s.flags = ImGui.CheckboxFlags("ImGuiWindowFlags_NoBackground", s.flags, 128 --[[ImGuiWindowFlags.NoBackground]])
_, s.flags = ImGui.CheckboxFlags("ImGuiWindowFlags_NoDecoration", s.flags, 43 --[[ImGuiWindowFlags.NoDecoration]])
ImGui.Indent()
_, s.flags = ImGui.CheckboxFlags("ImGuiWindowFlags_NoTitleBar", s.flags, 1 --[[ImGuiWindowFlags.NoTitleBar]])
_, s.flags = ImGui.CheckboxFlags("ImGuiWindowFlags_NoCollapse", s.flags, 32 --[[ImGuiWindowFlags.NoCollapse]])
_, s.flags = ImGui.CheckboxFlags("ImGuiWindowFlags_NoScrollbar", s.flags, 8 --[[ImGuiWindowFlags.NoScrollbar]])
ImGui.Unindent()
if p_open ~= nil and ImGui.Button("Close this window") then
p_open = false
end
end
ImGui.End()
return p_open
end
-------------------------------------------------------------------------------
-- [SECTION] Example App: Manipulating Window Titles / ShowExampleAppWindowTitles()
-------------------------------------------------------------------------------
-- Demonstrate the use of "##" and "###" in identifiers to manipulate ID generation.
-- This applies to all regular items as well.
-- Read FAQ section "How can I have multiple widgets with the same label?" for details.
function ShowExampleAppWindowTitles(p_open)
local viewport = ImGui.GetMainViewport()
local base_pos = viewport.Pos
-- By default, Windows are uniquely identified by their title.
-- You can use the "##" and "###" markers to manipulate the display/ID.
-- Using "##" to display same title but have unique identifier.
ImGui.SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 100), 4 --[[ImGuiCond.FirstUseEver]])
ImGui.Begin("Same title as another window##1")
IMGUI_DEMO_MARKER("Examples/Manipulating window titles##1")
ImGui.Text("This is window 1.\nMy title is the same as window 2, but my identifier is unique.")
ImGui.End()
ImGui.SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 200), 4 --[[ImGuiCond.FirstUseEver]])
ImGui.Begin("Same title as another window##2")
IMGUI_DEMO_MARKER("Examples/Manipulating window titles##2")
ImGui.Text("This is window 2.\nMy title is the same as window 1, but my identifier is unique.")
ImGui.End()
-- Using "###" to display a changing title but keep a static identifier "AnimatedTitle"
local spin_i = bit32.band(math.floor(ImGui.GetTime() / 0.25), 3) + 1
local buf = string.format("Animated title %s %d###AnimatedTitle", string.sub("|/-\\", spin_i, spin_i), ImGui.GetFrameCount())
ImGui.SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 300), 4 --[[ImGuiCond.FirstUseEver]])
ImGui.Begin(buf)
IMGUI_DEMO_MARKER("Examples/Manipulating window titles##3")
ImGui.Text("This window has a changing title.")
ImGui.End()
return p_open
end
-------------------------------------------------------------------------------
-- [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering()
-------------------------------------------------------------------------------
-- See our Wiki guides:
-- - ImDrawList Vector Rendering Reference: https://github.com/ocornut/imgui/wiki/Draw-List
-- - Pixel Perfect Anti-aliased Rendering: https://github.com/ocornut/imgui/wiki/Pixel-Perfect-Rendering
-- - How Anti-aliased Polyline Rendering is Implemented: https://github.com/ocornut/imgui/wiki/Polyline-Rendering
-- Demonstrate using the low-level ImDrawList to draw custom shapes.
S.ShowExampleAppCustomRendering = {
-- Primitives
sz = 42.0,
base_rounding = 8.0,
base_thickness = 3.0,
animate_rounding = false,
animate_thickness = false,
ngon_segments = 6,
circle_segments_override = false,
circle_segments_override_v = 12,
curve_segments_override = false,
curve_segments_override_v = 8,
colf_stroke = ImVec4(1.000, 0.384, 0.169, 1.000),
colf_fill = ImVec4(0.416, 0.378, 0.420, 1.000),
prim_stroke_flags = ImDrawFlags.None,
prim_other_flags = ImDrawFlags.AALineEnds,
scope_flags = nil, -- First init copy current state (see below).
-- Canvas
points = ImVector(),
scrolling = ImVec2(0.0, 0.0),
opt_enable_grid = true,
opt_enable_context_menu = true,
adding_line = false,
-- BG/FG draw lists
draw_bg = true,
draw_fg = true,
}
function ShowExampleAppCustomRendering(p_open)
local s = S.ShowExampleAppCustomRendering
local visible
p_open, visible = ImGui.Begin("Example: Custom rendering", p_open)
if not visible then
ImGui.End()
return p_open
end
IMGUI_DEMO_MARKER("Examples/Custom rendering")
-- Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of
-- overloaded operators, etc. Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your
-- types and ImVec2/ImVec4. Dear ImGui defines overloaded operators but they are internal to imgui.cpp and not
-- exposed outside (to avoid messing with your types) In this example we are not using the maths operators!
if ImGui.BeginTabBar("##TabBar") then
if ImGui.BeginTabItem("Primitives") then
IMGUI_DEMO_MARKER("Examples/Custom rendering/Primitives")
ImGui.PushItemWidth(-ImGui.GetFontSize() * 15)
ImGui.PushItemFlag(384 --[[ImGuiItemFlags.LiveEditOnInput]], true)
local draw_list = ImGui.GetWindowDrawList()
-- Draw gradients
-- (note that those are currently exacerbating our sRGB/Linear issues)
-- Calling ImGui.GetColorU32() multiplies the given colors by the current Style Alpha, but you may pass the IM_COL32() directly as well..
-- [Port] GetColorU32(u32, nil, true): the ImU32 overload is selected explicitly
ImGui.Text("Gradients")
local gradient_size = ImVec2(ImGui.CalcItemWidth(), ImGui.GetFrameHeight())
do
ImGui.InvisibleButton("##gradient1", gradient_size)
local col_a = ImGui.GetColorU32(IM_COL32(0, 0, 0, 255), nil, true)
local col_b = ImGui.GetColorU32(IM_COL32(255, 255, 255, 255), nil, true)
draw_list:AddRectFilledMultiColor(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), col_a, col_b, col_b, col_a)
end
do
ImGui.InvisibleButton("##gradient2", gradient_size)
local col_a = ImGui.GetColorU32(IM_COL32(0, 255, 0, 255), nil, true)
local col_b = ImGui.GetColorU32(IM_COL32(255, 0, 0, 255), nil, true)
draw_list:AddRectFilledMultiColor(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), col_a, col_b, col_b, col_a)
end
-- Draw a bunch of primitives
ImGui.Text("All primitives")
s.sz = ImGui.DragFloat("Size", s.sz, 0.2, 0.2, 100.0, "%.0f")
local rounding_anim_period = 3.0
local rounding_wave = 1.0 - math.abs(math.fmod(ImGui.GetTime(), rounding_anim_period) * (2.0 / rounding_anim_period) - 1.0)
local rounding = s.animate_rounding and math.floor(rounding_wave * s.base_rounding * 10.0) / 10.0 or s.base_rounding -- Sub-integer rounding works but has discontinuities in small values, so best disable we
s.base_rounding = ImGui.DragFloat("Rounding", s.base_rounding, 0.02, 0.0, 32.0, "%.1f"); ImGui.SameLine()
_, s.animate_rounding = ImGui.Checkbox("Animate##rounding", s.animate_rounding); ImGui.SameLine()
ImGui.Text("%.2f", rounding)
local thickness_anim_period = 5.0
local thickness_wave = 1.0 - math.abs(math.fmod(ImGui.GetTime(), thickness_anim_period) * (2.0 / thickness_anim_period) - 1.0)
local thickness = s.animate_thickness and thickness_wave * s.base_thickness or s.base_thickness
s.base_thickness = ImGui.DragFloat("Thickness", s.base_thickness, 0.02, 0.0, 32.0, "%.02f"); ImGui.SameLine()
_, s.animate_thickness = ImGui.Checkbox("Animate##thickness", s.animate_thickness); ImGui.SameLine()
ImGui.Text("%.2f", thickness)
local changed
s.ngon_segments = ImGui.SliderInt("N-gon sides", s.ngon_segments, 3, 12)
_, s.circle_segments_override = ImGui.Checkbox("##CircleSegmentOverride", s.circle_segments_override)
ImGui.SameLine(0.0, ImGui.GetStyle().ItemInnerSpacing.x)
s.circle_segments_override_v, changed = ImGui.SliderInt("Circle segments override", s.circle_segments_override_v, 3, 30)
s.circle_segments_override = s.circle_segments_override or changed
_, s.curve_segments_override = ImGui.Checkbox("##CurvesSegmentOverride", s.curve_segments_override)
ImGui.SameLine(0.0, ImGui.GetStyle().ItemInnerSpacing.x)
s.curve_segments_override_v, changed = ImGui.SliderInt("Curves segments override", s.curve_segments_override_v, 3, 30)
s.curve_segments_override = s.curve_segments_override or changed
ImGui.ColorEdit4("Stroke Color", s.colf_stroke)
ImGui.ColorEdit4("Fill Color", s.colf_fill)
ImGui.SeparatorText("Per primitive flags (AddXXX functions)")
_, s.prim_stroke_flags = ImGui.RadioButton("Default", s.prim_stroke_flags, 0 --[[ImDrawFlags.None]]); ImGui.SameLine()
_, s.prim_stroke_flags = ImGui.RadioButton("Inside", s.prim_stroke_flags, 131072 --[[ImDrawFlags.StrokeInside]]); ImGui.SameLine()
_, s.prim_stroke_flags = ImGui.RadioButton("Center", s.prim_stroke_flags, 262144 --[[ImDrawFlags.StrokeCenter]]); ImGui.SameLine()
_, s.prim_stroke_flags = ImGui.RadioButton("CenterBiased", s.prim_stroke_flags, 393216 --[[ImDrawFlags.StrokeCenterBiased]]); ImGui.SameLine()
_, s.prim_stroke_flags = ImGui.RadioButton("Outside", s.prim_stroke_flags, 524288 --[[ImDrawFlags.StrokeOutside]]); ImGui.SameLine()
_, s.prim_stroke_flags = ImGui.RadioButton("Legacy", s.prim_stroke_flags, 917504 --[[ImDrawFlags.StrokeLegacy]])
_, s.prim_other_flags = ImGui.CheckboxFlags("AAFill", s.prim_other_flags, 4096 --[[ImDrawFlags.AAFill]]); ImGui.SameLine() -- Note: Only for functions taking flags.
_, s.prim_other_flags = ImGui.CheckboxFlags("AALines", s.prim_other_flags, 8192 --[[ImDrawFlags.AALines]]); ImGui.SameLine() -- Note: Only for functions taking flags.
_, s.prim_other_flags = ImGui.CheckboxFlags("AALineEnds", s.prim_other_flags, 16384 --[[ImDrawFlags.AALineEnds]]); ImGui.SameLine()
_, s.prim_other_flags = ImGui.CheckboxFlags("JoinMiter", s.prim_other_flags, 1024 --[[ImDrawFlags.JoinMiter]]); ImGui.SameLine()
_, s.prim_other_flags = ImGui.CheckboxFlags("CapSquare", s.prim_other_flags, 2048 --[[ImDrawFlags.CapSquare]])
--ImGui.CheckboxFlags("UseTexForRoundCorners", &prim_other_flags, ImDrawFlags_UseTexForRoundCorners);
if s.scope_flags == nil then s.scope_flags = bit32.band(draw_list.Flags, 8286704 --[[ImDrawFlags.AllowInPushScope_]]) end -- First init copy current state.
ImGui.SeparatorText("Current ImDrawList flags (e.g. PushDrawFlag function)")
ImGui.PushID("ScopeFlags")
_, s.scope_flags = ImGui.CheckboxFlags("AAFill", s.scope_flags, 4096 --[[ImDrawFlags.AAFill]]); ImGui.SameLine()
_, s.scope_flags = ImGui.CheckboxFlags("AALines", s.scope_flags, 8192 --[[ImDrawFlags.AALines]]); ImGui.SameLine()
_, s.scope_flags = ImGui.CheckboxFlags("AALineEnds", s.scope_flags, 16384 --[[ImDrawFlags.AALineEnds]]); ImGui.SameLine()
_, s.scope_flags = ImGui.CheckboxFlags("StrokeLegacy", s.scope_flags, 917504 --[[ImDrawFlags.StrokeLegacy]])
--ImGui.CheckboxFlags("UseTexForRoundCorners", &scope_flags, ImDrawFlags_UseTexForRoundCorners);
ImGui.PopID()
ImGui.Spacing()
draw_list:PushDrawFlag(8286704 --[[ImDrawFlags.AllowInPushScope_]], false) -- Reset all existing (very unusual: done for this demo)
draw_list:PushDrawFlag(s.scope_flags, true)
local flags = bit32.bor(s.prim_stroke_flags, s.prim_other_flags)
local sz = s.sz
local start_pos = ImGui.GetCursorScreenPos()
local pi = 3.141592
local step = sz + 10.0
local corners_tl_br = bit32.bor(16 --[[ImDrawFlags.RoundTopLeft]], 128 --[[ImDrawFlags.RoundBottomRight]])
local half_sz = sz * 0.5
local circle_segments = s.circle_segments_override and s.circle_segments_override_v or 0
local curve_segments = s.curve_segments_override and s.curve_segments_override_v or 0
local cp3 = { ImVec2(0.0, sz * 0.6), ImVec2(sz * 0.5, -sz * 0.4), ImVec2(sz, sz) } -- Control points for curves
local cp4 = { ImVec2(0.0, 0.0), ImVec2(sz * 1.3, sz * 0.3), ImVec2(sz - sz * 1.3, sz - sz * 0.3), ImVec2(sz, sz) }
local concave_shape = { ImVec2(0.0, 0.0), ImVec2(0.3, 0.0), ImVec2(0.3, 0.7), ImVec2(0.7, 0.7), ImVec2(0.7, 0.0), ImVec2(1.0, 0.0), ImVec2(1.0, 1.0), ImVec2(0.0, 1.0) } -- |_| looking shape. Normalized.
local zigzag_shape = { ImVec2(0.0, 0.0), ImVec2(0.9, 0.0), ImVec2(1.0, 0.1), ImVec2(1.0, 0.9), ImVec2(0.9, 1.0), ImVec2(0.3, 1.0), ImVec2(0.3, 0.4), ImVec2(0.9, 0.4) }
local rotating_square = {} -- 1-based
for side = 0, 4 - 1 do
local t = ImGui.GetTime() * 0.1 + side * pi * 0.5
rotating_square[side + 1] = ImVec2(math.cos(t) * half_sz, math.sin(t) * half_sz)
end
local x = start_pos.x
local y = start_pos.y
for row = 0, 4 - 1 do
x = start_pos.x
local draw_fill = (row == 2 or row == 3)
local draw_strokes = (row == 0 or row == 1 or row == 3)
if draw_fill then
-- FILLED SHAPES
local col = ImColor(s.colf_fill):U32()
draw_list:AddNgonFilled(ImVec2(x + half_sz, y + half_sz), half_sz, col, s.ngon_segments); x = x + step -- N-gon
draw_list:AddCircleFilled(ImVec2(x + half_sz, y + half_sz), half_sz, col, circle_segments); x = x + step -- Circle
draw_list:AddEllipseFilled(ImVec2(x + half_sz, y + half_sz), ImVec2(half_sz, sz * 0.3), col, -0.3, circle_segments); x = x + step -- Ellipse
draw_list:AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0, flags); x = x + step -- Square
draw_list:AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, flags); x = x + step -- Square with all rounded corners
draw_list:AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, bit32.bor(flags, corners_tl_br)); x = x + step -- Square with two rounded corners
draw_list:AddTriangleFilled(ImVec2(x + sz * 0.5, y), ImVec2(x + sz, y + sz), ImVec2(x, y + sz), col); x = x + step -- Triangle
draw_list:AddTriangleFilled(ImVec2(x + sz * 0.2, y), ImVec2(x + sz * 0.4, y + sz), ImVec2(x, y + sz), col); x = x + step - math.floor(sz * 0.6) -- Thin triangle
-- Concave shape: U
for _, p in ipairs(concave_shape) do
draw_list:PathLineTo(ImVec2(x + math.floor(sz * p.x), y + math.floor(sz * p.y)))
end
draw_list:PathFillConcave(col)
x = x + step
-- Concave shape: Zig-Zag with sharp angles (see difference with/without JoinMiter)
for _, p in ipairs(zigzag_shape) do
draw_list:PathLineTo(ImVec2(x + math.floor(sz * p.x), y + math.floor(sz * p.y)))
end
draw_list:PathFillConcave(col)
x = x + step
-- Horizontal/Vertical lines
-- (here for consistency with the strokes section. AddRectFilled() is roughly close to AddLineH(), AddLineV()
draw_list:AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col); x = x + step
draw_list:AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col); x = x + step - math.floor(half_sz)
if not (draw_fill and draw_strokes) then -- -V560
local block_sz, off = 1, 0
while block_sz < 16 and off + block_sz <= sz do
draw_list:AddRectFilled(ImVec2(x, y + off), ImVec2(x + block_sz, y + off + block_sz), col)
off = off + block_sz + 1; block_sz = block_sz + 1
end
end
x = x + step - math.floor(half_sz)
-- Rotating square
for n = 0, 4 - 1 do
draw_list:PathLineTo(ImVec2(x + half_sz + rotating_square[n + 1].x, y + half_sz + rotating_square[n + 1].y))
end
draw_list:PathFillConvex(col, flags)
x = x + step
-- Path
draw_list:PathArcTo(ImVec2(x + half_sz, y + half_sz), half_sz, pi * -0.5, pi * 1.1)
draw_list:PathFillConvex(col)
x = x + step
-- Quadratic Bezier Curve (3 control points)
draw_list:PathLineTo(ImVec2(x + cp3[1].x, y + cp3[1].y))
draw_list:PathBezierQuadraticCurveTo(ImVec2(x + cp3[2].x, y + cp3[2].y), ImVec2(x + cp3[3].x, y + cp3[3].y), curve_segments)
draw_list:PathFillConvex(col)
x = x + step
if not (draw_fill and draw_strokes) then -- -V560
draw_list:AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x + sz, y + sz), IM_COL32(0, 0, 0, 255), IM_COL32(255, 0, 0, 255), IM_COL32(255, 255, 0, 255), IM_COL32(0, 255, 0, 255))
x = x + step
end
end
if draw_fill and draw_strokes then
x = start_pos.x
end
if draw_strokes then
-- STROKES
-- First row uses a thickness of 1.0f, second row uses the configurable thickness
local col = ImColor(s.colf_stroke):U32()
local th = (row == 0) and 1.0 or thickness
draw_list:AddNgon(ImVec2(x + half_sz, y + half_sz), half_sz, col, s.ngon_segments, th, flags); x = x + step -- N-gon
draw_list:AddCircle(ImVec2(x + half_sz, y + half_sz), half_sz, col, circle_segments, th, flags); x = x + step -- Circle
draw_list:AddEllipse(ImVec2(x + half_sz, y + half_sz), ImVec2(half_sz, sz * 0.3), col, -0.3, circle_segments, th, flags); x = x + step -- Ellipse
draw_list:AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0, th, flags); x = x + step -- Square
draw_list:AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, th, flags); x = x + step -- Square with all rounded corners
draw_list:AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, th, bit32.bor(flags, corners_tl_br)); x = x + step -- Square with two rounded corners
draw_list:AddTriangle(ImVec2(x + sz * 0.5, y), ImVec2(x + sz, y + sz), ImVec2(x, y + sz), col, th, flags); x = x + step -- Triangle
draw_list:AddTriangle(ImVec2(x + sz * 0.2, y), ImVec2(x + sz * 0.4, y + sz), ImVec2(x, y + sz), col, th, flags); x = x + step - math.floor(sz * 0.6) -- Thin triangle
-- Concave shape: U
for _, p in ipairs(concave_shape) do
draw_list:PathLineTo(ImVec2(x + math.floor(sz * p.x), y + math.floor(sz * p.y)))
end
draw_list:PathStroke(col, th, bit32.bor(flags, 512 --[[ImDrawFlags.Closed]]))
x = x + step
-- Concave shape: Zig-Zag with sharp angles (see difference with/without JoinMiter)
for _, p in ipairs(zigzag_shape) do
draw_list:PathLineTo(ImVec2(x + math.floor(sz * p.x), y + math.floor(sz * p.y)))
end
draw_list:PathStroke(col, th, bit32.bor(flags, 512 --[[ImDrawFlags.Closed]]))
x = x + step
-- Horizontal/Vertical/Diagonal lines
local off = math.floor(sz * 0.4)
draw_list:AddLineH(x, x + sz, y, col, th, flags)
draw_list:AddLine(ImVec2(x, y + off), ImVec2(x + sz, y + off), col, th, flags)
x = x + step
draw_list:AddLineV(x, y, y + sz, col, th, flags)
draw_list:AddLine(ImVec2(x + off, y + sz), ImVec2(x + off, y), col, th, flags)
x = x + step - math.floor(half_sz)
if not (draw_fill and draw_strokes) then -- -V560
draw_list:AddLine(ImVec2(x, y), ImVec2(x + half_sz, y + sz), col, th, flags)
draw_list:AddLine(ImVec2(x, y + sz), ImVec2(x + half_sz, y), col, th, flags)
end
x = x + step - math.floor(half_sz)
-- Rotating square
for n = 0, 4 - 1 do
draw_list:PathLineTo(ImVec2(x + half_sz + rotating_square[n + 1].x, y + half_sz + rotating_square[n + 1].y))
end
draw_list:PathStroke(col, th, bit32.bor(flags, 512 --[[ImDrawFlags.Closed]]))
--for (int n = 0; n < 4; n++) // ...made of individual line
-- draw_list->AddLine({ x + half_sz + rotating_square[n].x, y + half_sz + rotating_square[n].y }, { x + half_sz + rotating_square[(n + 1) & 3].x, y + half_sz + rotating_square[(n + 1) & 3].y }, col, th, flags);
x = x + step
-- Path
draw_list:PathArcTo(ImVec2(x + half_sz, y + half_sz), half_sz, pi * -0.5, pi * 1.1)
draw_list:PathStroke(col, th, flags)
x = x + step
-- Quadratic Bezier Curve (3 control points)
draw_list:AddBezierQuadratic(ImVec2(x + cp3[1].x, y + cp3[1].y), ImVec2(x + cp3[2].x, y + cp3[2].y), ImVec2(x + cp3[3].x, y + cp3[3].y), col, th, curve_segments, flags)
x = x + step
-- Cubic Bezier Curve (4 control points)
if not (draw_fill and draw_strokes) then -- -V560
draw_list:AddBezierCubic(ImVec2(x + cp4[1].x, y + cp4[1].y), ImVec2(x + cp4[2].x, y + cp4[2].y), ImVec2(x + cp4[3].x, y + cp4[3].y), ImVec2(x + cp4[4].x, y + cp4[4].y), col, th, curve_segments, flags)
x = x + step
end
end
y = y + step
end
ImGui.Dummy(ImVec2(step * 15.5, step * 4.0))
draw_list:PopDrawFlag()
draw_list:PopDrawFlag()
ImGui.Text("ImDrawList Vector Rendering Reference:")
ImGui.TextLinkOpenURL("https://github.com/ocornut/imgui/wiki/Draw-List")
ImGui.PopItemFlag()
ImGui.PopItemWidth()
ImGui.EndTabItem()
end
if ImGui.BeginTabItem("Canvas") then
IMGUI_DEMO_MARKER("Examples/Custom rendering/Canvas")
local points = s.points
local scrolling = s.scrolling
_, s.opt_enable_grid = ImGui.Checkbox("Enable grid", s.opt_enable_grid)
_, s.opt_enable_context_menu = ImGui.Checkbox("Enable context menu", s.opt_enable_context_menu)
ImGui.Text("Mouse Left: drag to add lines,\nMouse Right: drag to scroll, click for context menu.")
-- Typically you would use a BeginChild()/EndChild() pair to benefit from a clipping region + own scrolling.
-- Here we demonstrate that this can be replaced by simple offsetting + custom drawing + PushClipRect/PopClipRect() calls.
-- To use a child window instead we could use, e.g:
-- ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, ImVec2(0, 0)) -- Disable padding
-- ImGui.PushStyleColor(ImGuiCol.ChildBg, IM_COL32(50, 50, 50, 255)) -- Set a background color
-- ImGui.BeginChild("canvas", ImVec2(0.0, 0.0), ImGuiChildFlags.Borders, ImGuiWindowFlags.NoMove)
-- ImGui.PopStyleColor()
-- ImGui.PopStyleVar()
-- [...]
-- ImGui.EndChild()
-- Using InvisibleButton() as a convenience 1) it will advance the layout cursor and 2) allows us to use IsItemHovered()/IsItemActive()
local canvas_p0 = ImGui.GetCursorScreenPos() -- ImDrawList API uses screen coordinates!
local canvas_sz = ImGui.GetContentRegionAvail() -- Resize canvas to what's available
if canvas_sz.x < 50.0 then canvas_sz.x = 50.0 end
if canvas_sz.y < 50.0 then canvas_sz.y = 50.0 end
local canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y)
-- Draw border and background color
local io = ImGui.GetIO()
local draw_list = ImGui.GetWindowDrawList()
draw_list:AddRectFilled(canvas_p0, canvas_p1, IM_COL32(50, 50, 50, 255))
draw_list:AddRect(canvas_p0, canvas_p1, IM_COL32(255, 255, 255, 255))
-- This will catch our interactions
ImGui.InvisibleButton("canvas", canvas_sz, bit32.bor(1 --[[ImGuiButtonFlags.MouseButtonLeft]], 2 --[[ImGuiButtonFlags.MouseButtonRight]]))
local is_hovered = ImGui.IsItemHovered() -- Hovered
local is_active = ImGui.IsItemActive() -- Held
local origin = ImVec2(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y) -- Lock scrolled origin
local mouse_pos_in_canvas = ImVec2(io.MousePos.x - origin.x, io.MousePos.y - origin.y)
-- Add first and second point
if is_hovered and not s.adding_line and ImGui.IsMouseClicked(0 --[[ImGuiMouseButton.Left]]) then
points:push_back(ImVec2(mouse_pos_in_canvas.x, mouse_pos_in_canvas.y)) -- [Port] copies: ImVector stores references
points:push_back(ImVec2(mouse_pos_in_canvas.x, mouse_pos_in_canvas.y))
s.adding_line = true
end
if s.adding_line then
ImVec2_Copy(points:back(), mouse_pos_in_canvas)
if not ImGui.IsMouseDown(0 --[[ImGuiMouseButton.Left]]) then
s.adding_line = false
end
end
-- Pan (we use a zero mouse threshold when there's no context menu)
-- You may decide to make that threshold dynamic based on whether the mouse is hovering something etc.
local mouse_threshold_for_pan = s.opt_enable_context_menu and -1.0 or 0.0
if is_active and ImGui.IsMouseDragging(1 --[[ImGuiMouseButton.Right]], mouse_threshold_for_pan) then
scrolling.x = scrolling.x + io.MouseDelta.x
scrolling.y = scrolling.y + io.MouseDelta.y
end
-- Context menu (under default mouse threshold)
local drag_delta = ImGui.GetMouseDragDelta(1 --[[ImGuiMouseButton.Right]])
if s.opt_enable_context_menu and drag_delta.x == 0.0 and drag_delta.y == 0.0 then
ImGui.OpenPopupOnItemClick("context", 8 --[[ImGuiPopupFlags.MouseButtonRight]])
end
if ImGui.BeginPopup("context") then
if s.adding_line then
points:resize(points.Size - 2)
end
s.adding_line = false
if ImGui.MenuItem("Remove one", nil, false, points.Size > 0) then points:resize(points.Size - 2) end
if ImGui.MenuItem("Remove all", nil, false, points.Size > 0) then points:clear() end
ImGui.EndPopup()
end
-- Draw grid + all lines in the canvas
draw_list:PushClipRect(canvas_p0, canvas_p1, true)
if s.opt_enable_grid then
local GRID_STEP = 64.0
local x = math.fmod(scrolling.x, GRID_STEP)
while x < canvas_sz.x do
draw_list:AddLineV(canvas_p0.x + x, canvas_p0.y, canvas_p1.y, IM_COL32(200, 200, 200, 40))
x = x + GRID_STEP
end
local y = math.fmod(scrolling.y, GRID_STEP)
while y < canvas_sz.y do
draw_list:AddLineH(canvas_p0.x, canvas_p1.x, canvas_p0.y + y, IM_COL32(200, 200, 200, 40))
y = y + GRID_STEP
end
end
for n = 0, points.Size - 1, 2 do
draw_list:AddLine(ImVec2(origin.x + points.Data[n + 1].x, origin.y + points.Data[n + 1].y), ImVec2(origin.x + points.Data[n + 2].x, origin.y + points.Data[n + 2].y), IM_COL32(255, 255, 0, 255), 2.0)
end
draw_list:PopClipRect()
ImGui.EndTabItem()
end
if ImGui.BeginTabItem("BG/FG draw lists") then
IMGUI_DEMO_MARKER("Examples/Custom rendering/BG & FG draw lists")
_, s.draw_bg = ImGui.Checkbox("Draw in Background draw list", s.draw_bg)
ImGui.SameLine(); HelpMarker("The Background draw list will be rendered below every Dear ImGui windows.")
_, s.draw_fg = ImGui.Checkbox("Draw in Foreground draw list", s.draw_fg)
ImGui.SameLine(); HelpMarker("The Foreground draw list will be rendered over every Dear ImGui windows.")
local window_pos = ImGui.GetWindowPos()
local window_size = ImGui.GetWindowSize()
local window_center = ImVec2(window_pos.x + window_size.x * 0.5, window_pos.y + window_size.y * 0.5)
if s.draw_bg then
ImGui.GetBackgroundDrawList():AddCircle(window_center, window_size.x * 0.6, IM_COL32(255, 0, 0, 200), 0, 10 + 4)
end
if s.draw_fg then
ImGui.GetForegroundDrawList():AddCircle(window_center, window_size.y * 0.6, IM_COL32(0, 255, 0, 200), 0, 10)
end
ImGui.EndTabItem()
end
-- Demonstrate out-of-order rendering via channels splitting
-- We use functions in ImDrawList as each draw list contains a convenience splitter,
-- but you can also instantiate your own ImDrawListSplitter if you need to nest them.
if ImGui.BeginTabItem("Draw Channels") then
IMGUI_DEMO_MARKER("Examples/Custom rendering/Draw Channels")
local draw_list = ImGui.GetWindowDrawList()
do
ImGui.Text("Blue shape is drawn first: appears in back")
ImGui.Text("Red shape is drawn after: appears in front")
local p0 = ImGui.GetCursorScreenPos()
draw_list:AddRectFilled(ImVec2(p0.x, p0.y), ImVec2(p0.x + 50, p0.y + 50), IM_COL32(0, 0, 255, 255)) -- Blue
draw_list:AddRectFilled(ImVec2(p0.x + 25, p0.y + 25), ImVec2(p0.x + 75, p0.y + 75), IM_COL32(255, 0, 0, 255)) -- Red
ImGui.Dummy(ImVec2(75, 75))
end
ImGui.Separator()
do
ImGui.Text("Blue shape is drawn first, into channel 1: appears in front")
ImGui.Text("Red shape is drawn after, into channel 0: appears in back")
local p1 = ImGui.GetCursorScreenPos()
-- Create 2 channels and draw a Blue shape THEN a Red shape.
-- You can create any number of channels. Tables API use 1 channel per column in order to better batch draw calls.
draw_list:ChannelsSplit(2)
draw_list:ChannelsSetCurrent(1)
draw_list:AddRectFilled(ImVec2(p1.x, p1.y), ImVec2(p1.x + 50, p1.y + 50), IM_COL32(0, 0, 255, 255)) -- Blue
draw_list:ChannelsSetCurrent(0)
draw_list:AddRectFilled(ImVec2(p1.x + 25, p1.y + 25), ImVec2(p1.x + 75, p1.y + 75), IM_COL32(255, 0, 0, 255)) -- Red
-- Flatten/reorder channels. Red shape is in channel 0 and it appears below the Blue shape in channel 1.
-- This works by copying draw indices only (vertices are not copied).
draw_list:ChannelsMerge()
ImGui.Dummy(ImVec2(75, 75))
ImGui.Text("After reordering, contents of channel 0 appears below channel 1.")
end
ImGui.EndTabItem()
end
ImGui.EndTabBar()
end
ImGui.End()
return p_open
end
-------------------------------------------------------------------------------
-- [SECTION] Example App: Docking, DockSpace / ShowExampleAppDockSpace()
-------------------------------------------------------------------------------
function ImGuiDemoDockspaceArgs()
return {
IsFullscreen = true,
KeepWindowPadding = false, -- Keep WindowPadding to help understand that DockSpace() is a widget inside the window.
DockSpaceFlags = 0 --[[ImGuiDockNodeFlags.None]],
}
end
-- THIS IS A DEMO FOR ADVANCED USAGE OF DockSpace().
-- MOST REGULAR APPLICATIONS WANTING TO ALLOW DOCKING WINDOWS ON THE EDGE OF YOUR SCREEN CAN SIMPLY USE:
-- ImGui.NewFrame(); + ImGui.DockSpaceOverViewport(); -- Create a dockspace in main viewport
-- OR:
-- ImGui.NewFrame(); + ImGui.DockSpaceOverViewport(0, nil, ImGuiDockNodeFlags.PassthruCentralNode); -- Create a dockspace in main viewport, where central node is transparent.
-- Demonstrate using DockSpace() to create an explicit docking node within an existing window, with various options.
-- Read https://github.com/ocornut/imgui/wiki/Docking for details.
-- The reasons we do not use DockSpaceOverViewport() in this demo is because:
-- - (1) we allow the host window to be floating/moveable instead of filling the viewport (when args.IsFullscreen == false)
-- which is mostly to showcase the idea that DockSpace() may be submitted anywhere.
-- Also see 'Demo->Examples->Documents' for a less abstract version of this.
-- - (2) we allow the host window to have padding (when args.UsePadding == true)
-- - (3) we expose variety of other flags.
function ShowExampleAppDockSpaceAdvanced(args, p_open)
local dockspace_flags = args.DockSpaceFlags
-- We are using the ImGuiWindowFlags.NoDocking flag to make the parent window not dockable into,
-- because it would be confusing to have two docking targets within each others.
local window_flags = 524288 --[[ImGuiWindowFlags.NoDocking]]
if args.IsFullscreen then
-- Fullscreen dockspace: practically the same as calling DockSpaceOverViewport();
local viewport = ImGui.GetMainViewport()
ImGui.SetNextWindowPos(viewport.WorkPos)
ImGui.SetNextWindowSize(viewport.WorkSize)
ImGui.SetNextWindowViewport(viewport.ID)
ImGui.PushStyleVar(3 --[[ImGuiStyleVar.WindowRounding]], 0.0)
ImGui.PushStyleVar(4 --[[ImGuiStyleVar.WindowBorderSize]], 0.0)
window_flags = bit32.bor(window_flags, 1 --[[ImGuiWindowFlags.NoTitleBar]], 32 --[[ImGuiWindowFlags.NoCollapse]], 2 --[[ImGuiWindowFlags.NoResize]], 4 --[[ImGuiWindowFlags.NoMove]])
window_flags = bit32.bor(window_flags, 8192 --[[ImGuiWindowFlags.NoBringToFrontOnFocus]], 131072 --[[ImGuiWindowFlags.NoNavFocus]])
window_flags = bit32.bor(window_flags, 128 --[[ImGuiWindowFlags.NoBackground]])
else
-- Floating dockspace
dockspace_flags = bit32.band(dockspace_flags, bit32.bnot(8 --[[ImGuiDockNodeFlags.PassthruCentralNode]]))
end
-- Important: note that we proceed even if Begin() returns false (aka window is collapsed).
-- This is because we want to keep our DockSpace() active. If a DockSpace() is inactive,
-- all active windows docked into it will lose their parent and become undocked.
-- We cannot preserve the docking relationship between an active window and an inactive docking, otherwise
-- any change of dockspace/settings would lead to windows being stuck in limbo and never being visible.
if not args.KeepWindowPadding then
ImGui.PushStyleVar(2 --[[ImGuiStyleVar.WindowPadding]], ImVec2(0.0, 0.0))
end
p_open = ImGui.Begin("Window with a DockSpace", p_open, window_flags)
if not args.KeepWindowPadding then
ImGui.PopStyleVar()
end
if args.IsFullscreen then
ImGui.PopStyleVar(2)
end
-- Submit the DockSpace widget inside our window
-- - Note that the id here is different from the one used by DockSpaceOverViewport(), so docking state won't get transfered between "Basic" and "Advanced" demos.
-- - If we made the ShowExampleAppDockSpaceBasic() calculate its own ID and pass it to DockSpaceOverViewport() the ID could easily match.
local dockspace_id = ImGui.GetID("MyDockSpace")
ImGui.DockSpace(dockspace_id, ImVec2(0.0, 0.0), dockspace_flags)
ImGui.End()
return p_open
end
function ShowExampleAppDockSpaceBasic(flags)
-- Basic version which you can use in many apps:
-- e.g:
-- ImGui.DockSpaceOverViewport();
-- or:
-- ImGui.DockSpaceOverViewport(0, nil, ImGuiDockNodeFlags.PassthruCentralNode); -- Central node will be transparent
-- or:
-- local viewport = ImGui.GetMainViewport();
-- ImGui.DockSpaceOverViewport(0, viewport, ImGuiDockNodeFlags.None);
ImGui.DockSpaceOverViewport(0, nil, flags)
end
S.ShowExampleAppDockSpace = {
opt_demo_mode = 0,
opt_demo_mode_changed = false,
args = ImGuiDemoDockspaceArgs(),
}
function ShowExampleAppDockSpace(p_open)
local s = S.ShowExampleAppDockSpace
local args = s.args
if s.opt_demo_mode == 0 then
ShowExampleAppDockSpaceBasic(args.DockSpaceFlags)
else
p_open = ShowExampleAppDockSpaceAdvanced(args, p_open)
end
-- Refocus our window to minimize perceived loss of focus when changing mode (caused by the fact that each use a different window, which would not happen in a real app)
if s.opt_demo_mode_changed then
ImGui.SetNextWindowFocus()
end
p_open = ImGui.Begin("Examples: Dockspace", p_open, 1024 --[[ImGuiWindowFlags.MenuBar]])
s.opt_demo_mode_changed = false
local pressed
pressed, s.opt_demo_mode = ImGui.RadioButton("Basic demo mode", s.opt_demo_mode, 0)
s.opt_demo_mode_changed = s.opt_demo_mode_changed or pressed
pressed, s.opt_demo_mode = ImGui.RadioButton("Advanced demo mode", s.opt_demo_mode, 1)
s.opt_demo_mode_changed = s.opt_demo_mode_changed or pressed
ImGui.SeparatorText("Options")
if bit32.band(ImGui.GetIO().ConfigFlags, 128 --[[ImGuiConfigFlags.DockingEnable]]) == 0 then
ShowDockingDisabledMessage()
elseif s.opt_demo_mode == 0 then
args.DockSpaceFlags = bit32.band(args.DockSpaceFlags, 8 --[[ImGuiDockNodeFlags.PassthruCentralNode]]) -- Allowed flags
_, args.DockSpaceFlags = ImGui.CheckboxFlags("Flag: PassthruCentralNode", args.DockSpaceFlags, 8 --[[ImGuiDockNodeFlags.PassthruCentralNode]])
elseif s.opt_demo_mode == 1 then
_, args.IsFullscreen = ImGui.Checkbox("Fullscreen", args.IsFullscreen)
_, args.KeepWindowPadding = ImGui.Checkbox("Keep Window Padding", args.KeepWindowPadding)
ImGui.SameLine()
HelpMarker("This is mostly exposed to facilitate understanding that a DockSpace() is _inside_ a window.")
ImGui.BeginDisabled(args.IsFullscreen == false)
_, args.DockSpaceFlags = ImGui.CheckboxFlags("Flag: PassthruCentralNode", args.DockSpaceFlags, 8 --[[ImGuiDockNodeFlags.PassthruCentralNode]])
ImGui.EndDisabled()
_, args.DockSpaceFlags = ImGui.CheckboxFlags("Flag: NoDockingOverCentralNode", args.DockSpaceFlags, 4 --[[ImGuiDockNodeFlags.NoDockingOverCentralNode]])
_, args.DockSpaceFlags = ImGui.CheckboxFlags("Flag: NoDockingSplit", args.DockSpaceFlags, 16 --[[ImGuiDockNodeFlags.NoDockingSplit]])
_, args.DockSpaceFlags = ImGui.CheckboxFlags("Flag: NoUndocking", args.DockSpaceFlags, 128 --[[ImGuiDockNodeFlags.NoUndocking]])
_, args.DockSpaceFlags = ImGui.CheckboxFlags("Flag: NoResize", args.DockSpaceFlags, 32 --[[ImGuiDockNodeFlags.NoResize]])
_, args.DockSpaceFlags = ImGui.CheckboxFlags("Flag: AutoHideTabBar", args.DockSpaceFlags, 64 --[[ImGuiDockNodeFlags.AutoHideTabBar]])
end
-- Show demo options and help
if ImGui.BeginMenuBar() then
if ImGui.BeginMenu("Help") then
ImGui.TextUnformatted(
"This demonstrates the use of ImGui::DockSpace() which allows you to manually\ncreate a docking node _within_ another window." .. "\n" ..
"The \"Basic\" version uses the ImGui::DockSpaceOverViewport() helper. Most applications can probably use this.")
ImGui.Separator()
ImGui.TextUnformatted("When docking is enabled, you can ALWAYS dock MOST window into another! Try it now!" .. "\n" ..
"- Drag from window title bar or their tab to dock/undock." .. "\n" ..
"- Drag from window menu button (upper-left button) to undock an entire node (all windows)." .. "\n" ..
"- Hold SHIFT to disable docking (if io.ConfigDockingWithShift == false, default)" .. "\n" ..
"- Hold SHIFT to enable docking (if io.ConfigDockingWithShift == true)")
ImGui.Separator()
ImGui.TextUnformatted("More details:"); ImGui.Bullet(); ImGui.SameLine(); ImGui.TextLinkOpenURL("Docking Wiki page", "https://github.com/ocornut/imgui/wiki/Docking")
ImGui.BulletText("Read comments in ShowExampleAppDockSpace()")
ImGui.EndMenu()
end
ImGui.EndMenuBar()
end
ImGui.End()
return p_open
end
-------------------------------------------------------------------------------
-- [SECTION] Example App: Documents Handling / ShowExampleAppDocuments()
-------------------------------------------------------------------------------
-- Simplified structure to mimic a Document model
-- Name Document title -- [Port] char[32] text buffer, read with ImGui.TextBufferToString()
-- UID Unique ID (necessary as we can change title)
-- Open Set when open (we keep an array of all available documents to simplify demo code!)
-- OpenPrev Copy of Open from last update.
-- Dirty Set when the document has been modified
-- Color An arbitrary variable associated to the document
local MyDocumentMT = {}
MyDocumentMT.__index = MyDocumentMT
function MyDocument(uid, name, open, color)
if open == nil then open = true end
if color == nil then color = ImVec4(1.0, 1.0, 1.0, 1.0) end
local self = setmetatable({}, MyDocumentMT)
self.UID = uid
self.Name = ImGui.NewTextBuffer(name, 32)
self.Open = open; self.OpenPrev = open
self.Dirty = false
self.Color = color
return self
end
function MyDocumentMT:DoOpen() self.Open = true end
function MyDocumentMT:DoForceClose() self.Open = false; self.Dirty = false end
function MyDocumentMT:DoSave() self.Dirty = false end
local ExampleAppDocumentsMT = {}
ExampleAppDocumentsMT.__index = ExampleAppDocumentsMT
function ExampleAppDocuments()
local self = setmetatable({}, ExampleAppDocumentsMT)
self.Documents = ImVector()
self.CloseQueue = ImVector()
self.RenamingDoc = nil
self.RenamingStarted = false
self.Documents:push_back(MyDocument(0, "Lettuce", true, ImVec4(0.4, 0.8, 0.4, 1.0)))
self.Documents:push_back(MyDocument(1, "Eggplant", true, ImVec4(0.8, 0.5, 1.0, 1.0)))
self.Documents:push_back(MyDocument(2, "Carrot", true, ImVec4(1.0, 0.8, 0.5, 1.0)))
self.Documents:push_back(MyDocument(3, "Tomato", false, ImVec4(1.0, 0.3, 0.4, 1.0)))
self.Documents:push_back(MyDocument(4, "A Rather Long Title", false, ImVec4(0.4, 0.8, 0.8, 1.0)))
self.Documents:push_back(MyDocument(5, "Some Document", false, ImVec4(0.8, 0.8, 1.0, 1.0)))
return self
end
-- As we allow to change document name, we append a never-changing document ID so tabs are stable
function ExampleAppDocumentsMT:GetTabName(doc) -- [Port] returns the string instead of filling out_buf
return string.format("%s###doc%d", ImGui.TextBufferToString(doc.Name), doc.UID)
end
-- Display placeholder contents for the Document
function ExampleAppDocumentsMT:DisplayDocContents(doc)
ImGui.PushID(doc.UID) -- [Port] PushID(doc): no pointer IDs, UID is unique
ImGui.Text("Document \"%s\"", ImGui.TextBufferToString(doc.Name))
ImGui.PushStyleColor(0 --[[ImGuiCol.Text]], doc.Color)
ImGui.TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.")
ImGui.PopStyleColor()
ImGui.SetNextItemShortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 563 --[[ImGuiKey.R]]), 262144 --[[ImGuiInputFlags.Tooltip]])
if ImGui.Button("Rename..") then
self.RenamingDoc = doc
self.RenamingStarted = true
end
ImGui.SameLine()
ImGui.SetNextItemShortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 558 --[[ImGuiKey.M]]), 262144 --[[ImGuiInputFlags.Tooltip]])
if ImGui.Button("Modify") then
doc.Dirty = true
end
ImGui.SameLine()
ImGui.SetNextItemShortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 564 --[[ImGuiKey.S]]), 262144 --[[ImGuiInputFlags.Tooltip]])
if ImGui.Button("Save") then
doc:DoSave()
end
ImGui.SameLine()
ImGui.SetNextItemShortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 568 --[[ImGuiKey.W]]), 262144 --[[ImGuiInputFlags.Tooltip]])
if ImGui.Button("Close") then
self.CloseQueue:push_back(doc)
end
ImGui.ColorEdit3("color", doc.Color) -- Useful to test drag and drop and hold-dragged-to-open-tab behavior.
ImGui.PopID()
end
-- Display context menu for the Document
function ExampleAppDocumentsMT:DisplayDocContextMenu(doc)
if not ImGui.BeginPopupContextItem() then
return
end
local buf = string.format("Save %s", ImGui.TextBufferToString(doc.Name))
if ImGui.MenuItem(buf, "Ctrl+S", false, doc.Open) then
doc:DoSave()
end
if ImGui.MenuItem("Rename...", "Ctrl+R", false, doc.Open) then
self.RenamingDoc = doc
end
if ImGui.MenuItem("Close", "Ctrl+W", false, doc.Open) then
self.CloseQueue:push_back(doc)
end
ImGui.EndPopup()
end
-- [Optional] Notify the system of Tabs/Windows closure that happened outside the regular tab interface.
-- If a tab has been closed programmatically (aka closed from another source such as the Checkbox() in the demo,
-- as opposed to clicking on the regular tab closing button) and stops being submitted, it will take a frame for
-- the tab bar to notice its absence. During this frame there will be a gap in the tab bar, and if the tab that has
-- disappeared was the selected one, the tab bar will report no selected tab during the frame. This will effectively
-- give the impression of a flicker for one frame.
-- We call SetTabItemClosed() to manually notify the Tab Bar or Docking system of removed tabs to avoid this glitch.
-- Note that this completely optional, and only affect tab bars with the ImGuiTabBarFlags.Reorderable flag.
function ExampleAppDocumentsMT:NotifyOfDocumentsClosedElsewhere()
for _, doc in self.Documents:iter() do
if not doc.Open and doc.OpenPrev then
ImGui.SetTabItemClosed(ImGui.TextBufferToString(doc.Name))
end
doc.OpenPrev = doc.Open
end
end
S.ShowExampleAppDocuments = {
app = ExampleAppDocuments(),
opt_target = nil, -- set below to Target_Tab
opt_reorderable = true,
opt_fitting_flags = nil, -- set below to ImGuiTabBarFlags.FittingPolicyDefault_
}
function ShowExampleAppDocuments(p_open)
local s = S.ShowExampleAppDocuments
local app = s.app
-- Options
local Target_None = 0
local Target_Tab = 1 -- Create documents as local tab into a local tab bar
local Target_DockSpaceAndWindow = 2 -- Create documents as regular windows, and create an embedded dockspace
if s.opt_target == nil then s.opt_target = Target_Tab end
if s.opt_fitting_flags == nil then s.opt_fitting_flags = 128 --[[ImGuiTabBarFlags.FittingPolicyDefault_]] end
-- When (opt_target == Target_DockSpaceAndWindow) there is the possibily that one of our child Document window (e.g. "Eggplant")
-- that we emit gets docked into the same spot as the parent window ("Example: Documents").
-- This would create a problematic feedback loop because selecting the "Eggplant" tab would make the "Example: Documents" tab
-- not visible, which in turn would stop submitting the "Eggplant" window.
-- We avoid this problem by submitting our documents window even if our parent window is not currently visible.
-- Another solution may be to make the "Example: Documents" window use the ImGuiWindowFlags.NoDocking.
local window_contents_visible
p_open, window_contents_visible = ImGui.Begin("Example: Documents", p_open, 1024 --[[ImGuiWindowFlags.MenuBar]])
if not window_contents_visible and s.opt_target ~= Target_DockSpaceAndWindow then
ImGui.End()
return p_open
end
IMGUI_DEMO_MARKER("Examples/Documents")
-- Menu
if ImGui.BeginMenuBar() then
if ImGui.BeginMenu("File") then
local open_count = 0
for _, doc in app.Documents:iter() do
open_count = open_count + (doc.Open and 1 or 0)
end
if ImGui.BeginMenu("Open", open_count < app.Documents.Size) then
for _, doc in app.Documents:iter() do
if not doc.Open and ImGui.MenuItem(ImGui.TextBufferToString(doc.Name)) then
doc:DoOpen()
end
end
ImGui.EndMenu()
end
if ImGui.MenuItem("Close All Documents", nil, false, open_count > 0) then
for _, doc in app.Documents:iter() do
app.CloseQueue:push_back(doc)
end
end
if ImGui.MenuItem("Exit") and p_open ~= nil then
p_open = false
end
ImGui.EndMenu()
end
ImGui.EndMenuBar()
end
-- [Debug] List documents with one checkbox for each
for doc_n = 0, app.Documents.Size - 1 do
local doc = app.Documents.Data[doc_n + 1]
if doc_n > 0 then
ImGui.SameLine()
end
ImGui.PushID(doc.UID) -- [Port] PushID(&doc): no pointer IDs, UID is unique
local pressed
pressed, doc.Open = ImGui.Checkbox(ImGui.TextBufferToString(doc.Name), doc.Open)
if pressed then
if not doc.Open then
doc:DoForceClose()
end
end
ImGui.PopID()
end
ImGui.PushItemWidth(ImGui.GetFontSize() * 12)
s.opt_target = ImGui.Combo("Output", s.opt_target, "None\0TabBar+Tabs\0DockSpace+Window\0")
ImGui.PopItemWidth()
local redock_all = false
if s.opt_target == Target_Tab then ImGui.SameLine(); _, s.opt_reorderable = ImGui.Checkbox("Reorderable Tabs", s.opt_reorderable) end
if s.opt_target == Target_DockSpaceAndWindow then ImGui.SameLine(); redock_all = ImGui.Button("Redock all") end
ImGui.Separator()
-- About the ImGuiWindowFlags.UnsavedDocument / ImGuiTabItemFlags.UnsavedDocument flags.
-- They have multiple effects:
-- - Display a dot next to the title.
-- - Tab is selected when clicking the X close button.
-- - Closure is not assumed (will wait for user to stop submitting the tab).
-- Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.
-- We need to assume closure by default otherwise waiting for "lack of submission" on the next frame would leave an empty
-- hole for one-frame, both in the tab-bar and in tab-contents when closing a tab/window.
-- The rarely used SetTabItemClosed() function is a way to notify of programmatic closure to avoid the one-frame hole.
-- Tabs
if s.opt_target == Target_Tab then
local tab_bar_flags = bit32.bor(s.opt_fitting_flags, s.opt_reorderable and 1 --[[ImGuiTabBarFlags.Reorderable]] or 0)
tab_bar_flags = bit32.bor(tab_bar_flags, 64 --[[ImGuiTabBarFlags.DrawSelectedOverline]])
if ImGui.BeginTabBar("##tabs", tab_bar_flags) then
if s.opt_reorderable then
app:NotifyOfDocumentsClosedElsewhere()
end
-- [DEBUG] Stress tests
--if ((ImGui.GetFrameCount() % 30) == 0) docs[1].Open ^= 1; -- [DEBUG] Automatically show/hide a tab. Test various interactions e.g. dragging with this on.
--if (ImGui.GetIO().KeyCtrl) ImGui.SetTabItemSelected(docs[1].Name); -- [DEBUG] Test SetTabItemSelected(), probably not very useful as-is anyway..
-- Submit Tabs
for _, doc in app.Documents:iter() do repeat
if not doc.Open then
break -- continue
end
-- As we allow to change document name, we append a never-changing document id so tabs are stable
local doc_name_buf = app:GetTabName(doc)
local tab_flags = (doc.Dirty and 1 --[[ImGuiTabItemFlags.UnsavedDocument]] or 0)
local visible
visible, doc.Open = ImGui.BeginTabItem(doc_name_buf, doc.Open, tab_flags)
-- Cancel attempt to close when unsaved add to save queue so we can display a popup.
if not doc.Open and doc.Dirty then
doc.Open = true
app.CloseQueue:push_back(doc)
end
app:DisplayDocContextMenu(doc)
if visible then
app:DisplayDocContents(doc)
ImGui.EndTabItem()
end
until true end
ImGui.EndTabBar()
end
elseif s.opt_target == Target_DockSpaceAndWindow then
if bit32.band(ImGui.GetIO().ConfigFlags, 128 --[[ImGuiConfigFlags.DockingEnable]]) ~= 0 then
app:NotifyOfDocumentsClosedElsewhere()
-- Create a DockSpace node where any window can be docked
local dockspace_id = ImGui.GetID("MyDockSpace")
ImGui.DockSpace(dockspace_id)
-- Create Windows
for doc_n = 0, app.Documents.Size - 1 do repeat
local doc = app.Documents.Data[doc_n + 1]
if not doc.Open then
break -- continue
end
ImGui.SetNextWindowDockID(dockspace_id, redock_all and 1 --[[ImGuiCond.Always]] or 4 --[[ImGuiCond.FirstUseEver]])
local window_flags = (doc.Dirty and 262144 --[[ImGuiWindowFlags.UnsavedDocument]] or 0)
local visible
doc.Open, visible = ImGui.Begin(ImGui.TextBufferToString(doc.Name), doc.Open, window_flags)
-- Cancel attempt to close when unsaved add to save queue so we can display a popup.
if not doc.Open and doc.Dirty then
doc.Open = true
app.CloseQueue:push_back(doc)
end
app:DisplayDocContextMenu(doc)
if visible then
app:DisplayDocContents(doc)
end
ImGui.End()
until true end
else
ShowDockingDisabledMessage()
end
end
-- Early out other contents
if not window_contents_visible then
ImGui.End()
return p_open
end
-- Display renaming UI
if app.RenamingDoc ~= nil then
if app.RenamingStarted then
ImGui.OpenPopup("Rename")
end
if ImGui.BeginPopup("Rename") then
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 30)
if ImGui.InputText("###Name", app.RenamingDoc.Name, 32, 64 --[[ImGuiInputTextFlags.EnterReturnsTrue]]) then
ImGui.CloseCurrentPopup()
app.RenamingDoc = nil
end
if app.RenamingStarted then
ImGui.SetKeyboardFocusHere(-1)
end
ImGui.EndPopup()
else
app.RenamingDoc = nil
end
app.RenamingStarted = false
end
-- Display closing confirmation UI
if not app.CloseQueue:empty() then
local close_queue_unsaved_documents = 0
for n = 0, app.CloseQueue.Size - 1 do
if app.CloseQueue.Data[n + 1].Dirty then
close_queue_unsaved_documents = close_queue_unsaved_documents + 1
end
end
if close_queue_unsaved_documents == 0 then
-- Close documents when all are unsaved
for n = 0, app.CloseQueue.Size - 1 do
app.CloseQueue.Data[n + 1]:DoForceClose()
end
app.CloseQueue:clear()
else
if not ImGui.IsPopupOpen("Save?") then
ImGui.OpenPopup("Save?")
end
if ImGui.BeginPopupModal("Save?", nil, 64 --[[ImGuiWindowFlags.AlwaysAutoResize]]) then
ImGui.Text("Save change to the following items?")
local item_height = ImGui.GetTextLineHeightWithSpacing()
if ImGui.BeginChild(ImGui.GetID("frame"), ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], 6.25 * item_height), 128 --[[ImGuiChildFlags.FrameStyle]]) then
for _, doc in app.CloseQueue:iter() do
if doc.Dirty then
ImGui.Text("%s", ImGui.TextBufferToString(doc.Name))
end
end
end
ImGui.EndChild()
local button_size = ImVec2(ImGui.GetFontSize() * 7.0, 0.0)
if ImGui.Button("Yes", button_size) then
for _, doc in app.CloseQueue:iter() do
if doc.Dirty then
doc:DoSave()
end
doc:DoForceClose()
end
app.CloseQueue:clear()
ImGui.CloseCurrentPopup()
end
ImGui.SameLine()
if ImGui.Button("No", button_size) then
for _, doc in app.CloseQueue:iter() do
doc:DoForceClose()
end
app.CloseQueue:clear()
ImGui.CloseCurrentPopup()
end
ImGui.SameLine()
if ImGui.Button("Cancel", button_size) then
app.CloseQueue:clear()
ImGui.CloseCurrentPopup()
end
ImGui.EndPopup()
end
end
end
ImGui.End()
return p_open
end
-------------------------------------------------------------------------------
-- [SECTION] Example App: Assets Browser / ShowExampleAppAssetsBrowser()
-------------------------------------------------------------------------------
--#include "imgui_internal.h" -- NavMoveRequestTryWrapping()
function ExampleAsset(id, type_)
local self = {}
self.ID = id -- ImGuiID
self.Type = type_ -- int
return self
end
ExampleAsset_s_current_sort_specs = nil
-- [Port] items: 1-based Lua array (ImVector .Data), sorted in place over [1, items_count].
local function ExampleAsset_Less(a, b) return ExampleAsset_CompareWithSortSpecs(a, b) < 0 end -- [Port] table.sort wants a "less than" predicate
function ExampleAsset_SortWithSortSpecs(sort_specs, items, items_count)
ExampleAsset_s_current_sort_specs = sort_specs -- Store in variable accessible by the sort function.
if items_count > 1 then
if items_count == #items then
table.sort(items, ExampleAsset_Less)
else -- [Port] sort only the used range
local tmp = {}
for i = 1, items_count do tmp[i] = items[i] end
table.sort(tmp, ExampleAsset_Less)
for i = 1, items_count do items[i] = tmp[i] end
end
end
ExampleAsset_s_current_sort_specs = nil
end
-- Compare function to be used by qsort()
function ExampleAsset_CompareWithSortSpecs(a, b)
for n = 0, ExampleAsset_s_current_sort_specs.SpecsCount - 1 do
local sort_spec = ExampleAsset_s_current_sort_specs.Specs[n + 1] -- [Port] Specs is a 1-based array
local delta = 0
if sort_spec.ColumnIndex == 0 then
delta = (a.ID - b.ID)
elseif sort_spec.ColumnIndex == 1 then
delta = (a.Type - b.Type)
end
if delta > 0 then
return (sort_spec.SortDirection == 1 --[[ImGuiSortDirection.Ascending]]) and 1 or -1
end
if delta < 0 then
return (sort_spec.SortDirection == 1 --[[ImGuiSortDirection.Ascending]]) and -1 or 1
end
end
return a.ID - b.ID
end
local ExampleAssetsBrowserMT = {}
ExampleAssetsBrowserMT.__index = ExampleAssetsBrowserMT
function ExampleAssetsBrowser()
local self = setmetatable({}, ExampleAssetsBrowserMT)
-- Options
self.ShowTypeOverlay = true
self.AllowSorting = true
self.AllowBoxSelect = true -- Will set ImGuiMultiSelectFlags.BoxSelect2d
self.AllowBoxSelectInsideSelection = false -- Will set ImGuiMultiSelectFlags.SelectOnClickAlways
self.AllowDragUnselected = false -- Will set ImGuiMultiSelectFlags.SelectOnClickRelease
self.IconSize = 0
self.IconSpacing = 10
self.IconHitSpacing = 4 -- Increase hit-spacing if you want to make it possible to clear or box-select from gaps. Some spacing is required to able to amend with Shift+box-select. Value is small in Explorer.
self.StretchSpacing = true
self.UseScrollX = false -- Debug: submit twice the number of items per line (overflow horizontally to exercise ScrollX + box-select)
-- State
self.Items = ImVector() -- Our items
self.Selection = ExampleSelectionWithDeletion() -- Our selection (ImGuiSelectionBasicStorage + helper funcs to handle deletion)
self.NextItemId = 0 -- Unique identifier when creating new items
self.RequestDelete = false -- Deferred deletion request
self.RequestSort = false -- Deferred sort request
self.ZoomWheelAccum = 0.0 -- Mouse wheel accumulator to handle smooth wheels better
-- Calculated sizes for layout, output of UpdateLayoutSizes(). Could be locals but our code is simpler this way.
self.LayoutItemSize = ImVec2()
self.LayoutItemStep = ImVec2() -- == LayoutItemSize + LayoutItemSpacing
self.LayoutItemSpacing = 0.0
self.LayoutSelectableSpacing = 0.0
self.LayoutOuterPadding = 0.0
self.LayoutColumnCount = 0
self.LayoutLineCount = 0
-- Functions
self:AddItems(10000)
return self
end
function ExampleAssetsBrowserMT:AddItems(count)
if self.Items.Size == 0 then
self.NextItemId = 0
end
self.Items:reserve(self.Items.Size + count)
for n = 0, count - 1 do
self.Items:push_back(ExampleAsset(self.NextItemId, (self.NextItemId % 20) < 15 and 0 or ((self.NextItemId % 20) < 18 and 1 or 2)))
self.NextItemId = self.NextItemId + 1
end
self.RequestSort = true
end
function ExampleAssetsBrowserMT:ClearItems()
self.Items:clear()
self.Selection:Clear()
end
-- Logic would be written in the main code BeginChild() and outputting to local variables.
-- We extracted it into a function so we can call it easily from multiple places.
function ExampleAssetsBrowserMT:UpdateLayoutSizes(avail_width)
-- Layout: when not stretching: allow extending into right-most spacing.
self.LayoutItemSpacing = self.IconSpacing
if self.StretchSpacing == false then
avail_width = avail_width + math.floor(self.LayoutItemSpacing * 0.5)
end
-- Layout: calculate number of icon per line and number of lines
self.LayoutItemSize = ImVec2(math.floor(self.IconSize), math.floor(self.IconSize))
self.LayoutColumnCount = ImMax(ImTrunc(avail_width / (self.LayoutItemSize.x + self.LayoutItemSpacing)), 1)
-- Layout: when stretching: allocate remaining space to more spacing. Round before division, so item_spacing may be non-integer.
if self.StretchSpacing and self.LayoutColumnCount > 1 then
self.LayoutItemSpacing = math.floor(avail_width - self.LayoutItemSize.x * self.LayoutColumnCount) / self.LayoutColumnCount
end
if self.UseScrollX then
self.LayoutColumnCount = self.LayoutColumnCount * 2
end
self.LayoutLineCount = math.floor((self.Items.Size + self.LayoutColumnCount - 1) / self.LayoutColumnCount)
self.LayoutItemStep = ImVec2(self.LayoutItemSize.x + self.LayoutItemSpacing, self.LayoutItemSize.y + self.LayoutItemSpacing)
self.LayoutSelectableSpacing = ImMax(math.floor(self.LayoutItemSpacing) - self.IconHitSpacing, 0.0)
self.LayoutOuterPadding = math.floor(self.LayoutItemSpacing * 0.5)
end
-- [Port] the non-capturing C++ lambda assigned every frame, hoisted to a single function (no per-frame closure)
local function ExampleAssetsBrowser_AdapterIndexToStorageId(self_, idx) local self = self_.UserData; return self.Items.Data[idx + 1].ID end
-- [Port] returns the new p_open
function ExampleAssetsBrowserMT:Draw(title, p_open)
if self.IconSize <= 0.0 then
self.IconSize = ImGui.CalcTextSize("99999").x
end
ImGui.SetNextWindowSize(ImVec2(self.IconSize * 25, self.IconSize * 15), 4 --[[ImGuiCond.FirstUseEver]])
local visible
p_open, visible = ImGui.Begin(title, p_open, 1024 --[[ImGuiWindowFlags.MenuBar]])
if not visible then
ImGui.End()
return p_open
end
IMGUI_DEMO_MARKER("Examples/Assets Browser")
local Selection = self.Selection
-- Menu bar
if ImGui.BeginMenuBar() then
if ImGui.BeginMenu("File") then
if ImGui.MenuItem("Add 10000 items") then
self:AddItems(10000)
end
if ImGui.MenuItem("Clear items") then
self:ClearItems()
end
ImGui.Separator()
if ImGui.MenuItem("Close", nil, false, p_open ~= nil) then
p_open = false
end
ImGui.EndMenu()
end
if ImGui.BeginMenu("Edit") then
if ImGui.MenuItem("Delete", "Del", false, Selection.Size > 0) then
self.RequestDelete = true
end
ImGui.EndMenu()
end
if ImGui.BeginMenu("Options") then
ImGui.PushItemWidth(ImGui.GetFontSize() * 10)
ImGui.SeparatorText("Contents")
_, self.ShowTypeOverlay = ImGui.Checkbox("Show Type Overlay", self.ShowTypeOverlay)
_, self.AllowSorting = ImGui.Checkbox("Allow Sorting", self.AllowSorting)
ImGui.SeparatorText("Selection Behavior")
_, self.AllowBoxSelect = ImGui.Checkbox("Allow box-selection", self.AllowBoxSelect)
local pressed
pressed, self.AllowBoxSelectInsideSelection = ImGui.Checkbox("Allow box-selection from selected items", self.AllowBoxSelectInsideSelection)
if pressed and self.AllowBoxSelectInsideSelection then
self.AllowDragUnselected = false
end
pressed, self.AllowDragUnselected = ImGui.Checkbox("Allow dragging unselected item", self.AllowDragUnselected)
if pressed and self.AllowDragUnselected then
self.AllowBoxSelectInsideSelection = false
end
ImGui.SeparatorText("Layout")
self.IconSize = ImGui.SliderFloat("Icon Size", self.IconSize, 16.0, 128.0, "%.0f")
ImGui.SameLine(); HelpMarker("Use Ctrl+Wheel to zoom")
self.IconSpacing = ImGui.SliderInt("Icon Spacing", self.IconSpacing, 0, 32)
self.IconHitSpacing = ImGui.SliderInt("Icon Hit Spacing", self.IconHitSpacing, 0, 32)
_, self.StretchSpacing = ImGui.Checkbox("Stretch Spacing", self.StretchSpacing)
_, self.UseScrollX = ImGui.Checkbox("Use ScrollX", self.UseScrollX)
ImGui.PopItemWidth()
ImGui.EndMenu()
end
ImGui.EndMenuBar()
end
-- Show a table with ONLY one header row to showcase the idea/possibility of using this to provide a sorting UI
if self.AllowSorting then
ImGui.PushStyleVar(14 --[[ImGuiStyleVar.ItemSpacing]], ImVec2(0, 0))
local table_flags_for_sort_specs = bit32.bor(8 --[[ImGuiTableFlags.Sortable]], 67108864 --[[ImGuiTableFlags.SortMulti]], 8192 --[[ImGuiTableFlags.SizingFixedFit]], 1920 --[[ImGuiTableFlags.Borders]])
if ImGui.BeginTable("for_sort_specs_only", 2, table_flags_for_sort_specs, ImVec2(0.0, ImGui.GetFrameHeight())) then
ImGui.TableSetupColumn("Index")
ImGui.TableSetupColumn("Type")
ImGui.TableHeadersRow()
local sort_specs = ImGui.TableGetSortSpecs()
if sort_specs then
if sort_specs.SpecsDirty or self.RequestSort then
ExampleAsset_SortWithSortSpecs(sort_specs, self.Items.Data, self.Items.Size)
sort_specs.SpecsDirty = false; self.RequestSort = false
end
end
ImGui.EndTable()
end
ImGui.PopStyleVar()
end
local io = ImGui.GetIO()
ImGui.SetNextWindowContentSize(ImVec2(0.0, self.LayoutOuterPadding + self.LayoutLineCount * (self.LayoutItemSize.y + self.LayoutItemSpacing)))
if ImGui.BeginChild("Assets", ImVec2(0.0, -ImGui.GetTextLineHeightWithSpacing()), 1 --[[ImGuiChildFlags.Borders]], bit32.bor(4 --[[ImGuiWindowFlags.NoMove]], 2048 --[[ImGuiWindowFlags.HorizontalScrollbar]])) then
local draw_list = ImGui.GetWindowDrawList()
local avail_width = ImGui.GetContentRegionAvail().x
self:UpdateLayoutSizes(avail_width)
-- Calculate and store start position.
local start_pos = ImGui.GetCursorScreenPos()
start_pos = ImVec2(start_pos.x + self.LayoutOuterPadding, start_pos.y + self.LayoutOuterPadding)
ImGui.SetCursorScreenPos(start_pos)
-- Multi-select
local ms_flags = bit32.bor(512 --[[ImGuiMultiSelectFlags.ClearOnEscape]], 1024 --[[ImGuiMultiSelectFlags.ClearOnClickVoid]])
-- - Enable box-select (in 2D mode, so that changing box-select rectangle X1/X2 boundaries will affect clipped items)
if self.AllowBoxSelect then
ms_flags = bit32.bor(ms_flags, 128 --[[ImGuiMultiSelectFlags.BoxSelect2d]])
end
-- - Selection mode
if self.AllowDragUnselected then
ms_flags = bit32.bor(ms_flags, 32768 --[[ImGuiMultiSelectFlags.SelectOnClickRelease]]) -- Rarely used: Allows dragging an unselected item without selecting it(rarely used)
elseif self.AllowBoxSelectInsideSelection then
ms_flags = bit32.bor(ms_flags, 16384 --[[ImGuiMultiSelectFlags.SelectOnClickAlways]]) -- Rarely used: Prevents Drag and Drop from being used on multiple-selection, but allows e.g. BoxSelect to always reselect even when clicking inside an existing selection.
end
-- - Enable keyboard wrapping on X axis
-- (FIXME-MULTISELECT: We haven't designed/exposed a general nav wrapping api yet, so this flag is provided as a courtesy to avoid doing:
-- ImGui.NavMoveRequestTryWrapping(ImGui.GetCurrentWindow(), ImGuiNavMoveFlags.WrapX);
-- When we finish implementing a more general API for this, we will obsolete this flag in favor of the new system)
ms_flags = bit32.bor(ms_flags, 65536 --[[ImGuiMultiSelectFlags.NavWrapX]])
local ms_io = ImGui.BeginMultiSelect(ms_flags, Selection.Size, self.Items.Size)
-- Use custom selection adapter: store ID in selection (recommended)
Selection.UserData = self
Selection.AdapterIndexToStorageId = ExampleAssetsBrowser_AdapterIndexToStorageId
Selection:ApplyRequests(ms_io)
local want_delete = (ImGui.Shortcut(522 --[[ImGuiKey.Delete]], 1 --[[ImGuiInputFlags.Repeat]]) and (Selection.Size > 0)) or self.RequestDelete
local item_curr_idx_to_focus = want_delete and Selection:ApplyDeletionPreLoop(ms_io, self.Items.Size) or -1
self.RequestDelete = false
-- Push LayoutSelectableSpacing (which is LayoutItemSpacing minus hit-spacing, if we decide to have hit gaps between items)
-- Altering style ItemSpacing may seem unnecessary as we position every items using SetCursorScreenPos()...
-- But it is necessary for two reasons:
-- - Selectables uses it by default to visually fill the space between two items.
-- - The vertical spacing would be measured by Clipper to calculate line height if we didn't provide it explicitly (here we do).
ImGui.PushStyleVar(14 --[[ImGuiStyleVar.ItemSpacing]], ImVec2(self.LayoutSelectableSpacing, self.LayoutSelectableSpacing))
-- Rendering parameters
local icon_type_overlay_colors = { 0, IM_COL32(200, 70, 70, 255), IM_COL32(70, 170, 70, 255) }
local icon_bg_color = ImGui.GetColorU32(IM_COL32(35, 35, 35, 220), nil, true) -- [Port] ImU32 overload selected explicitly
local icon_type_overlay_size = ImVec2(4.0, 4.0)
local display_label = (self.LayoutItemSize.x >= ImGui.CalcTextSize("999").x)
local column_count = self.LayoutColumnCount
local clipper = ImGuiListClipper()
clipper:Begin(self.LayoutLineCount, self.LayoutItemStep.y)
if item_curr_idx_to_focus ~= -1 then
clipper:IncludeItemByIndex(math.floor(item_curr_idx_to_focus / column_count)) -- Ensure focused item line is not clipped.
end
if ms_io.RangeSrcItem ~= -1 then
clipper:IncludeItemByIndex(math.floor(ms_io.RangeSrcItem / column_count)) -- Ensure RangeSrc item line is not clipped.
end
while clipper:Step() do
for line_idx = clipper.DisplayStart, clipper.DisplayEnd - 1 do
local item_min_idx_for_current_line = line_idx * column_count
local item_max_idx_for_current_line = ImMin((line_idx + 1) * column_count, self.Items.Size)
for item_idx = item_min_idx_for_current_line, item_max_idx_for_current_line - 1 do
local item_data = self.Items.Data[item_idx + 1]
ImGui.PushID(item_data.ID)
-- Position item
local pos = ImVec2(start_pos.x + (item_idx % column_count) * self.LayoutItemStep.x, start_pos.y + line_idx * self.LayoutItemStep.y)
ImGui.SetCursorScreenPos(pos)
ImGui.SetNextItemSelectionUserData(item_idx)
local item_is_selected = Selection:Contains(item_data.ID)
local item_is_visible = ImGui.IsRectVisible(self.LayoutItemSize)
ImGui.Selectable("", item_is_selected, 0 --[[ImGuiSelectableFlags.None]], self.LayoutItemSize)
-- Update our selection state immediately (without waiting for EndMultiSelect() requests)
-- because we use this to alter the color of our text/icon.
if ImGui.IsItemToggledSelection() then
item_is_selected = not item_is_selected
end
-- Focus (for after deletion)
if item_curr_idx_to_focus == item_idx then
ImGui.SetKeyboardFocusHere(-1)
end
-- Drag and drop
if ImGui.BeginDragDropSource() then
-- Create payload with full selection OR single unselected item.
-- (the later is only possible when using ImGuiMultiSelectFlags.SelectOnClickRelease)
if ImGui.GetDragDropPayload() == nil then
local payload_items = ImVector()
local it = nil
local id = 0
if not item_is_selected then
payload_items:push_back(item_data.ID)
else
while true do
local has_more
has_more, it, id = Selection:GetNextSelectedItem(it)
if not has_more then break end
payload_items:push_back(id)
end
end
ImGui.SetDragDropPayload("ASSETS_BROWSER_ITEMS", payload_items.Data, payload_items.Size * 4) -- [Port] size_in_bytes() with sizeof(ImGuiID) == 4, payload carries the Lua array
end
-- Display payload content in tooltip, by extracting it from the payload data
-- (we could read from selection, but it is more correct and reusable to read from payload)
local payload = ImGui.GetDragDropPayload()
local payload_count = math.floor(payload.DataSize / 4) -- [Port] sizeof(ImGuiID) == 4
ImGui.Text("%d assets", payload_count)
ImGui.EndDragDropSource()
end
-- Render icon (a real app would likely display an image/thumbnail here)
-- Because we use ImGuiMultiSelectFlags.BoxSelect2d, clipping vertical may occasionally be larger, so we coarse-clip our rendering as well.
if item_is_visible then
local box_min = ImVec2(pos.x - 1, pos.y - 1)
local box_max = ImVec2(box_min.x + self.LayoutItemSize.x + 2, box_min.y + self.LayoutItemSize.y + 2) -- Dubious
draw_list:AddRectFilled(box_min, box_max, icon_bg_color) -- Background color
if self.ShowTypeOverlay and item_data.Type ~= 0 then
local type_col = icon_type_overlay_colors[(item_data.Type % #icon_type_overlay_colors) + 1]
draw_list:AddRectFilled(ImVec2(box_max.x - 2 - icon_type_overlay_size.x, box_min.y + 2), ImVec2(box_max.x - 2, box_min.y + 2 + icon_type_overlay_size.y), type_col)
end
if display_label then
local label_col = ImGui.GetColorU32(item_is_selected and 0 --[[ImGuiCol.Text]] or 1 --[[ImGuiCol.TextDisabled]])
local label = string.format("%d", item_data.ID)
draw_list:AddText(ImVec2(box_min.x, box_max.y - ImGui.GetFontSize()), label_col, label)
end
end
ImGui.PopID()
end
end
end
clipper:End()
if self.Items.Size == 0 then
ImGui.Dummy(ImVec2(0, 0))
end
ImGui.PopStyleVar() -- ImGuiStyleVar.ItemSpacing
-- Context menu
if ImGui.BeginPopupContextWindow() then
ImGui.Text("Selection: %d items", Selection.Size)
ImGui.Separator()
if ImGui.MenuItem("Delete", "Del", false, Selection.Size > 0) then
self.RequestDelete = true
end
ImGui.EndPopup()
end
ms_io = ImGui.EndMultiSelect()
Selection:ApplyRequests(ms_io)
if want_delete then
Selection:ApplyDeletionPostLoop(ms_io, self.Items, item_curr_idx_to_focus)
end
-- Zooming with Ctrl+Wheel
if ImGui.IsWindowAppearing() then
self.ZoomWheelAccum = 0.0
end
if ImGui.IsWindowHovered() and io.MouseWheel ~= 0.0 and ImGui.IsKeyDown(4096 --[[ImGuiMod_Ctrl]]) and ImGui.IsAnyItemActive() == false then
self.ZoomWheelAccum = self.ZoomWheelAccum + io.MouseWheel
if math.abs(self.ZoomWheelAccum) >= 1.0 then
-- Calculate hovered item index from mouse location
-- FIXME: Locking aiming on 'hovered_item_idx' (with a cool-down timer) would ensure zoom keeps on it.
local hovered_item_nx = (io.MousePos.x - start_pos.x + self.LayoutItemSpacing * 0.5) / self.LayoutItemStep.x
local hovered_item_ny = (io.MousePos.y - start_pos.y + self.LayoutItemSpacing * 0.5) / self.LayoutItemStep.y
local hovered_item_idx = (ImTrunc(hovered_item_ny) * self.LayoutColumnCount) + ImTrunc(hovered_item_nx)
--ImGui.SetTooltip("%f,%f -> item %d", hovered_item_nx, hovered_item_ny, hovered_item_idx); -- Move those 4 lines in block above for easy debugging
-- Zoom
self.IconSize = self.IconSize * (1.1 ^ ImTrunc(self.ZoomWheelAccum))
self.IconSize = ImClamp(self.IconSize, 16.0, 128.0)
self.ZoomWheelAccum = self.ZoomWheelAccum - ImTrunc(self.ZoomWheelAccum)
self:UpdateLayoutSizes(avail_width)
-- Manipulate scroll to that we will land at the same Y location of currently hovered item.
-- - Calculate next frame position of item under mouse
-- - Set new scroll position to be used in next ImGui.BeginChild() call.
local hovered_item_rel_pos_y = (ImTrunc(hovered_item_idx / self.LayoutColumnCount) + math.fmod(hovered_item_ny, 1.0)) * self.LayoutItemStep.y
hovered_item_rel_pos_y = hovered_item_rel_pos_y + ImGui.GetStyle().WindowPadding.y
local mouse_local_y = io.MousePos.y - ImGui.GetWindowPos().y
ImGui.SetScrollY(hovered_item_rel_pos_y - mouse_local_y)
end
end
end
ImGui.EndChild()
ImGui.Text("Selected: %d/%d items", Selection.Size, self.Items.Size)
ImGui.End()
return p_open
end
S.ShowExampleAppAssetsBrowser = {
assets_browser = nil, -- static ExampleAssetsBrowser assets_browser; -- [Port] created lazily on first call (10000 items)
}
function ShowExampleAppAssetsBrowser(p_open)
IMGUI_DEMO_MARKER("Examples/Assets Browser")
local s = S.ShowExampleAppAssetsBrowser
if s.assets_browser == nil then s.assets_browser = ExampleAssetsBrowser() end
return s.assets_browser:Draw("Example: Assets Browser", p_open)
end
return true
end
__FILES['imgui_draw.lua'] = function()
--- Dear ImGui WIP
-- (drawing and font code)
--
-- Index of this file:
--
-- [SECTION] STB libraries implementation
-- [SECTION] Style functions
-- [SECTION] ImDrawList
-- [SECTION] ImTriangulator, ImDrawList concave polygon fill
-- [SECTION] ImDrawListSplitter
-- [SECTION] ImDrawData
-- [SECTION] Helpers ShadeVertsXXX functions
-- [SECTION] ImFontConfig
-- [SECTION] ImFontAtlas, ImFontAtlasBuilder
-- [SECTION] ImFontAtlas: backend for stb_truetype
-- [SECTION] ImFontAtlas: glyph ranges helpers
-- [SECTION] ImFontGlyphRangesBuilder
-- [SECTION] ImFontBaked, ImFont
-- [SECTION] ImGui Internal Render Helpers
-- [SECTION] Decompression code
-- [SECTION] Default font data (ProggyClean.ttf)
-- [SECTION] Default font data (ProggyForever.ttf)
--
FONT_ATLAS_DEFAULT_TEX_DATA_W = 122 -- Actual texture will be 2 times that + 1 spacing.
FONT_ATLAS_DEFAULT_TEX_DATA_H = 27
local IM_DRAWLIST_TEX_LINES_WIDTH_MAX = 32 -- The maximum line width to bake anti-aliased textures for.
local writef64, writeu32, readf64, readu32 = buffer.writef64, buffer.writeu32, buffer.readf64, buffer.readu32
-- Write vertex i (1-based) of a VtxBuffer.Buf
local function WV(b, i, x, y, u, v, col)
local o = (i - 1) * 36
writef64(b, o, x); writef64(b, o + 8, y); writef64(b, o + 16, u); writef64(b, o + 24, v); writeu32(b, o + 32, col)
end
local IM_FONTGLYPH_INDEX_UNUSED = 0xFFFF -- ((ImU16)-1)
local IM_FONTGLYPH_INDEX_NOT_FOUND = 0xFFFE -- ((ImU16)-2)
--- @param str string
--- @return table
--- @nodiscard
--- @package
local function str_to_table(str)
local t = {} for i = 1, #str do t[i] = string.sub(str, i, i) end return t
end
--- @param buf char[] # 1-based
--- @param base int # 1-based start index
--- @param u32 ImU32
local function write_u32(buf, base, u32)
buf[base + 0] = bit32.band(u32, 0xFF)
buf[base + 1] = bit32.band(bit32.rshift(u32, 8), 0xFF)
buf[base + 2] = bit32.band(bit32.rshift(u32, 16), 0xFF)
buf[base + 3] = bit32.band(bit32.rshift(u32, 24), 0xFF)
end
-- A work of art lies ahead! (. = white layer, X = black layer, others are blank)
-- The 2x2 white texels on the top left are the ones we'll use everywhere in Dear ImGui to render filled shapes.
-- (This is used when io.MouseDrawCursor = true)
local FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS = str_to_table(
"..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX- XX - XX XX " ..
"..- -X.....X- X.X - X.X -X.....X - X.....X- X..X -X..X X..X" ..
"--- -XXX.XXX- X...X - X...X -X....X - X....X- X..X -X...X X...X" ..
"X - X.X - X.....X - X.....X -X...X - X...X- X..X - X...X X...X " ..
"XX - X.X -X.......X- X.......X -X..X.X - X.X..X- X..X - X...X...X " ..
"X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X- X..XXX - X.....X " ..
"X..X - X.X - X.X - X.X -XX X.X - X.X XX- X..X..XXX - X...X " ..
"X...X - X.X - X.X - XX X.X XX - X.X - X.X - X..X..X..XX - X.X " ..
"X....X - X.X - X.X - X.X X.X X.X - X.X - X.X - X..X..X..X.X - X...X " ..
"X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X -XXX X..X..X..X..X- X.....X " ..
"X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X -X..XX........X..X- X...X...X " ..
"X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X -X...X...........X- X...X X...X " ..
"X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X - X..............X-X...X X...X" ..
"X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X - X.............X-X..X X..X" ..
"X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X - X.............X- XX XX " ..
"X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X - X............X--------------" ..
"X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX - X...........X - " ..
"X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------- X..........X - " ..
"X.X X..X - -X.......X- X.......X - XX XX - - X..........X - " ..
"XX X..X - - X.....X - X.....X - X.X X.X - - X........X - " ..
" X..X - - X...X - X...X - X..X X..X - - X........X - " ..
" XX - - X.X - X.X - X...XXXXXXXXXXXXX...X - - XXXXXXXXXX - " ..
"------------- - X - X -X.....................X- ------------------- " ..
" ----------------------------------- X...XXXXXXXXXXXXX...X - " ..
" - X..X X..X - " ..
" - X.X X.X - " ..
" - XX XX - "
)
local FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA = {
-- Pos .......... Size ......... Offset .......
{ ImVec2( 0, 3), ImVec2(12, 19), ImVec2( 0, 0) }, -- ImGuiMouseCursor.Arrow
{ ImVec2( 13, 0), ImVec2( 7, 16), ImVec2( 1, 8) }, -- ImGuiMouseCursor.TextInput
{ ImVec2( 31, 0), ImVec2(23, 23), ImVec2(11, 11) }, -- ImGuiMouseCursor.ResizeAll
{ ImVec2( 21, 0), ImVec2( 9, 23), ImVec2( 4, 11) }, -- ImGuiMouseCursor.ResizeNS
{ ImVec2( 55, 18), ImVec2(23, 9), ImVec2(11, 4) }, -- ImGuiMouseCursor.ResizeEW
{ ImVec2( 73, 0), ImVec2(17, 17), ImVec2( 8, 8) }, -- ImGuiMouseCursor.ResizeNESW
{ ImVec2( 55, 0), ImVec2(17, 17), ImVec2( 8, 8) }, -- ImGuiMouseCursor.ResizeNWSE
{ ImVec2( 91, 0), ImVec2(17, 22), ImVec2( 5, 0) }, -- ImGuiMouseCursor.Hand
{ ImVec2( 0, 3), ImVec2(12, 19), ImVec2( 0, 0) }, -- ImGuiMouseCursor.Wait -- Arrow + custom code in ImGui.RenderMouseCursor()
{ ImVec2( 0, 3), ImVec2(12, 19), ImVec2( 0, 0) }, -- ImGuiMouseCursor.Progress -- Arrow + custom code in ImGui.RenderMouseCursor()
{ ImVec2(109, 0), ImVec2(13, 15), ImVec2( 6, 7) }, -- ImGuiMouseCursor.NotAllowed
}
local MT = ImGui.GetMetatables()
-------------------------------------------------------------------------------
-- [SECTION] STB libraries implementation (for stb_truetype and stb_rect_pack)
-------------------------------------------------------------------------------
--- @module "imstb_rectpack"
local stbrp = IM_INCLUDE("imstb_rectpack.lua")
--- @module "imstb_truetype"
local stbtt = IM_INCLUDE("imstb_truetype.lua")
-------------------------------------------------------------------------------
-- [SECTION] Style functions
-------------------------------------------------------------------------------
--- @param a ImVec4
--- @param b ImVec4
--- @param t float
local function ImLerpV4V4F(a, b, t) return ImVec4(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t, a.w + (b.w - a.w) * t) end
--- @param dst? ImGuiStyle
function ImGui.StyleColorsDark(dst)
local style = dst and dst or ImGui.GetStyle()
local colors = style.Colors
colors[0 --[[ImGuiCol.Text]]] = ImVec4(1.00, 1.00, 1.00, 1.00)
colors[1 --[[ImGuiCol.TextDisabled]]] = ImVec4(0.50, 0.50, 0.50, 1.00)
colors[2 --[[ImGuiCol.WindowBg]]] = ImVec4(0.06, 0.06, 0.06, 0.94)
colors[3 --[[ImGuiCol.ChildBg]]] = ImVec4(0.00, 0.00, 0.00, 0.00)
colors[4 --[[ImGuiCol.PopupBg]]] = ImVec4(0.08, 0.08, 0.08, 0.94)
colors[5 --[[ImGuiCol.Border]]] = ImVec4(0.43, 0.43, 0.50, 0.50)
colors[6 --[[ImGuiCol.BorderShadow]]] = ImVec4(0.00, 0.00, 0.00, 0.00)
colors[7 --[[ImGuiCol.FrameBg]]] = ImVec4(0.16, 0.29, 0.48, 0.54)
colors[8 --[[ImGuiCol.FrameBgHovered]]] = ImVec4(0.26, 0.59, 0.98, 0.40)
colors[9 --[[ImGuiCol.FrameBgActive]]] = ImVec4(0.26, 0.59, 0.98, 0.67)
colors[10 --[[ImGuiCol.TitleBg]]] = ImVec4(0.04, 0.04, 0.04, 1.00)
colors[11 --[[ImGuiCol.TitleBgActive]]] = ImVec4(0.16, 0.29, 0.48, 1.00)
colors[12 --[[ImGuiCol.TitleBgCollapsed]]] = ImVec4(0.00, 0.00, 0.00, 0.51)
colors[13 --[[ImGuiCol.MenuBarBg]]] = ImVec4(0.14, 0.14, 0.14, 1.00)
colors[14 --[[ImGuiCol.ScrollbarBg]]] = ImVec4(0.02, 0.02, 0.02, 0.53)
colors[15 --[[ImGuiCol.ScrollbarGrab]]] = ImVec4(0.31, 0.31, 0.31, 1.00)
colors[16 --[[ImGuiCol.ScrollbarGrabHovered]]] = ImVec4(0.41, 0.41, 0.41, 1.00)
colors[17 --[[ImGuiCol.ScrollbarGrabActive]]] = ImVec4(0.51, 0.51, 0.51, 1.00)
colors[18 --[[ImGuiCol.CheckMark]]] = ImVec4(0.26, 0.59, 0.98, 1.00)
colors[19 --[[ImGuiCol.CheckboxSelectedBg]]] = ImLerpV4V4F(colors[7 --[[ImGuiCol.FrameBg]]], colors[8 --[[ImGuiCol.FrameBgHovered]]], 0.65)
colors[20 --[[ImGuiCol.SliderGrab]]] = ImVec4(0.24, 0.52, 0.88, 1.00)
colors[21 --[[ImGuiCol.SliderGrabActive]]] = ImVec4(0.26, 0.59, 0.98, 1.00)
colors[22 --[[ImGuiCol.Button]]] = ImVec4(0.26, 0.59, 0.98, 0.40)
colors[23 --[[ImGuiCol.ButtonHovered]]] = ImVec4(0.26, 0.59, 0.98, 1.00)
colors[24 --[[ImGuiCol.ButtonActive]]] = ImVec4(0.06, 0.53, 0.98, 1.00)
colors[25 --[[ImGuiCol.Header]]] = ImVec4(0.26, 0.59, 0.98, 0.31)
colors[26 --[[ImGuiCol.HeaderHovered]]] = ImVec4(0.26, 0.59, 0.98, 0.80)
colors[27 --[[ImGuiCol.HeaderActive]]] = ImVec4(0.26, 0.59, 0.98, 1.00)
colors[28 --[[ImGuiCol.Separator]]] = colors[5 --[[ImGuiCol.Border]]]
colors[29 --[[ImGuiCol.SeparatorHovered]]] = ImVec4(0.10, 0.40, 0.75, 0.78)
colors[30 --[[ImGuiCol.SeparatorActive]]] = ImVec4(0.10, 0.40, 0.75, 1.00)
colors[31 --[[ImGuiCol.ResizeGrip]]] = ImVec4(0.26, 0.59, 0.98, 0.20)
colors[32 --[[ImGuiCol.ResizeGripHovered]]] = ImVec4(0.26, 0.59, 0.98, 0.67)
colors[33 --[[ImGuiCol.ResizeGripActive]]] = ImVec4(0.26, 0.59, 0.98, 0.95)
colors[34 --[[ImGuiCol.InputTextCursor]]] = colors[0 --[[ImGuiCol.Text]]]
colors[35 --[[ImGuiCol.TabHovered]]] = colors[26 --[[ImGuiCol.HeaderHovered]]]
colors[36 --[[ImGuiCol.Tab]]] = ImLerpV4V4F(colors[25 --[[ImGuiCol.Header]]], colors[11 --[[ImGuiCol.TitleBgActive]]], 0.80)
colors[37 --[[ImGuiCol.TabSelected]]] = ImLerpV4V4F(colors[27 --[[ImGuiCol.HeaderActive]]], colors[11 --[[ImGuiCol.TitleBgActive]]], 0.60)
colors[38 --[[ImGuiCol.TabSelectedOverline]]] = colors[27 --[[ImGuiCol.HeaderActive]]]
colors[39 --[[ImGuiCol.TabDimmed]]] = ImLerpV4V4F(colors[36 --[[ImGuiCol.Tab]]], colors[10 --[[ImGuiCol.TitleBg]]], 0.80)
colors[40 --[[ImGuiCol.TabDimmedSelected]]] = ImLerpV4V4F(colors[37 --[[ImGuiCol.TabSelected]]], colors[10 --[[ImGuiCol.TitleBg]]], 0.40)
colors[41 --[[ImGuiCol.TabDimmedSelectedOverline]]] = ImVec4(0.50, 0.50, 0.50, 0.00)
colors[42 --[[ImGuiCol.DockingPreview]]] = ImVec4(colors[27 --[[ImGuiCol.HeaderActive]]].x, colors[27 --[[ImGuiCol.HeaderActive]]].y, colors[27 --[[ImGuiCol.HeaderActive]]].z, colors[27 --[[ImGuiCol.HeaderActive]]].w * 0.7)
colors[43 --[[ImGuiCol.DockingEmptyBg]]] = ImVec4(0.20, 0.20, 0.20, 1.00)
colors[44 --[[ImGuiCol.PlotLines]]] = ImVec4(0.61, 0.61, 0.61, 1.00)
colors[45 --[[ImGuiCol.PlotLinesHovered]]] = ImVec4(1.00, 0.43, 0.35, 1.00)
colors[46 --[[ImGuiCol.PlotHistogram]]] = ImVec4(0.90, 0.70, 0.00, 1.00)
colors[47 --[[ImGuiCol.PlotHistogramHovered]]] = ImVec4(1.00, 0.60, 0.00, 1.00)
colors[48 --[[ImGuiCol.TableHeaderBg]]] = ImVec4(0.19, 0.19, 0.20, 1.00)
colors[49 --[[ImGuiCol.TableBorderStrong]]] = ImVec4(0.31, 0.31, 0.35, 1.00) -- Prefer using Alpha=1.0 here
colors[50 --[[ImGuiCol.TableBorderLight]]] = ImVec4(0.23, 0.23, 0.25, 1.00) -- Prefer using Alpha=1.0 here
colors[51 --[[ImGuiCol.TableRowBg]]] = ImVec4(0.00, 0.00, 0.00, 0.00)
colors[52 --[[ImGuiCol.TableRowBgAlt]]] = ImVec4(1.00, 1.00, 1.00, 0.06)
colors[53 --[[ImGuiCol.TextLink]]] = colors[27 --[[ImGuiCol.HeaderActive]]]
colors[54 --[[ImGuiCol.TextSelectedBg]]] = ImVec4(0.26, 0.59, 0.98, 0.35)
colors[55 --[[ImGuiCol.TreeLines]]] = colors[5 --[[ImGuiCol.Border]]]
colors[56 --[[ImGuiCol.DragDropTarget]]] = ImVec4(1.00, 1.00, 0.00, 0.90)
colors[57 --[[ImGuiCol.DragDropTargetBg]]] = ImVec4(0.00, 0.00, 0.00, 0.00)
colors[58 --[[ImGuiCol.UnsavedMarker]]] = ImVec4(1.00, 1.00, 1.00, 1.00)
colors[59 --[[ImGuiCol.NavCursor]]] = ImVec4(0.26, 0.59, 0.98, 1.00)
colors[60 --[[ImGuiCol.NavWindowingHighlight]]] = ImVec4(1.00, 1.00, 1.00, 0.70)
colors[61 --[[ImGuiCol.NavWindowingDimBg]]] = ImVec4(0.80, 0.80, 0.80, 0.20)
colors[62 --[[ImGuiCol.ModalWindowDimBg]]] = ImVec4(0.80, 0.80, 0.80, 0.35)
end
--- @param dst? ImGuiStyle
function ImGui.StyleColorsClassic(dst)
local style = dst and dst or ImGui.GetStyle()
local colors = style.Colors
colors[0 --[[ImGuiCol.Text]]] = ImVec4(0.90, 0.90, 0.90, 1.00)
colors[1 --[[ImGuiCol.TextDisabled]]] = ImVec4(0.60, 0.60, 0.60, 1.00)
colors[2 --[[ImGuiCol.WindowBg]]] = ImVec4(0.00, 0.00, 0.00, 0.85)
colors[3 --[[ImGuiCol.ChildBg]]] = ImVec4(0.00, 0.00, 0.00, 0.00)
colors[4 --[[ImGuiCol.PopupBg]]] = ImVec4(0.11, 0.11, 0.14, 0.92)
colors[5 --[[ImGuiCol.Border]]] = ImVec4(0.50, 0.50, 0.50, 0.50)
colors[6 --[[ImGuiCol.BorderShadow]]] = ImVec4(0.00, 0.00, 0.00, 0.00)
colors[7 --[[ImGuiCol.FrameBg]]] = ImVec4(0.43, 0.43, 0.43, 0.39)
colors[8 --[[ImGuiCol.FrameBgHovered]]] = ImVec4(0.47, 0.47, 0.69, 0.40)
colors[9 --[[ImGuiCol.FrameBgActive]]] = ImVec4(0.42, 0.41, 0.64, 0.69)
colors[10 --[[ImGuiCol.TitleBg]]] = ImVec4(0.27, 0.27, 0.54, 0.83)
colors[11 --[[ImGuiCol.TitleBgActive]]] = ImVec4(0.32, 0.32, 0.63, 0.87)
colors[12 --[[ImGuiCol.TitleBgCollapsed]]] = ImVec4(0.40, 0.40, 0.80, 0.20)
colors[13 --[[ImGuiCol.MenuBarBg]]] = ImVec4(0.40, 0.40, 0.55, 0.80)
colors[14 --[[ImGuiCol.ScrollbarBg]]] = ImVec4(0.20, 0.25, 0.30, 0.60)
colors[15 --[[ImGuiCol.ScrollbarGrab]]] = ImVec4(0.40, 0.40, 0.80, 0.30)
colors[16 --[[ImGuiCol.ScrollbarGrabHovered]]] = ImVec4(0.40, 0.40, 0.80, 0.40)
colors[17 --[[ImGuiCol.ScrollbarGrabActive]]] = ImVec4(0.41, 0.39, 0.80, 0.60)
colors[18 --[[ImGuiCol.CheckMark]]] = ImVec4(0.90, 0.90, 0.90, 0.50)
colors[19 --[[ImGuiCol.CheckboxSelectedBg]]] = ImLerpV4V4F(colors[7 --[[ImGuiCol.FrameBg]]], colors[9 --[[ImGuiCol.FrameBgActive]]], 0.65)
colors[20 --[[ImGuiCol.SliderGrab]]] = ImVec4(1.00, 1.00, 1.00, 0.30)
colors[21 --[[ImGuiCol.SliderGrabActive]]] = ImVec4(0.41, 0.39, 0.80, 0.60)
colors[22 --[[ImGuiCol.Button]]] = ImVec4(0.35, 0.40, 0.61, 0.62)
colors[23 --[[ImGuiCol.ButtonHovered]]] = ImVec4(0.40, 0.48, 0.71, 0.79)
colors[24 --[[ImGuiCol.ButtonActive]]] = ImVec4(0.46, 0.54, 0.80, 1.00)
colors[25 --[[ImGuiCol.Header]]] = ImVec4(0.40, 0.40, 0.90, 0.45)
colors[26 --[[ImGuiCol.HeaderHovered]]] = ImVec4(0.45, 0.45, 0.90, 0.80)
colors[27 --[[ImGuiCol.HeaderActive]]] = ImVec4(0.53, 0.53, 0.87, 0.80)
colors[28 --[[ImGuiCol.Separator]]] = ImVec4(0.50, 0.50, 0.50, 0.60)
colors[29 --[[ImGuiCol.SeparatorHovered]]] = ImVec4(0.60, 0.60, 0.70, 1.00)
colors[30 --[[ImGuiCol.SeparatorActive]]] = ImVec4(0.70, 0.70, 0.90, 1.00)
colors[31 --[[ImGuiCol.ResizeGrip]]] = ImVec4(1.00, 1.00, 1.00, 0.10)
colors[32 --[[ImGuiCol.ResizeGripHovered]]] = ImVec4(0.78, 0.82, 1.00, 0.60)
colors[33 --[[ImGuiCol.ResizeGripActive]]] = ImVec4(0.78, 0.82, 1.00, 0.90)
colors[34 --[[ImGuiCol.InputTextCursor]]] = colors[0 --[[ImGuiCol.Text]]]
colors[35 --[[ImGuiCol.TabHovered]]] = colors[26 --[[ImGuiCol.HeaderHovered]]]
colors[36 --[[ImGuiCol.Tab]]] = ImLerpV4V4F(colors[25 --[[ImGuiCol.Header]]], colors[11 --[[ImGuiCol.TitleBgActive]]], 0.80)
colors[37 --[[ImGuiCol.TabSelected]]] = ImLerpV4V4F(colors[27 --[[ImGuiCol.HeaderActive]]], colors[11 --[[ImGuiCol.TitleBgActive]]], 0.60)
colors[38 --[[ImGuiCol.TabSelectedOverline]]] = colors[27 --[[ImGuiCol.HeaderActive]]]
colors[39 --[[ImGuiCol.TabDimmed]]] = ImLerpV4V4F(colors[36 --[[ImGuiCol.Tab]]], colors[10 --[[ImGuiCol.TitleBg]]], 0.80)
colors[40 --[[ImGuiCol.TabDimmedSelected]]] = ImLerpV4V4F(colors[37 --[[ImGuiCol.TabSelected]]], colors[10 --[[ImGuiCol.TitleBg]]], 0.40)
colors[41 --[[ImGuiCol.TabDimmedSelectedOverline]]] = ImVec4(0.53, 0.53, 0.87, 0.00)
colors[42 --[[ImGuiCol.DockingPreview]]] = ImVec4(colors[25 --[[ImGuiCol.Header]]].x, colors[25 --[[ImGuiCol.Header]]].y, colors[25 --[[ImGuiCol.Header]]].z, colors[25 --[[ImGuiCol.Header]]].w * 0.7)
colors[43 --[[ImGuiCol.DockingEmptyBg]]] = ImVec4(0.20, 0.20, 0.20, 1.00)
colors[44 --[[ImGuiCol.PlotLines]]] = ImVec4(1.00, 1.00, 1.00, 1.00)
colors[45 --[[ImGuiCol.PlotLinesHovered]]] = ImVec4(0.90, 0.70, 0.00, 1.00)
colors[46 --[[ImGuiCol.PlotHistogram]]] = ImVec4(0.90, 0.70, 0.00, 1.00)
colors[47 --[[ImGuiCol.PlotHistogramHovered]]] = ImVec4(1.00, 0.60, 0.00, 1.00)
colors[48 --[[ImGuiCol.TableHeaderBg]]] = ImVec4(0.27, 0.27, 0.38, 1.00)
colors[49 --[[ImGuiCol.TableBorderStrong]]] = ImVec4(0.31, 0.31, 0.45, 1.00) -- Prefer using Alpha=1.0 here
colors[50 --[[ImGuiCol.TableBorderLight]]] = ImVec4(0.26, 0.26, 0.28, 1.00) -- Prefer using Alpha=1.0 here
colors[51 --[[ImGuiCol.TableRowBg]]] = ImVec4(0.00, 0.00, 0.00, 0.00)
colors[52 --[[ImGuiCol.TableRowBgAlt]]] = ImVec4(1.00, 1.00, 1.00, 0.07)
colors[53 --[[ImGuiCol.TextLink]]] = colors[27 --[[ImGuiCol.HeaderActive]]]
colors[54 --[[ImGuiCol.TextSelectedBg]]] = ImVec4(0.00, 0.00, 1.00, 0.35)
colors[55 --[[ImGuiCol.TreeLines]]] = colors[5 --[[ImGuiCol.Border]]]
colors[56 --[[ImGuiCol.DragDropTarget]]] = ImVec4(1.00, 1.00, 0.00, 0.90)
colors[57 --[[ImGuiCol.DragDropTargetBg]]] = ImVec4(0.00, 0.00, 0.00, 0.00)
colors[58 --[[ImGuiCol.UnsavedMarker]]] = ImVec4(0.90, 0.90, 0.90, 1.00)
colors[59 --[[ImGuiCol.NavCursor]]] = colors[26 --[[ImGuiCol.HeaderHovered]]]
colors[60 --[[ImGuiCol.NavWindowingHighlight]]] = ImVec4(1.00, 1.00, 1.00, 0.70)
colors[61 --[[ImGuiCol.NavWindowingDimBg]]] = ImVec4(0.80, 0.80, 0.80, 0.20)
colors[62 --[[ImGuiCol.ModalWindowDimBg]]] = ImVec4(0.20, 0.20, 0.20, 0.35)
end
-- Those light colors are better suited with a thicker font than the default one + FrameBorder
--- @param dst? ImGuiStyle
function ImGui.StyleColorsLight(dst)
local style = dst and dst or ImGui.GetStyle()
local colors = style.Colors
colors[0 --[[ImGuiCol.Text]]] = ImVec4(0.00, 0.00, 0.00, 1.00)
colors[1 --[[ImGuiCol.TextDisabled]]] = ImVec4(0.60, 0.60, 0.60, 1.00)
colors[2 --[[ImGuiCol.WindowBg]]] = ImVec4(0.94, 0.94, 0.94, 1.00)
colors[3 --[[ImGuiCol.ChildBg]]] = ImVec4(0.00, 0.00, 0.00, 0.00)
colors[4 --[[ImGuiCol.PopupBg]]] = ImVec4(1.00, 1.00, 1.00, 0.98)
colors[5 --[[ImGuiCol.Border]]] = ImVec4(0.00, 0.00, 0.00, 0.30)
colors[6 --[[ImGuiCol.BorderShadow]]] = ImVec4(0.00, 0.00, 0.00, 0.00)
colors[7 --[[ImGuiCol.FrameBg]]] = ImVec4(1.00, 1.00, 1.00, 1.00)
colors[8 --[[ImGuiCol.FrameBgHovered]]] = ImVec4(0.26, 0.59, 0.98, 0.40)
colors[9 --[[ImGuiCol.FrameBgActive]]] = ImVec4(0.26, 0.59, 0.98, 0.67)
colors[10 --[[ImGuiCol.TitleBg]]] = ImVec4(0.96, 0.96, 0.96, 1.00)
colors[11 --[[ImGuiCol.TitleBgActive]]] = ImVec4(0.82, 0.82, 0.82, 1.00)
colors[12 --[[ImGuiCol.TitleBgCollapsed]]] = ImVec4(1.00, 1.00, 1.00, 0.51)
colors[13 --[[ImGuiCol.MenuBarBg]]] = ImVec4(0.86, 0.86, 0.86, 1.00)
colors[14 --[[ImGuiCol.ScrollbarBg]]] = ImVec4(0.98, 0.98, 0.98, 0.53)
colors[15 --[[ImGuiCol.ScrollbarGrab]]] = ImVec4(0.69, 0.69, 0.69, 0.80)
colors[16 --[[ImGuiCol.ScrollbarGrabHovered]]] = ImVec4(0.49, 0.49, 0.49, 0.80)
colors[17 --[[ImGuiCol.ScrollbarGrabActive]]] = ImVec4(0.49, 0.49, 0.49, 1.00)
colors[18 --[[ImGuiCol.CheckMark]]] = ImVec4(0.26, 0.59, 0.98, 1.00)
colors[19 --[[ImGuiCol.CheckboxSelectedBg]]] = ImVec4(0.95, 0.97, 1.00, 1.00)
colors[20 --[[ImGuiCol.SliderGrab]]] = ImVec4(0.26, 0.59, 0.98, 0.78)
colors[21 --[[ImGuiCol.SliderGrabActive]]] = ImVec4(0.46, 0.54, 0.80, 0.60)
colors[22 --[[ImGuiCol.Button]]] = ImVec4(0.26, 0.59, 0.98, 0.40)
colors[23 --[[ImGuiCol.ButtonHovered]]] = ImVec4(0.26, 0.59, 0.98, 1.00)
colors[24 --[[ImGuiCol.ButtonActive]]] = ImVec4(0.06, 0.53, 0.98, 1.00)
colors[25 --[[ImGuiCol.Header]]] = ImVec4(0.26, 0.59, 0.98, 0.31)
colors[26 --[[ImGuiCol.HeaderHovered]]] = ImVec4(0.26, 0.59, 0.98, 0.80)
colors[27 --[[ImGuiCol.HeaderActive]]] = ImVec4(0.26, 0.59, 0.98, 1.00)
colors[28 --[[ImGuiCol.Separator]]] = ImVec4(0.39, 0.39, 0.39, 0.62)
colors[29 --[[ImGuiCol.SeparatorHovered]]] = ImVec4(0.14, 0.44, 0.80, 0.78)
colors[30 --[[ImGuiCol.SeparatorActive]]] = ImVec4(0.14, 0.44, 0.80, 1.00)
colors[31 --[[ImGuiCol.ResizeGrip]]] = ImVec4(0.35, 0.35, 0.35, 0.17)
colors[32 --[[ImGuiCol.ResizeGripHovered]]] = ImVec4(0.26, 0.59, 0.98, 0.67)
colors[33 --[[ImGuiCol.ResizeGripActive]]] = ImVec4(0.26, 0.59, 0.98, 0.95)
colors[34 --[[ImGuiCol.InputTextCursor]]] = colors[0 --[[ImGuiCol.Text]]]
colors[35 --[[ImGuiCol.TabHovered]]] = colors[26 --[[ImGuiCol.HeaderHovered]]]
colors[36 --[[ImGuiCol.Tab]]] = ImLerpV4V4F(colors[25 --[[ImGuiCol.Header]]], colors[11 --[[ImGuiCol.TitleBgActive]]], 0.90)
colors[37 --[[ImGuiCol.TabSelected]]] = ImLerpV4V4F(colors[27 --[[ImGuiCol.HeaderActive]]], colors[11 --[[ImGuiCol.TitleBgActive]]], 0.60)
colors[38 --[[ImGuiCol.TabSelectedOverline]]] = colors[27 --[[ImGuiCol.HeaderActive]]]
colors[39 --[[ImGuiCol.TabDimmed]]] = ImLerpV4V4F(colors[36 --[[ImGuiCol.Tab]]], colors[10 --[[ImGuiCol.TitleBg]]], 0.80)
colors[40 --[[ImGuiCol.TabDimmedSelected]]] = ImLerpV4V4F(colors[37 --[[ImGuiCol.TabSelected]]], colors[10 --[[ImGuiCol.TitleBg]]], 0.40)
colors[41 --[[ImGuiCol.TabDimmedSelectedOverline]]] = ImVec4(0.26, 0.59, 1.00, 0.00)
colors[42 --[[ImGuiCol.DockingPreview]]] = ImVec4(colors[25 --[[ImGuiCol.Header]]].x, colors[25 --[[ImGuiCol.Header]]].y, colors[25 --[[ImGuiCol.Header]]].z, colors[25 --[[ImGuiCol.Header]]].w * 0.7)
colors[43 --[[ImGuiCol.DockingEmptyBg]]] = ImVec4(0.68, 0.69, 0.73, 1.00)
colors[44 --[[ImGuiCol.PlotLines]]] = ImVec4(0.39, 0.39, 0.39, 1.00)
colors[45 --[[ImGuiCol.PlotLinesHovered]]] = ImVec4(1.00, 0.43, 0.35, 1.00)
colors[46 --[[ImGuiCol.PlotHistogram]]] = ImVec4(0.90, 0.70, 0.00, 1.00)
colors[47 --[[ImGuiCol.PlotHistogramHovered]]] = ImVec4(1.00, 0.45, 0.00, 1.00)
colors[48 --[[ImGuiCol.TableHeaderBg]]] = ImVec4(0.78, 0.87, 0.98, 1.00)
colors[49 --[[ImGuiCol.TableBorderStrong]]] = ImVec4(0.57, 0.57, 0.64, 1.00) -- Prefer using Alpha=1.0 here
colors[50 --[[ImGuiCol.TableBorderLight]]] = ImVec4(0.68, 0.68, 0.74, 1.00) -- Prefer using Alpha=1.0 here
colors[51 --[[ImGuiCol.TableRowBg]]] = ImVec4(0.00, 0.00, 0.00, 0.00)
colors[52 --[[ImGuiCol.TableRowBgAlt]]] = ImVec4(0.30, 0.30, 0.30, 0.09)
colors[53 --[[ImGuiCol.TextLink]]] = colors[27 --[[ImGuiCol.HeaderActive]]]
colors[54 --[[ImGuiCol.TextSelectedBg]]] = ImVec4(0.26, 0.59, 0.98, 0.35)
colors[55 --[[ImGuiCol.TreeLines]]] = colors[5 --[[ImGuiCol.Border]]]
colors[56 --[[ImGuiCol.DragDropTarget]]] = ImVec4(0.26, 0.59, 0.98, 0.95)
colors[57 --[[ImGuiCol.DragDropTargetBg]]] = ImVec4(0.00, 0.00, 0.00, 0.00)
colors[58 --[[ImGuiCol.UnsavedMarker]]] = ImVec4(0.00, 0.00, 0.00, 1.00)
colors[59 --[[ImGuiCol.NavCursor]]] = colors[26 --[[ImGuiCol.HeaderHovered]]]
colors[60 --[[ImGuiCol.NavWindowingHighlight]]] = ImVec4(0.70, 0.70, 0.70, 0.70)
colors[61 --[[ImGuiCol.NavWindowingDimBg]]] = ImVec4(0.20, 0.20, 0.20, 0.20)
colors[62 --[[ImGuiCol.ModalWindowDimBg]]] = ImVec4(0.20, 0.20, 0.20, 0.35)
end
-------------------------------------------------------------------------------
-- [SECTION] Helpers ShadeVertsXXX functions
-------------------------------------------------------------------------------
-- Generic linear color gradient, write to RGB fields, leave A untouched.
--- @param draw_list ImDrawList
--- @param vert_start_idx int
--- @param vert_end_idx int
--- @param gradient_p0 ImVec2
--- @param gradient_p1 ImVec2
--- @param col0 ImU32
--- @param col1 ImU32
function ImGui.ShadeVertsLinearColorGradientKeepAlpha(draw_list, vert_start_idx, vert_end_idx, gradient_p0, gradient_p1, col0, col1)
local gradient_extent = gradient_p1 - gradient_p0
local gradient_inv_length2 = 1.0 / ImLengthSqr(gradient_extent)
local col0_r = bit32.band(bit32.rshift(col0, 0 --[[IM_COL32_R_SHIFT]]), 0xFF)
local col0_g = bit32.band(bit32.rshift(col0, 8 --[[IM_COL32_G_SHIFT]]), 0xFF)
local col0_b = bit32.band(bit32.rshift(col0, 16 --[[IM_COL32_B_SHIFT]]), 0xFF)
local col_delta_r = bit32.band(bit32.rshift(col1, 0 --[[IM_COL32_R_SHIFT]]), 0xFF) - col0_r
local col_delta_g = bit32.band(bit32.rshift(col1, 8 --[[IM_COL32_G_SHIFT]]), 0xFF) - col0_g
local col_delta_b = bit32.band(bit32.rshift(col1, 16 --[[IM_COL32_B_SHIFT]]), 0xFF) - col0_b
for vert_idx = vert_start_idx, vert_end_idx - 1 do
local vb = draw_list.VtxBuffer.Buf
local o = (vert_idx - 1) * 36
local d = (readf64(vb, o) - gradient_p0.x) * gradient_extent.x + (readf64(vb, o + 8) - gradient_p0.y) * gradient_extent.y
local t = ImClamp(d * gradient_inv_length2, 0.0, 1.0)
local r = math.floor(col0_r + col_delta_r * t)
local g = math.floor(col0_g + col_delta_g * t)
local b = math.floor(col0_b + col_delta_b * t)
writeu32(vb, o + 32, bit32.bor(bit32.lshift(r, 0 --[[IM_COL32_R_SHIFT]]), bit32.lshift(g, 8 --[[IM_COL32_G_SHIFT]]), bit32.lshift(b, 16 --[[IM_COL32_B_SHIFT]]), bit32.band(readu32(vb, o + 32), 4278190080 --[[IM_COL32_A_MASK]])))
end
end
-- Rotate and translate vertices [vert_start_idx, vert_end_idx) (1-based, like _VtxCurrentIdx)
function ImGui.ShadeVertsTransformPos(draw_list, vert_start_idx, vert_end_idx, pivot_in, cos_a, sin_a, pivot_out)
local data = draw_list.VtxBuffer.Buf
local pix, piy, pox, poy = pivot_in.x, pivot_in.y, pivot_out.x, pivot_out.y
for i = vert_start_idx, vert_end_idx - 1 do
local o = (i - 1) * 36
local x, y = readf64(data, o) - pix, readf64(data, o + 8) - piy
writef64(data, o, x * cos_a - y * sin_a + pox)
writef64(data, o + 8, x * sin_a + y * cos_a + poy)
end
end
-- Distribute UV over (a, b) rectangle
--- @param draw_list ImDrawList
--- @param vert_start_idx int
--- @param vert_end_idx int
--- @param a ImVec2
--- @param b ImVec2
--- @param uv_a ImVec2
--- @param uv_b ImVec2
--- @param clamp bool
function ImGui.ShadeVertsLinearUV(draw_list, vert_start_idx, vert_end_idx, a, b, uv_a, uv_b, clamp)
local size = b - a
local uv_size = uv_b - uv_a
local scale = ImVec2(
(size.x ~= 0.0) and (uv_size.x / size.x) or 0.0,
(size.y ~= 0.0) and (uv_size.y / size.y) or 0.0
)
local vertex
local verts = draw_list.VtxBuffer.Buf
if clamp then
local min = ImMin(uv_a, uv_b)
local max = ImMax(uv_a, uv_b)
for vert_idx = vert_start_idx, vert_end_idx - 1 do
local o = (vert_idx - 1) * 36
local uv = ImClamp(uv_a + ImMul(ImVec2(readf64(verts, o), readf64(verts, o + 8)) - a, scale), min, max)
writef64(verts, o + 16, uv.x); writef64(verts, o + 24, uv.y)
end
else
for vert_idx = vert_start_idx, vert_end_idx - 1 do
local o = (vert_idx - 1) * 36
local uv = uv_a + ImMul(ImVec2(readf64(verts, o), readf64(verts, o + 8)) - a, scale)
writef64(verts, o + 16, uv.x); writef64(verts, o + 24, uv.y)
end
end
end
--- Forward Declarations
local ImFontAtlasTextureAdd
local ImFontAtlasTextureGrow
local ImFontAtlasTextureGetSizeEstimate
local ImFontAtlasTextureBlockCopy
local ImFontAtlasTextureBlockConvert
local ImFontAtlasTextureBlockPostProcess
local ImFontAtlasTextureBlockPostProcessMultiply
local ImFontAtlasTextureBlockQueueUpload
local ImFontAtlasTextureCompact
local ImFontAtlasPackInit
local ImFontAtlasPackAddRect
local ImFontAtlasPackGetRect
local ImFontAtlasPackGetRectSafe
local ImFontAtlasBakedAdd
local ImFontAtlasBakedAddFontGlyph
local ImFontAtlasBakedAddFontGlyphAdvancedX
local ImFontAtlasBakedDiscard
local ImFontAtlasBakedGetOrAdd
local ImFontAtlasBakedGetId
local ImFontAtlasBakedGetClosestMatch
local ImFontAtlasBuildInit
local ImFontAtlasBuildSetTexture
local ImFontAtlasBuildUpdateTexDataLines
local ImFontAtlasBuildUpdateTexDataCorners
local ImFontAtlasBuildUpdateTexDataBasic
local IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC -- [Luau] forward declared, defined in [SECTION] ImDrawList
local CalcCornerArcStepAndCount -- [Luau] forward declared, defined in [SECTION] ImDrawList
local ImFontAtlasBuildSetupFontLoader
local ImFontAtlasBuildSetupFontSpecialGlyphs
local ImFontAtlasBuildSetupFontBakedBlanks
local ImFontAtlasBuildSetupFontBakedEllipsis
local ImFontAtlasBuildSetupFontBakedFallback
local ImFontAtlasBuildDestroy
local ImFontAtlasBuildAcceptCodepointForSource
local ImFontAtlasBuildNotifySetFont
local ImFontAtlasBuildRenderBitmapFromString
local ImFontAtlasFontSourceInit
local ImFontAtlasFontSourceAddToFont
local ImFontAtlasFontDestroySourceData
local ImFontAtlasFontDestroyOutput
local ImFontBaked_BuildGrowIndex
local ImFontBaked_BuildLoadGlyph
local ImFontBaked_BuildLoadGlyphAdvanceX
local ImTextInitClassifiers
local ImTextClassifierGet
local ImTextClassifierClear
local ImTextClassifierSetCharClass
local ImTextClassifierSetCharClassFromStr
local ImTextureDataGetFormatBytesPerPixel
-------------------------------------------------------------------------------
-- [SECTION] ImFontAtlas, ImFontAtlasBuilder
-------------------------------------------------------------------------------
-- - Default texture data encoded in ASCII
-- - ImFontAtlas()
-- - ImFontAtlas:Clear()
-- - ImFontAtlas:ClearFonts()
-- - ImFontAtlas:ClearInputData()
-- - ImFontAtlas:ClearTexData()
-- - ImFontAtlas:CompactCache()
-- - ImFontAtlas:SetFontLoader()
-------------------------------------------------------------------------------
-- - ImFontAtlasUpdateNewFrame()
-- - ImFontAtlasTextureBlockConvert()
-- - ImFontAtlasTextureBlockPostProcess()
-- - ImFontAtlasTextureBlockPostProcessMultiply()
-- - ImFontAtlasTextureBlockFill()
-- - ImFontAtlasTextureBlockCopy()
-- - ImFontAtlasTextureBlockQueueUpload()
-------------------------------------------------------------------------------
-- - ImFontAtlas:GetTexDataAsAlpha8() [legacy]
-- - ImFontAtlas:GetTexDataAsRGBA32() [legacy]
-- - ImFontAtlas:Build() [legacy]
-------------------------------------------------------------------------------
-- - ImFontAtlas:AddFont()
-- - ImFontAtlas:AddFontDefault()
-- - ImFontAtlas:AddFontDefaultBitmap()
-- - ImFontAtlas:AddFontDefaultVector()
-- - ImFontAtlas:AddFontFromFileTTF()
-- - ImFontAtlas:AddFontFromMemoryTTF()
-- - ImFontAtlas:AddFontFromMemoryCompressedTTF()
-- - ImFontAtlas:AddFontFromMemoryCompressedBase85TTF()
-- - ImFontAtlas:RemoveFont()
-- - ImFontAtlasBuildNotifySetFont()
-------------------------------------------------------------------------------
-- - ImFontAtlas:AddCustomRect()
-- - ImFontAtlas:RemoveCustomRect()
-- - ImFontAtlas:GetCustomRect()
-- - ImFontAtlas:AddCustomRectFontGlyph() [legacy]
-- - ImFontAtlas:AddCustomRectFontGlyphForSize() [legacy]
-- - ImFontAtlasGetMouseCursorTexData()
-------------------------------------------------------------------------------
-- - ImFontAtlasBuildMain()
-- - ImFontAtlasBuildSetupFontLoader()
-- - ImFontAtlasBuildPreloadAllGlyphRanges()
-- - ImFontAtlasBuildUpdatePointers()
-- - ImFontAtlasBuildRenderBitmapFromString()
-- - ImFontAtlasBuildAddFont()
-- - ImFontAtlasBuildSetupFontBakedEllipsis()
-- - ImFontAtlasBuildSetupFontBakedBlanks()
-- - ImFontAtlasBuildSetupFontBakedFallback()
-- - ImFontAtlasBuildSetupFontSpecialGlyphs()
-- - ImFontAtlasBuildDiscardBakes()
-- - ImFontAtlasBuildDiscardFontBakedGlyph()
-- - ImFontAtlasBuildDiscardFontBaked()
-- - ImFontAtlasBuildDiscardFontBakes()
-------------------------------------------------------------------------------
-- - ImFontAtlasAddDrawListSharedData()
-- - ImFontAtlasRemoveDrawListSharedData()
-- - ImFontAtlasUpdateDrawListsTextures()
-- - ImFontAtlasUpdateDrawListsSharedData()
-------------------------------------------------------------------------------
-- - ImFontAtlasBuildSetTexture()
-- - ImFontAtlasBuildUpdateTexData()
-- - ImFontAtlasTextureAdd()
-- - ImFontAtlasTextureRepack()
-- - ImFontAtlasTextureGrow()
-- - ImFontAtlasTextureMakeSpace()
-- - ImFontAtlasTextureGetSizeEstimate()
-- - ImFontAtlasBuildClear()
-- - ImFontAtlasTextureCompact()
-- - ImFontAtlasBuildInit()
-- - ImFontAtlasBuildDestroy()
-------------------------------------------------------------------------------
-- - ImFontAtlasPackInit()
-- - ImFontAtlasPackAllocRectEntry()
-- - ImFontAtlasPackReuseRectEntry()
-- - ImFontAtlasPackDiscardRect()
-- - ImFontAtlasPackAddRect()
-- - ImFontAtlasPackGetRect()
-------------------------------------------------------------------------------
-- - ImFontBaked_BuildGrowIndex()
-- - ImFontBaked_BuildLoadGlyph()
-- - ImFontBaked_BuildLoadGlyphAdvanceX()
-- - ImFontAtlasDebugLogTextureRequests()
-------------------------------------------------------------------------------
-- - ImFontAtlasGetFontLoaderForStbTruetype()
-------------------------------------------------------------------------------
-- You probably should not call this directly. It is not well specified.
-- If you want to replace all your fonts mid-frame, most likely you should instead call ClearFonts() then load the new fonts.
-- Calling this mid-frame will discard the CPU-side copy of the texture data which is generally unreliable as you may have textures queued for creation or updates.
function MT.ImFontAtlas:Clear()
IMGUI_DEBUG_LOG_FONT("[font] ImFontAtlas:Clear()")
local backup_renderer_has_textures = self.RendererHasTextures
self.RendererHasTextures = false -- Full Clear() is supported, but ClearTexData() only isn't.
self:ClearFonts()
self:ClearTexData()
self.RendererHasTextures = backup_renderer_has_textures
end
function MT.ImFontAtlas:SetFontLoader(font_loader)
ImFontAtlasBuildSetupFontLoader(self, font_loader)
end
function MT.ImFontAtlas:ClearInputData()
assert(not self.Locked, "Cannot modify a locked ImFontAtlas!")
for _, font in self.Fonts:iter() do
ImFontAtlasFontDestroyOutput(self, font)
end
for _, font_cfg in self.Sources:iter() do
ImFontAtlasFontDestroySourceData(self, font_cfg)
end
for _, font in self.Fonts:iter() do
-- When clearing this we lose access to the font name and other information used to build the font.
font.Sources:clear()
font.Flags = bit32.bor(font.Flags, 4 --[[ImFontFlags.NoLoadGlyphs]])
end
self.Sources:clear()
end
-- Clear CPU-side copy of the texture data.
function MT.ImFontAtlas:ClearTexData()
assert(not self.Locked, "Cannot modify a locked ImFontAtlas!")
assert(self.RendererHasTextures == false, "Not supported for dynamic atlases, but you may call Clear().")
for _, tex in self.TexList:iter() do
tex:DestroyPixels()
end
--self.Locked = true -- Hoped to be able to lock this down but some reload patterns may not be happy with it.
end
function MT.ImFontAtlas:CompactCache()
ImFontAtlasTextureCompact(self)
end
function MT.ImFontAtlas:ClearFonts()
-- FIXME-NEWATLAS: Illegal to remove currently bound font.
IMGUI_DEBUG_LOG_FONT("[font] ImFontAtlas:ClearFonts()")
assert(not self.Locked, "Cannot modify a locked ImFontAtlas!")
for _, font in self.Fonts:iter() do
ImFontAtlasBuildNotifySetFont(self, font, nil)
end
ImFontAtlasBuildDestroy(self)
self:ClearInputData()
self.Fonts:clear_delete()
self.TexIsBuilt = false
for _, shared_data in self.DrawListSharedDatas:iter() do
if shared_data.FontAtlas == self then
shared_data.Font = nil
shared_data.FontSize = 0.0
shared_data.FontScale = 0.0
end
end
end
--- @param tex ImTextureData
function ImTextureDataUpdateNewFrame(tex)
local remove_from_list = false
if tex.Status == 0 --[[ImTextureStatus.OK]] then
tex.Updates:resize(0)
tex.UpdateRect.x = 65535 tex.UpdateRect.y = 65535
tex.UpdateRect.w = 0 tex.UpdateRect.h = 0
end
-- Request destroy
-- - Keep bool to true in order to differentiate a planned destroy vs a destroy decided by the backend.
-- - We don't destroy pixels right away, as backend may have an in-flight copy from RAM.
if (tex.WantDestroyNextFrame and tex.Status ~= 1 --[[ImTextureStatus.Destroyed]] and tex.Status ~= 4 --[[ImTextureStatus.WantDestroy]]) then
assert(tex.Status == 0 --[[ImTextureStatus.OK]] or tex.Status == 2 --[[ImTextureStatus.WantCreate]] or tex.Status == 3 --[[ImTextureStatus.WantUpdates]])
tex.Status = 4 --[[ImTextureStatus.WantDestroy]]
end
-- If a texture has never reached the backend, they don't need to know about it.
-- (note: backends between 1.92.0 and 1.92.4 could set an already destroyed texture to ImTextureStatus.WantDestroy
-- when invalidating graphics objects twice, which would previously remove it from the list and crash.)
if (tex.Status == 4 --[[ImTextureStatus.WantDestroy]] and tex.TexID == (-1) --[[ImTextureID_Invalid]] and tex.BackendUserData == nil and tex.QueueUserData == nil) then
tex.Status = 1 --[[ImTextureStatus.Destroyed]]
end
-- Process texture being destroyed
if (tex.Status == 1 --[[ImTextureStatus.Destroyed]]) then
assert(tex.TexID == (-1) --[[ImTextureID_Invalid]] and tex.BackendUserData == nil, "Backend set texture Status to Destroyed but did not clear TexID/BackendUserData!")
assert(tex.QueueUserData == nil, "Texture queue set Status to Destroyed but did not clear QueueUserData!")
if (tex.WantDestroyNextFrame) then
remove_from_list = true -- Destroy was scheduled by us
else
tex.Status = 2 --[[ImTextureStatus.WantCreate]] -- Destroy was done was backend: recreate it (e.g. freed resources mid-run)
end
end
-- The backend may need defer destroying by a few frames, to handle texture used by previous in-flight rendering.
-- We allow the texture staying in _WantDestroy state and increment a counter which the backend can use to take its decision.
if (tex.Status == 4 --[[ImTextureStatus.WantDestroy]]) then
tex.UnusedFrames = tex.UnusedFrames + 1
end
return remove_from_list
end
-- Called by NewFrame() for atlases owned by a context.
-- If you manually manage font atlases, you'll need to call this yourself.
-- - 'frame_count' needs to be provided because we can gc/prioritize baked fonts based on their age.
-- - 'frame_count' may not match those of all imgui contexts using this atlas, as contexts may be updated as different frequencies. But generally you can use ImGui.GetFrameCount() on one of your context.
--- @param atlas ImFontAtlas
--- @param frame_count int
--- @param renderer_has_textures any
function ImFontAtlasUpdateNewFrame(atlas, frame_count, renderer_has_textures)
assert(atlas.Builder == nil or atlas.Builder.FrameCount < frame_count) -- Protection against being called twice.
atlas.RendererHasTextures = renderer_has_textures
-- Update texture status and discard old textures.
-- (we do this first thing to handle an edge case: if user mistakenly calls ClearFonts()+SetStatus(OK) during
-- rendering, it would ImFontAtlasBuildMain() rebuilding before tex.Updates[] gets a chance to be cleared)
-- (if somehow we need to move this back lower in the function, we could manually call the code to clear Updates[]).
local tex_n = 1
while tex_n <= atlas.TexList.Size do
-- Update and remove if requested
local tex = atlas.TexList.Data[tex_n]
if (tex.Status == 2 --[[ImTextureStatus.WantCreate]] and atlas.RendererHasTextures) then
assert(tex.TexID == (-1) --[[ImTextureID_Invalid]] and tex.BackendUserData == nil, "Backend set texture's TexID/BackendUserData but did not update Status to OK.")
end
local remove_from_list = ImTextureDataUpdateNewFrame(tex)
if remove_from_list then
assert(atlas.TexData ~= tex)
tex:DestroyPixels()
atlas.TexList:erase(tex_n)
tex_n = tex_n - 1
end
tex_n = tex_n + 1
end
-- Check that font atlas was built or backend support texture reload in which case we can build now
if (atlas.RendererHasTextures) then
atlas.TexIsBuilt = true
if (atlas.Builder == nil) then -- This will only happen if fonts were not already loaded.
ImFontAtlasBuildMain(atlas)
end
end
-- Legacy backend
if (not atlas.RendererHasTextures) then
IM_ASSERT_USER_ERROR(atlas.TexIsBuilt, "Backend does not support ImGuiBackendFlags.RendererHasTextures, and font atlas is not built! Update backend OR make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8().")
end
if (atlas.TexIsBuilt and atlas.Builder.PreloadedAllGlyphsRanges) then
IM_ASSERT_USER_ERROR(atlas.RendererHasTextures == false, "Called ImFontAtlas::Build() before ImGuiBackendFlags.RendererHasTextures got set! With new backends: you don't need to call Build().")
end
-- Clear BakedCurrent cache, this is important because it ensure the uncached path gets taken once.
-- We also rely on ImFontBaked pointers never crossing frames.
local builder = atlas.Builder
builder.FrameCount = frame_count
for _, font in atlas.Fonts:iter() do
font.LastBaked = nil
end
-- Garbage collect BakedPool
if builder.BakedDiscardedCount > 0 then
local dst_n = 1
for src_n = 1, builder.BakedPool.Size do
repeat
local p_src = builder.BakedPool.Data[src_n]
if p_src.WantDestroy then
break
end
local p_dst = builder.BakedPool.Data[dst_n]
dst_n = dst_n + 1
if p_dst == p_src then
break
end
builder.BakedPool.Data[dst_n - 1] = p_src
builder.BakedMap[p_src.BakedId] = p_src
until true
end
assert(dst_n - 1 + builder.BakedDiscardedCount == builder.BakedPool.Size)
builder.BakedPool.Size = builder.BakedPool.Size - builder.BakedDiscardedCount
builder.BakedDiscardedCount = 0
end
end
--- @param src_pixels unsigned_char[]
--- @param src_fmt ImTextureFormat
--- @param src_pitch int
--- @param dst_pixels unsigned_char[]
--- @param dst_pixels_base int
--- @param dst_fmt ImTextureFormat
--- @param dst_pitch int
--- @param w int
--- @param h int
function ImFontAtlasTextureBlockConvert(src_pixels, src_fmt, src_pitch, dst_pixels, dst_pixels_base, dst_fmt, dst_pitch, w, h)
assert(src_pixels ~= nil and dst_pixels ~= nil)
local src_pixels_base = 1
if src_fmt == dst_fmt then
local line_sz = w * ImTextureDataGetFormatBytesPerPixel(src_fmt)
for ny = h, 1, -1 do
ImStd.memmove(dst_pixels, dst_pixels_base, src_pixels, src_pixels_base, line_sz)
src_pixels_base = src_pixels_base + src_pitch
dst_pixels_base = dst_pixels_base + dst_pitch
end
elseif src_fmt == 1 --[[ImTextureFormat.Alpha8]] and dst_fmt == 0 --[[ImTextureFormat.RGBA32]] then
for ny = h, 1, -1 do
for nx = w, 1, -1 do
local alpha = src_pixels[src_pixels_base + 0]
src_pixels_base = src_pixels_base + 1
write_u32(dst_pixels, dst_pixels_base, IM_COL32(255, 255, 255, alpha))
dst_pixels_base = dst_pixels_base + 4
end
src_pixels_base = src_pixels_base + (src_pitch - w)
dst_pixels_base = dst_pixels_base + (dst_pitch - w * 4)
end
elseif src_fmt == 0 --[[ImTextureFormat.RGBA32]] and dst_fmt == 1 --[[ImTextureFormat.Alpha8]] then
for ny = h, 1, -1 do
for nx = w, 1, -1 do
local alpha = src_pixels[src_pixels_base + 3]
src_pixels_base = src_pixels_base + 4
dst_pixels[dst_pixels_base + 0] = alpha
dst_pixels_base = dst_pixels_base + 1
end
src_pixels_base = src_pixels_base + (src_pitch - w * 4)
dst_pixels_base = dst_pixels_base + (dst_pitch - w)
end
else
assert(false)
end
end
-- Source buffer may be written to (used for in-place mods).
-- Post-process hooks may eventually be added here.
--- @param data ImFontAtlasPostProcessData
function ImFontAtlasTextureBlockPostProcess(data)
-- Multiply operator (legacy)
if data.FontSrc.RasterizerMultiply ~= 1.0 then
ImFontAtlasTextureBlockPostProcessMultiply(data, data.FontSrc.RasterizerMultiply)
end
end
--- @param data ImFontAtlasPostProcessData
--- @param multiply_factor float
function ImFontAtlasTextureBlockPostProcessMultiply(data, multiply_factor)
local pixels, pixels_base = data.Pixels, data._PixelsBase
local pitch = data.Pitch
if data.Format == 1 --[[ImTextureFormat.Alpha8]] then
for ny = data.Height, 1, -1 do
local p = pixels_base
for nx = data.Width, 1, -1 do
local v = ImMin(ImFloor(pixels[p + 0] * multiply_factor), 255)
pixels[p + 0] = v
p = p + 1
end
pixels_base = pixels_base + pitch
end
elseif data.Format == 0 --[[ImTextureFormat.RGBA32]] then
for ny = data.Height, 1, -1 do
local p = pixels_base
for nx = data.Width, 1, -1 do
local a = ImMin(ImFloor(pixels[p + 3] * multiply_factor), 255)
write_u32(pixels, p, IM_COL32(pixels[p + 0], pixels[p + 1], pixels[p + 2], a))
p = p + 4
end
pixels_base = pixels_base + pitch
end
else
assert(false)
end
end
-- Copy block from one texture to another
--- @param src_tex ImTextureData
--- @param src_x int
--- @param src_y int
--- @param dst_tex ImTextureData
--- @param dst_x int
--- @param dst_y int
--- @param w int
--- @param h int
function ImFontAtlasTextureBlockCopy(src_tex, src_x, src_y, dst_tex, dst_x, dst_y, w, h)
assert(src_tex.Pixels ~= nil and dst_tex.Pixels ~= nil)
assert(src_tex.Format == dst_tex.Format)
assert(src_x >= 0 and src_x + w <= src_tex.Width)
assert(src_y >= 0 and src_y + h <= src_tex.Height)
assert(dst_x >= 0 and dst_x + w <= dst_tex.Width)
assert(dst_y >= 0 and dst_y + h <= dst_tex.Height)
for y = 0, h - 1 do
local dst, dst_base = dst_tex:GetPixelsAt(dst_x, dst_y + y)
local src, src_base = src_tex:GetPixelsAt(src_x, src_y + y)
ImStd.memmove(dst, dst_base, src, src_base, w * dst_tex.BytesPerPixel)
end
end
--- @param dst_tex ImTextureData
--- @param col ImU32
function ImFontAtlasTextureBlockFill(dst_tex, dst_x, dst_y, w, h, col)
if dst_tex.Format == 1 --[[ImTextureFormat.Alpha8]] then
local col_a = bit32.band(bit32.rshift(col, 24 --[[IM_COL32_A_SHIFT]]), 0xFF)
for y = 0, h - 1 do
local p, base = dst_tex:GetPixelsAt(dst_x, dst_y + y)
for i = base, base + w - 1 do p[i] = col_a end
end
else
-- [Luau] Pixels is a byte table: write the 4 bytes of the little-endian ImU32
local c0, c1, c2, c3 = bit32.band(col, 0xFF), bit32.band(bit32.rshift(col, 8), 0xFF), bit32.band(bit32.rshift(col, 16), 0xFF), bit32.band(bit32.rshift(col, 24), 0xFF)
for y = 0, h - 1 do
local p, i = dst_tex:GetPixelsAt(dst_x, dst_y + y)
for _ = w, 1, -1 do
p[i] = c0; p[i + 1] = c1; p[i + 2] = c2; p[i + 3] = c3
i = i + 4
end
end
end
end
-- Queue texture block update for renderer backend
--- @param tex ImTextureData
--- @param x int
--- @param y int
--- @param w int
--- @param h int
local function ImTextureDataQueueUpload(tex, x, y, w, h)
assert(tex.Status ~= 4 --[[ImTextureStatus.WantDestroy]] and tex.Status ~= 1 --[[ImTextureStatus.Destroyed]])
assert(x >= 0 and x <= 0xFFFF and y >= 0 and y <= 0xFFFF and w >= 0 and x + w <= 0x10000 and h >= 0 and y + h <= 0x10000)
local req = ImTextureRect(x, y, w, h) -- (unsigned short)
local new_x1 = ImMax(tex.UpdateRect.w == 0 and 0 or tex.UpdateRect.x + tex.UpdateRect.w, req.x + req.w)
local new_y1 = ImMax(tex.UpdateRect.h == 0 and 0 or tex.UpdateRect.y + tex.UpdateRect.h, req.y + req.h)
tex.UpdateRect.x = ImMin(tex.UpdateRect.x, req.x)
tex.UpdateRect.y = ImMin(tex.UpdateRect.y, req.y)
tex.UpdateRect.w = (new_x1 - tex.UpdateRect.x) -- (unsigned short)
tex.UpdateRect.h = (new_y1 - tex.UpdateRect.y) -- (unsigned short)
tex.UsedRect.x = ImMin(tex.UsedRect.x, req.x)
tex.UsedRect.y = ImMin(tex.UsedRect.y, req.y)
tex.UsedRect.w = (ImMax(tex.UsedRect.x + tex.UsedRect.w, req.x + req.w) - tex.UsedRect.x) -- (unsigned short)
tex.UsedRect.h = (ImMax(tex.UsedRect.y + tex.UsedRect.h, req.y + req.h) - tex.UsedRect.y) -- (unsigned short)
-- No need to queue if status is == ImTextureStatus.WantCreate
if (tex.Status == 0 --[[ImTextureStatus.OK]] or tex.Status == 3 --[[ImTextureStatus.WantUpdates]]) then
tex.Status = 3 --[[ImTextureStatus.WantUpdates]]
tex.Updates:push_back(req)
end
end
--- @param atlas ImFontAtlas
--- @param tex ImTextureData
--- @param x int
--- @param y int
--- @param w int
--- @param h int
function ImFontAtlasTextureBlockQueueUpload(atlas, tex, x, y, w, h)
ImTextureDataQueueUpload(tex, x, y, w, h)
atlas.TexIsBuilt = false
end
-- Copy to texture, post-process and queue update for backend
--- @param atlas ImFontAtlas
--- @param baked ImFontBaked
--- @param src ImFontConfig
--- @param glyph ImFontGlyph
--- @param r ImTextureRect
--- @param src_pixels unsigned_char[]
--- @param src_fmt ImTextureFormat
--- @param src_pitch int
local function ImFontAtlasBakedSetFontGlyphBitmap(atlas, baked, src, glyph, r, src_pixels, src_fmt, src_pitch)
local tex = atlas.TexData
assert(r.x + r.w <= tex.Width and r.y + r.h <= tex.Height)
local out_p, out_p_base = tex:GetPixelsAt(r.x, r.y)
assert(out_p ~= nil)
ImFontAtlasTextureBlockConvert(src_pixels, src_fmt, src_pitch, out_p, out_p_base, tex.Format, tex:GetPitch(), r.w, r.h)
local pp_data = ImFontAtlasPostProcessData(atlas, baked.OwnerFont, src, baked, glyph, out_p, out_p_base, tex.Format, tex:GetPitch(), r.w, r.h)
ImFontAtlasTextureBlockPostProcess(pp_data)
ImFontAtlasTextureBlockQueueUpload(atlas, tex, r.x, r.y, r.w, r.h)
end
local function ImFontAtlasBuildGetOversampleFactors(src, baked)
-- (Only used by stb_truetype builder)
-- Automatically disable horizontal oversampling over size 36
local raster_size = baked.Size * baked.RasterizerDensity * src.RasterizerDensity
local out_oversample_h
if src.OversampleH ~= 0 then
out_oversample_h = src.OversampleH
elseif raster_size > 36.0 or src.PixelSnapH then
out_oversample_h = 1
else
out_oversample_h = 2
end
local out_oversample_v = (src.OversampleV ~= 0) and src.OversampleV or 1
return out_oversample_h, out_oversample_v
end
-- use unused_frames==0 to discard everything.
local function ImFontAtlasBuildDiscardBakes(atlas, unused_frames)
local builder = atlas.Builder
for baked_n = 1, builder.BakedPool.Size do
repeat
local baked = builder.BakedPool[baked_n]
if (baked.LastUsedFrame + unused_frames > atlas.Builder.FrameCount) then
break
end
if (baked.WantDestroy or (bit32.band(baked.OwnerFont.Flags, 8 --[[ImFontFlags.LockBakedSizes]]) ~= 0)) then
break
end
ImFontAtlasBakedDiscard(atlas, baked.OwnerFont, baked)
until true
end
end
-- Those functions are designed to facilitate changing the underlying structures for ImFontAtlas to store an array of ImDrawListSharedData
--- @param atlas ImFontAtlas
--- @param data ImDrawListSharedData
function ImFontAtlasAddDrawListSharedData(atlas, data)
assert(not atlas.DrawListSharedDatas:contains(data))
atlas.DrawListSharedDatas:push_back(data)
end
--- @param atlas ImFontAtlas
--- @param data ImDrawListSharedData
function ImFontAtlasRemoveDrawListSharedData(atlas, data)
assert(atlas.DrawListSharedDatas:contains(data))
atlas.DrawListSharedDatas:find_erase(data)
end
--- @param atlas ImFontAtlas
--- @param w int
--- @param h int
local function ImFontAtlasTextureRepack(atlas, w, h)
local builder = atlas.Builder
builder.LockDisableResize = true
local old_tex = atlas.TexData
local new_tex = ImFontAtlasTextureAdd(atlas, w, h)
new_tex.UseColors = old_tex.UseColors
IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: resize+repack %dx%d => Texture #%03d: %dx%d", old_tex.UniqueID, old_tex.Width, old_tex.Height, new_tex.UniqueID, new_tex.Width, new_tex.Height)
--for baked_n = 1, builder.BakedPool.Size do
-- IMGUI_DEBUG_LOG_FONT("[font] - Baked %.2fpx, %d glyphs, want_destroy=%d", builder.BakedPool[baked_n].FontSize, builder.BakedPool[baked_n].Glyphs.Size, builder.BakedPool[baked_n].WantDestroy)
--end
--IMGUI_DEBUG_LOG_FONT("[font] - Old packed rects: %d, area %d px", builder.RectsPackedCount, builder.RectsPackedSurface)
--ImFontAtlasDebugWriteTexToDisk(old_tex, "Before Pack")
-- Repack, lose discarded rectangle, copy pixels
-- FIXME-NEWATLAS: This is unstable because packing order is based on RectsIndex
-- FIXME-NEWATLAS-V2: Repacking in batch would be beneficial to packing heuristic, and fix stability.
-- FIXME-NEWATLAS-TESTS: Test calling RepackTexture with size too small to fits existing rects.
ImFontAtlasPackInit(atlas)
local old_rects = ImVector()
local old_index = builder.RectsIndex:copy()
old_rects:swap(builder.Rects)
for _, index_entry in builder.RectsIndex:iter() do
repeat
if index_entry.IsUsed == false then
break
end
local old_r = old_rects[index_entry.TargetIndex + 1]
if (old_r.w == 0 and old_r.h == 0) then
break
end
local new_r_id = ImFontAtlasPackAddRect(atlas, old_r.w, old_r.h, index_entry)
if new_r_id == (-1) --[[ImFontAtlasRectId_Invalid]] then
-- Undo, grow texture and try repacking again.
-- FIXME-NEWATLAS-TESTS: This is a very rarely exercised path! It needs to be automatically tested properly.
IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: resize failed. Will grow.", new_tex.UniqueID)
new_tex.WantDestroyNextFrame = true
builder.Rects:swap(old_rects)
builder.RectsIndex = old_index
ImFontAtlasBuildSetTexture(atlas, old_tex)
ImFontAtlasTextureGrow(atlas, w, h) -- Recurse
return
end
assert(ImFontAtlasRectId_GetIndex(new_r_id) == builder.RectsIndex:index_from_ptr(index_entry))
local new_r = ImFontAtlasPackGetRect(atlas, new_r_id)
ImFontAtlasTextureBlockCopy(old_tex, old_r.x, old_r.y, new_tex, new_r.x, new_r.y, new_r.w, new_r.h)
until true
end
assert(old_rects.Size == builder.Rects.Size + builder.RectsDiscardedCount)
builder.RectsDiscardedCount = 0
builder.RectsDiscardedSurface = 0
-- Patch glyphs UV
for baked_n = 1, builder.BakedPool.Size do
for _, glyph in builder.BakedPool[baked_n].Glyphs:iter() do
if (glyph.PackId ~= (-1) --[[ImFontAtlasRectId_Invalid]]) then
local r = ImFontAtlasPackGetRect(atlas, glyph.PackId)
glyph.U0 = (r.x) * atlas.TexUvScale.x
glyph.V0 = (r.y) * atlas.TexUvScale.y
glyph.U1 = (r.x + r.w) * atlas.TexUvScale.x
glyph.V1 = (r.y + r.h) * atlas.TexUvScale.y
end
end
end
-- Update other cached UV
ImFontAtlasBuildUpdateTexData(atlas)
builder.LockDisableResize = false
ImFontAtlasUpdateDrawListsSharedData(atlas)
--ImFontAtlasDebugWriteTexToDisk(new_tex, "After Pack")
end
function ImFontAtlasTextureGrow(atlas, old_tex_w, old_tex_h)
--ImFontAtlasDebugWriteTexToDisk(atlas.TexData, "Before Grow")
local builder = atlas.Builder
old_tex_w = old_tex_w or -1
old_tex_h = old_tex_h or -1
if (old_tex_w == -1) then
old_tex_w = atlas.TexData.Width
end
if (old_tex_h == -1) then
old_tex_h = atlas.TexData.Height
end
-- FIXME-NEWATLAS-V2: What to do when reaching limits exposed by backend?
-- FIXME-NEWATLAS-V2: Does ImFontAtlasFlags.NoPowerOfTwoHeight makes sense now? Allow 'lock' and 'compact' operations?
assert(ImIsPowerOfTwo(old_tex_w) and ImIsPowerOfTwo(old_tex_h))
assert(ImIsPowerOfTwo(atlas.TexMinWidth) and ImIsPowerOfTwo(atlas.TexMaxWidth) and ImIsPowerOfTwo(atlas.TexMinHeight) and ImIsPowerOfTwo(atlas.TexMaxHeight))
-- Grow texture so it follows roughly a square.
-- - Grow height before width, as width imply more packing nodes.
-- - Caller should be taking account of RectsDiscardedSurface and may not need to grow.
local new_tex_w = (old_tex_h <= old_tex_w) and old_tex_w or old_tex_w * 2
local new_tex_h = (old_tex_h <= old_tex_w) and old_tex_h * 2 or old_tex_h
-- Handle minimum size first (for pathologically large packed rects)
local pack_padding = atlas.TexGlyphPadding
new_tex_w = ImMax(new_tex_w, ImUpperPowerOfTwo(builder.MaxRectSize.x + pack_padding))
new_tex_h = ImMax(new_tex_h, ImUpperPowerOfTwo(builder.MaxRectSize.y + pack_padding))
new_tex_w = ImClamp(new_tex_w, atlas.TexMinWidth, atlas.TexMaxWidth)
new_tex_h = ImClamp(new_tex_h, atlas.TexMinHeight, atlas.TexMaxHeight)
if (new_tex_w == old_tex_w and new_tex_h == old_tex_h) then
return
end
ImFontAtlasTextureRepack(atlas, new_tex_w, new_tex_h)
end
--- @param atlas ImFontAtlas
local function ImFontAtlasTextureMakeSpace(atlas)
-- Can some baked contents be ditched?
--IMGUI_DEBUG_LOG_FONT("[font] ImFontAtlasBuildMakeSpace()")
local builder = atlas.Builder
ImFontAtlasBuildDiscardBakes(atlas, 2)
-- Currently using a heuristic for repack without growing.
if (builder.RectsDiscardedSurface < builder.RectsPackedSurface * 0.20) then
ImFontAtlasTextureGrow(atlas)
else
ImFontAtlasTextureRepack(atlas, atlas.TexData.Width, atlas.TexData.Height)
end
end
-- This is essentially a free-list pattern, it may be nice to wrap it into a dedicated type.
--- @param atlas ImFontAtlas
--- @param rect_idx ImFontAtlasRectId
local function ImFontAtlasPackAllocRectEntry(atlas, rect_idx)
local builder = atlas.Builder
local index_idx, index_entry
if builder.RectsIndexFreeListStart < 0 then
builder.RectsIndex:resize(builder.RectsIndex.Size + 1)
index_idx = builder.RectsIndex.Size - 1
index_entry = ImFontAtlasRectEntry()
builder.RectsIndex.Data[builder.RectsIndex.Size] = index_entry
else
index_idx = builder.RectsIndexFreeListStart
index_entry = builder.RectsIndex[index_idx + 1]
assert(index_entry.IsUsed == false and index_entry.Generation > 0) -- Generation is incremented during DiscardRect
builder.RectsIndexFreeListStart = index_entry.TargetIndex
end
index_entry.TargetIndex = rect_idx
index_entry.IsUsed = true
return ImFontAtlasRectId_Make(index_idx, index_entry.Generation)
end
-- Overwrite existing entry
--- @param atlas ImFontAtlas
--- @param index_entry ImFontAtlasRectEntry
local function ImFontAtlasPackReuseRectEntry(atlas, index_entry)
assert(index_entry.IsUsed)
index_entry.TargetIndex = atlas.Builder.Rects.Size - 1
local index_idx = atlas.Builder.RectsIndex:index_from_ptr(index_entry)
return ImFontAtlasRectId_Make(index_idx, index_entry.Generation)
end
-- This is expected to be called in batches and followed by a repack
--- @param atlas ImFontAtlas
--- @param id ImFontAtlasRectId
function ImFontAtlasPackDiscardRect(atlas, id)
assert(id ~= (-1) --[[ImFontAtlasRectId_Invalid]])
local rect = ImFontAtlasPackGetRect(atlas, id)
if (rect == nil) then
return
end
local builder = atlas.Builder
local index_idx = ImFontAtlasRectId_GetIndex(id)
local index_entry = builder.RectsIndex.Data[index_idx + 1]
assert(index_entry.IsUsed and index_entry.TargetIndex >= 0)
index_entry.IsUsed = false
index_entry.TargetIndex = builder.RectsIndexFreeListStart
index_entry.Generation = index_entry.Generation + 1
if (index_entry.Generation == 0) then
index_entry.Generation = index_entry.Generation + 1 -- Keep non-zero on overflow
end
local pack_padding = atlas.TexGlyphPadding
builder.RectsIndexFreeListStart = index_idx
builder.RectsDiscardedCount = builder.RectsDiscardedCount + 1
builder.RectsDiscardedSurface = builder.RectsDiscardedSurface + ((rect.w + pack_padding) * (rect.h + pack_padding))
rect.w = 0 rect.h = 0 -- Clear rectangle so it won't be packed again
end
-- Important: Calling this may recreate a new texture and therefore change atlas.TexData
-- FIXME-NEWFONTS: Expose other glyph padding settings for custom alteration (e.g. drop shadows). See #7962
function ImFontAtlasPackAddRect(atlas, w, h, overwrite_entry)
assert(w > 0 and w <= 0xFFFF)
assert(h > 0 and h <= 0xFFFF)
local builder = atlas.Builder
local pack_padding = atlas.TexGlyphPadding
builder.MaxRectSize.x = ImMax(builder.MaxRectSize.x, w)
builder.MaxRectSize.y = ImMax(builder.MaxRectSize.y, h)
-- Pack
local r = ImTextureRect(0, 0, w, h)
for attempts_remaining = 3, 0, -1 do
-- Try packing
local pack_r = stbrp.rect()
pack_r.w = w + pack_padding
pack_r.h = h + pack_padding
stbrp.pack_rects(builder.PackContext, {pack_r}, 1)
r.x = pack_r.x -- (unsigned short)
r.y = pack_r.y -- (unsigned short)
if pack_r.was_packed == 1 then
break
end
-- If we ran out of attempts, return fallback
if (attempts_remaining == 0 or builder.LockDisableResize) then
IMGUI_DEBUG_LOG_FONT("[font] Failed packing %dx%d rectangle. Returning fallback.", w, h)
return (-1) --[[ImFontAtlasRectId_Invalid]]
end
-- Resize or repack atlas! (this should be a rare event)
ImFontAtlasTextureMakeSpace(atlas)
end
builder.MaxRectBounds.x = ImMax(builder.MaxRectBounds.x, r.x + r.w + pack_padding)
builder.MaxRectBounds.y = ImMax(builder.MaxRectBounds.y, r.y + r.h + pack_padding)
builder.RectsPackedCount = builder.RectsPackedCount + 1
builder.RectsPackedSurface = builder.RectsPackedSurface + (w + pack_padding) * (h + pack_padding)
builder.Rects:push_back(r)
if (overwrite_entry ~= nil) then
return ImFontAtlasPackReuseRectEntry(atlas, overwrite_entry) -- Write into an existing entry instead of adding one (used during repack)
else
return ImFontAtlasPackAllocRectEntry(atlas, builder.Rects.Size - 1)
end
end
-- Generally for non-user facing functions: assert on invalid ID.
--- @param atlas ImFontAtlas
--- @param id ImFontAtlasRectId
--- @return ImTextureRect
function ImFontAtlasPackGetRect(atlas, id)
assert(id ~= (-1) --[[ImFontAtlasRectId_Invalid]])
local index_idx = ImFontAtlasRectId_GetIndex(id)
local builder = atlas.Builder
local index_entry = builder.RectsIndex.Data[index_idx + 1]
assert(index_entry.Generation == ImFontAtlasRectId_GetGeneration(id))
assert(index_entry.IsUsed)
--- @type ImTextureRect
return builder.Rects.Data[index_entry.TargetIndex + 1]
end
-- For user-facing functions: return nil on invalid ID.
-- Important: return value is valid until next call to AddRect(), e.g. FindGlyph(), CalcTextSize() can all potentially invalidate previous rects.
--- @param atlas ImFontAtlas
--- @param id ImFontAtlasRectId
function ImFontAtlasPackGetRectSafe(atlas, id)
if id == (-1) --[[ImFontAtlasRectId_Invalid]] then
return nil
end
local index_idx = ImFontAtlasRectId_GetIndex(id)
if atlas.Builder == nil then
ImFontAtlasBuildInit(atlas)
end
local builder = atlas.Builder
if index_idx >= builder.RectsIndex.Size then
return nil
end
local index_entry = builder.RectsIndex.Data[index_idx + 1]
if (index_entry.Generation ~= ImFontAtlasRectId_GetGeneration(id) or not index_entry.IsUsed) then
return nil
end
return builder.Rects.Data[index_entry.TargetIndex + 1]
end
-- Important! This assume by ImFontConfig.GlyphExcludeRanges[] is a SMALL ARRAY (e.g. <10 entries)
-- Use "Input Glyphs Overlap Detection Tool" to display a list of glyphs provided by multiple sources in order to set this array up.
--- @param src ImFontConfig
--- @param codepoint ImWchar
--- @return bool
function ImFontAtlasBuildAcceptCodepointForSource(src, codepoint)
local exclude_list = src.GlyphExcludeRanges
if exclude_list then
local i = 1
while exclude_list[i] ~= 0 do
if codepoint >= exclude_list[i] and codepoint <= exclude_list[i + 1] then
return false
end
i = i + 2
end
end
return true
end
--- @param baked ImFontBaked
--- @param new_size int
function ImFontBaked_BuildGrowIndex(baked, new_size)
assert(baked.IndexAdvanceX.Size == baked.IndexLookup.Size)
if (new_size <= baked.IndexLookup.Size) then
return
end
baked.IndexAdvanceX:resize(new_size, -1.0)
baked.IndexLookup:resize(new_size, IM_FONTGLYPH_INDEX_UNUSED)
end
-------------------------------------------------------------------------------
-- [SECTION] ImFontAtlas: backend for stb_truetype
-------------------------------------------------------------------------------
-- (imstb_truetype.lua in included near the top of this file)
-------------------------------------------------------------------------------
-- One for each ConfigData
--- @class ImGui_ImplStbTrueType_FontSrcData
--- @field FontInfo stbtt_fontinfo
--- @field ScaleFactor float
--- @return ImGui_ImplStbTrueType_FontSrcData
--- @nodiscard
local function ImGui_ImplStbTrueType_FontSrcData()
return {
FontInfo = stbtt.fontinfo(),
ScaleFactor = nil
}
end
-- Need this in Lua since I don't want to include 'stbrp' just for context constructor
-- in ImFontAtlasBuilder def code. The cpp code has 'stbrp_context_opaque' which I don't/can't have here
--- @param atlas ImFontAtlas
local function ImGui_ImplStbTrueType_LoaderInit(atlas)
atlas.Builder.PackContext = atlas.Builder.PackContext or stbrp.context()
end
local function ImGui_ImplStbTrueType_FontSrcInit(atlas, src)
-- IM_UNUSED(atlas)
--- @type ImGui_ImplStbTrueType_FontSrcData?
local bd_font_data = ImGui_ImplStbTrueType_FontSrcData()
assert(src.FontLoaderData == nil)
-- Initialize helper structure for font loading and verify that the TTF/OTF data is correct
local font_offset = stbtt.GetFontOffsetForIndex(src.FontData, src.FontNo)
if font_offset < 0 then
bd_font_data = nil
IM_ASSERT_USER_ERROR(0, "stbtt_GetFontOffsetForIndex(): FontData is incorrect, or FontNo cannot be found.")
return false
end
if (not stbtt.InitFont(bd_font_data.FontInfo, src.FontData, font_offset)) then
bd_font_data = nil
IM_ASSERT_USER_ERROR(0, "stbtt_InitFont(): failed to parse FontData. It is correct and complete? Check FontDataSize.")
return false
end
src.FontLoaderData = bd_font_data
local ref_size = src.DstFont.Sources[1].SizePixels
if (src.MergeMode and src.SizePixels == 0.0) then
src.SizePixels = ref_size
end
bd_font_data.ScaleFactor = stbtt.ScaleForPixelHeight(bd_font_data.FontInfo, 1.0)
if (src.MergeMode and src.SizePixels ~= 0.0 and ref_size ~= 0.0) then
bd_font_data.ScaleFactor = bd_font_data.ScaleFactor * (src.SizePixels / ref_size) -- FIXME-NEWATLAS: Should tidy up that a bit
end
bd_font_data.ScaleFactor = bd_font_data.ScaleFactor * src.ExtraSizeScale
return true
end
local function ImGui_ImplStbTrueType_FontSrcDestroy(atlas, src)
-- IM_UNUSED(atlas)
src.FontLoaderData = nil
end
local function ImGui_ImplStbTrueType_FontSrcContainsGlyph(atlas, src, codepoint)
-- IM_UNUSED(atlas)
local bd_font_data = src.FontLoaderData
assert(bd_font_data ~= nil)
local glyph_index = stbtt.FindGlyphIndex(bd_font_data.FontInfo, codepoint)
return (glyph_index ~= 0)
end
local function ImGui_ImplStbTrueType_FontBakedInit(atlas, src, baked)
-- IM_UNUSED(atlas)
local bd_font_data = src.FontLoaderData
if (src.MergeMode == false) then
-- FIXME-NEWFONTS: reevaluate how to use sizing metrics
-- FIXME-NEWFONTS: make use of line gap value
local scale_for_layout = bd_font_data.ScaleFactor * baked.Size / src.ExtraSizeScale
local unscaled_ascent, unscaled_descent, unscaled_line_gap = stbtt.GetFontVMetrics(bd_font_data.FontInfo)
baked.Ascent = ImCeil(unscaled_ascent * scale_for_layout)
baked.Descent = ImFloor(unscaled_descent * scale_for_layout)
-- FIXME: round to rasterizer density? (#9348)
end
return true
end
--- @param atlas ImFontAtlas
--- @param src ImFontConfig
--- @param baked ImFontBaked
--- @param _ any
--- @param codepoint ImWchar
--- @param out_glyph ImFontGlyph
--- @param advance_x? float
--- @return bool
--- @return float? out_advance_x # Updated `advance_x`
local function ImGui_ImplStbTrueType_FontBakedLoadGlyph(atlas, src, baked, _, codepoint, out_glyph, advance_x)
-- Search for first font which has the glyph
local bd_font_data = src.FontLoaderData
assert(bd_font_data ~= nil)
local glyph_index = stbtt.FindGlyphIndex(bd_font_data.FontInfo, codepoint)
if (glyph_index == 0) then
return false, advance_x
end
-- Fonts unit to pixels
local oversample_h, oversample_v = ImFontAtlasBuildGetOversampleFactors(src, baked)
local scale_for_layout = bd_font_data.ScaleFactor * baked.Size
local rasterizer_density = src.RasterizerDensity * baked.RasterizerDensity
local scale_for_raster_x = bd_font_data.ScaleFactor * baked.Size * rasterizer_density * oversample_h
local scale_for_raster_y = bd_font_data.ScaleFactor * baked.Size * rasterizer_density * oversample_v
-- Obtain size and advance
local x0, y0, x1, y1 = stbtt.GetGlyphBitmapBoxSubpixel(bd_font_data.FontInfo, glyph_index, scale_for_raster_x, scale_for_raster_y, 0, 0)
local advance, lsb = stbtt.GetGlyphHMetrics(bd_font_data.FontInfo, glyph_index)
-- Load metrics only mode
if (advance_x ~= nil) then
assert(out_glyph == nil)
advance_x = advance * scale_for_layout
return true, advance_x
end
-- Prepare glyph
out_glyph.Codepoint = codepoint
out_glyph.AdvanceX = advance * scale_for_layout
-- Pack and retrieve position inside texture atlas
-- (generally based on stbtt_PackFontRangesRenderIntoRects)
local is_visible = (x0 ~= x1 and y0 ~= y1)
if is_visible then
local w = (x1 - x0 + oversample_h - 1)
local h = (y1 - y0 + oversample_v - 1)
local pack_id = ImFontAtlasPackAddRect(atlas, w, h)
if (pack_id == (-1) --[[ImFontAtlasRectId_Invalid]]) then
-- Pathological out of memory case (TexMaxWidth/TexMaxHeight set too small?)
assert(pack_id ~= (-1) --[[ImFontAtlasRectId_Invalid]], "Out of texture memory.")
return false, advance_x
end
local r = ImFontAtlasPackGetRect(atlas, pack_id)
-- Render
x0, y0, x1, y1 = stbtt.GetGlyphBitmapBox(bd_font_data.FontInfo, glyph_index, scale_for_raster_x, scale_for_raster_y)
local builder = atlas.Builder
builder.TempBuffer:resize(w * h * 1)
local bitmap_pixels = builder.TempBuffer.Data
ImStd.memset(bitmap_pixels, 0, w * h * 1)
-- Render with oversampling
-- (those functions conveniently assert if pixels are not cleared, which is another safety layer)
local sub_x, sub_y = stbtt.MakeGlyphBitmapSubpixelPrefilter(bd_font_data.FontInfo, bitmap_pixels, w, h, w,
scale_for_raster_x, scale_for_raster_y, 0, 0, oversample_h, oversample_v, glyph_index)
local ref_size = baked.OwnerFont.Sources[1].SizePixels
local offsets_scale = (ref_size ~= 0.0) and (baked.Size / ref_size) or 1.0
local font_off_x = ImFloor(src.GlyphOffset.x * offsets_scale + 0.5) -- Snap scaled offset.
local font_off_y = ImFloor(src.GlyphOffset.y * offsets_scale + 0.5)
font_off_x = font_off_x + sub_x
font_off_y = font_off_y + (sub_y + IM_ROUND(baked.Ascent))
local recip_h = 1.0 / (oversample_h * rasterizer_density)
local recip_v = 1.0 / (oversample_v * rasterizer_density)
-- Register glyph
-- r.x r.y are coordinates inside texture (in pixels)
-- glyph.X0, glyph.Y0 are drawing coordinates from base text position, and accounting for oversampling.
out_glyph.X0 = x0 * recip_h + font_off_x
out_glyph.Y0 = y0 * recip_v + font_off_y
out_glyph.X1 = (x0 + r.w) * recip_h + font_off_x
out_glyph.Y1 = (y0 + r.h) * recip_v + font_off_y
out_glyph.Visible = true
out_glyph.PackId = pack_id
ImFontAtlasBakedSetFontGlyphBitmap(atlas, baked, src, out_glyph, r, bitmap_pixels, 1 --[[ImTextureFormat.Alpha8]], w)
end
return true, advance_x
end
local function ImFontAtlasGetFontLoaderForStbTruetype()
local loader = ImFontLoader()
loader.Name = "stb_truetype"
loader.LoaderInit = ImGui_ImplStbTrueType_LoaderInit
loader.FontSrcInit = ImGui_ImplStbTrueType_FontSrcInit
loader.FontSrcDestroy = ImGui_ImplStbTrueType_FontSrcDestroy
loader.FontSrcContainsGlyph = ImGui_ImplStbTrueType_FontSrcContainsGlyph
loader.FontBakedInit = ImGui_ImplStbTrueType_FontBakedInit
loader.FontBakedDestroy = nil
loader.FontBakedLoadGlyph = ImGui_ImplStbTrueType_FontBakedLoadGlyph
return loader
end
--- @param atlas ImFontAtlas
--- @param baked ImFontBaked
function ImFontAtlasBuildSetupFontBakedBlanks(atlas, baked)
-- Mark space as always hidden (not strictly correct/necessary. but some e.g. icons fonts don't have a space. it tends to look neater in previews)
local space_glyph = baked:FindGlyphNoFallback(32)
if space_glyph ~= nil then
space_glyph.Visible = false
end
-- Setup Tab character.
-- FIXME: Needs proper TAB handling but it needs to be contextualized (or we could arbitrary say that each string starts at "column 0" ?)
if baked:FindGlyphNoFallback(9) == nil and space_glyph ~= nil then
local tab_glyph = ImFontGlyph()
tab_glyph.Codepoint = 9
tab_glyph.AdvanceX = space_glyph.AdvanceX * 4 --[[IM_TABSIZE]]
ImFontAtlasBakedAddFontGlyph(atlas, baked, nil, tab_glyph)
end
end
--- @param glyph ImFontGlyph
function ImFontAtlasBakedDiscardFontGlyph(atlas, font, baked, glyph)
if glyph.PackId ~= (-1) --[[ImFontAtlasRectId_Invalid]] then
ImFontAtlasPackDiscardRect(atlas, glyph.PackId)
glyph.PackId = (-1) --[[ImFontAtlasRectId_Invalid]]
end
local c = glyph.Codepoint
assert(font.FallbackChar ~= c and font.EllipsisChar ~= c) -- Unsupported for simplicity
baked.IndexLookup.Data[c + 1] = IM_FONTGLYPH_INDEX_UNUSED
baked.IndexAdvanceX.Data[c + 1] = baked.FallbackAdvanceX
end
--- @param atlas ImFontAtlas
--- @param font ImFont
--- @param font_size float
--- @param font_rasterizer_density float
--- @param baked_id ImGuiID
--- @return ImFontBaked?
function ImFontAtlasBakedAdd(atlas, font, font_size, font_rasterizer_density, baked_id)
IMGUI_DEBUG_LOG_FONT("[font] Created baked %.2fpx", font_size)
local baked = atlas.Builder.BakedPool:push_back(ImFontBaked())
baked.Size = font_size
baked.RasterizerDensity = font_rasterizer_density
baked.BakedId = baked_id
baked.OwnerFont = font
baked.LastUsedFrame = atlas.Builder.FrameCount
-- Initialize backend data
local loader_data_size = 0
for _, src in font.Sources:iter() do -- Cannot easily be cached as we allow changing backend
local loader = src.FontLoader and src.FontLoader or atlas.FontLoader
loader_data_size = loader_data_size + loader.FontBakedSrcLoaderDataSize
end
-- baked.FontLoaderDatas = (loader_data_size > 0) and IM_ALLOC(loader_data_size) or nil
-- local loader_data_p = baked->FontLoaderDatas
for _, src in font.Sources:iter() do
local loader = src.FontLoader and src.FontLoader or atlas.FontLoader
if (loader.FontBakedInit) then
loader.FontBakedInit(atlas, src, baked--[[, loader_data_p--]])
end
-- loader_data_p += loader->FontBakedSrcLoaderDataSize
end
ImFontAtlasBuildSetupFontBakedBlanks(atlas, baked)
return baked
end
-- FIXME-OPT: This is not a fast query. Adding a BakedCount field in Font might allow to take a shortcut for the most common case.
--- @param atlas ImFontAtlas
--- @param font ImFont
--- @param font_size float
--- @param font_rasterizer_density float
--- @return ImFontBaked?
function ImFontAtlasBakedGetClosestMatch(atlas, font, font_size, font_rasterizer_density)
local builder = atlas.Builder
for step_n = 0, 1 do
local closest_larger_match
local closest_smaller_match
for baked_n = 1, builder.BakedPool.Size do
repeat
local baked = builder.BakedPool[baked_n]
if (baked.OwnerFont ~= font or baked.WantDestroy) then
break
end
if (step_n == 0 and baked.RasterizerDensity ~= font_rasterizer_density) then -- First try with same density
break
end
if (baked.Size > font_size and (closest_larger_match == nil or baked.Size < closest_larger_match.Size)) then
closest_larger_match = baked
end
if (baked.Size < font_size and (closest_smaller_match == nil or baked.Size > closest_smaller_match.Size)) then
closest_smaller_match = baked
end
until true
end
if (closest_larger_match) then
if (closest_smaller_match == nil or (closest_larger_match.Size >= font_size * 2.0 and closest_smaller_match.Size > font_size * 0.5)) then
return closest_larger_match
end
end
if (closest_smaller_match) then
return closest_smaller_match
end
end
return nil
end
--- @param atlas ImFontAtlas
--- @param font ImFont
--- @param baked ImFontBaked
function ImFontAtlasBakedDiscard(atlas, font, baked)
local builder = atlas.Builder
for _, glyph in baked.Glyphs:iter() do
if glyph.PackId ~= (-1) --[[ImFontAtlasRectId_Invalid]] then
ImFontAtlasPackDiscardRect(atlas, glyph.PackId)
end
end
-- char* loader_data_p = (char*)baked->FontLoaderDatas
for _, src in font.Sources:iter() do
local loader = src.FontLoader and src.FontLoader or atlas.FontLoader
if loader.FontBakedDestroy then
loader.FontBakedDestroy(atlas, src, baked)
end
end
if baked.FontLoaderDatas then
baked.FontLoaderDatas = nil
end
builder.BakedMap[baked.BakedId] = nil
builder.BakedDiscardedCount = builder.BakedDiscardedCount + 1
baked:ClearOutputData()
baked.WantDestroy = true
font.LastBaked = nil
end
-- use unused_frames==0 to discard everything.
function ImFontAtlasFontDiscardBakes(atlas, font, unused_frames)
local builder = atlas.Builder
if builder then -- This can be called from font destructor
for baked_n = 1, builder.BakedPool.Size do
repeat
local baked = builder.BakedPool[baked_n]
if baked.LastUsedFrame + unused_frames > atlas.Builder.FrameCount then
break
end
if (baked.OwnerFont ~= font) or baked.WantDestroy then
break
end
ImFontAtlasBakedDiscard(atlas, font, baked)
until true
end
end
end
-- Was tempted to lazily init FontSrc but wouldn't save much + makes it more complicated to detect invalid data at AddFont()
function ImFontAtlasFontInitOutput(atlas, font)
local ret = true
for _, src in font.Sources:iter() do
if not ImFontAtlasFontSourceInit(atlas, src) then
ret = false
end
end
assert(ret) -- Unclear how to react to this meaningfully. Assume that result will be same as initial AddFont() call.
return ret
end
-- Keep source/input FontData
function ImFontAtlasFontDestroyOutput(atlas, font)
font:ClearOutputData()
for _, src in font.Sources:iter() do
local loader = src.FontLoader and src.FontLoader or atlas.FontLoader
if loader and loader.FontSrcDestroy ~= nil then
loader.FontSrcDestroy(atlas, src)
end
end
end
function ImFontAtlasFontRebuildOutput(atlas, font)
ImFontAtlasFontDestroyOutput(atlas, font)
ImFontAtlasFontInitOutput(atlas, font)
end
--- @param atlas ImFontAtlas
--- @param src ImFontConfig
--- @return boolean
function ImFontAtlasFontSourceInit(atlas, src)
local loader = (src.FontLoader) and src.FontLoader or atlas.FontLoader
if (loader.FontSrcInit ~= nil and not loader.FontSrcInit(atlas, src)) then
return false
end
return true
end
--- @param atlas ImFontAtlas
--- @param font ImFont
--- @param src ImFontConfig
function ImFontAtlasFontSourceAddToFont(atlas, font, src)
if src.MergeMode == false then
font:ClearOutputData()
--font.FontSize = src.SizePixels
font.OwnerAtlas = atlas
assert(font.Sources[1] == src)
end
atlas.TexIsBuilt = false -- For legacy backends
ImFontAtlasBuildSetupFontSpecialGlyphs(atlas, font, src)
end
--- @param atlas ImFontAtlas
--- @param src ImFontConfig
function ImFontAtlasFontDestroySourceData(atlas, src)
-- IM_UNUSED(atlas)
-- if src.FontDataOwnedByAtlas then
-- IM_FREE(src.FontData)
-- end
src.FontData = nil
-- if src.GlyphExcludeRanges then
-- IM_FREE(src.GlyphExcludeRanges)
-- end
src.GlyphExcludeRanges = nil
end
-- Load/identify special glyphs
-- (note that this is called again for fonts with MergeMode)
--- @param atlas ImFontAtlas
--- @param font ImFont
--- @param src ImFontConfig
function ImFontAtlasBuildSetupFontSpecialGlyphs(atlas, font, src)
-- IM_UNUSED(atlas)
assert(font.Sources:contains(src))
-- Find Fallback character. Actual glyph loaded in GetFontBaked().
--- @type ImWchar[]
local fallback_chars = {font.FallbackChar, 65533 --[[IM_UNICODE_CODEPOINT_INVALID]], 63, 32}
if font.FallbackChar == 0 then
for _, candidate_char in ipairs(fallback_chars) do
if candidate_char ~= 0 and font:IsGlyphInFont(candidate_char) then
font.FallbackChar = candidate_char
break
end
end
end
-- Setup Ellipsis character. It is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis).
-- However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character.
-- FIXME: Note that 0x2026 is rarely included in our font ranges. Because of this we are more likely to use three individual dots.
--- @type ImWchar[]
local ellipsis_chars = {src.EllipsisChar, 0x2026, 0x0085}
if font.EllipsisChar == 0 then
for _, candidate_char in ipairs(ellipsis_chars) do
if candidate_char ~= 0 and font:IsGlyphInFont(candidate_char) then
font.EllipsisChar = candidate_char
break
end
end
end
if font.EllipsisChar == 0 then
font.EllipsisChar = 0x0085
font.EllipsisAutoBake = true
end
end
-- Create a compact, baked "..." if it doesn't exist, by using the ".".
-- This may seem overly complicated right now but the point is to exercise and improve a technique which should be increasingly used.
-- FIXME-NEWATLAS: This borrows too much from FontLoader's FontLoadGlyph() handlers and suggest that we should add further helpers.
--- @param atlas ImFontAtlas
--- @param baked ImFontBaked
--- @return ImFontGlyph?
function ImFontAtlasBuildSetupFontBakedEllipsis(atlas, baked)
local font = baked.OwnerFont
assert(font.EllipsisChar ~= 0)
local dot_glyph = baked:FindGlyphNoFallback(46) -- '.'
if (dot_glyph == nil) then
dot_glyph = baked:FindGlyphNoFallback(0xFF0E)
end
if (dot_glyph == nil) then
return nil
end
local dot_r_id = dot_glyph.PackId -- Deep copy to avoid invalidation of glyphs and rect pointers
local dot_r = ImFontAtlasPackGetRect(atlas, dot_r_id)
local dot_spacing = 1
local dot_step = (dot_glyph.X1 - dot_glyph.X0) + dot_spacing
local pack_id = ImFontAtlasPackAddRect(atlas, (dot_r.w * 3 + dot_spacing * 2), dot_r.h)
local r = ImFontAtlasPackGetRect(atlas, pack_id)
local glyph = ImFontGlyph()
glyph.Codepoint = font.EllipsisChar
glyph.AdvanceX = ImMax(dot_glyph.AdvanceX, dot_glyph.X0 + dot_step * 3.0 - dot_spacing) -- FIXME: Slightly odd for normally mono-space fonts but since this is used for trailing contents.
glyph.X0 = dot_glyph.X0
glyph.Y0 = dot_glyph.Y0
glyph.X1 = dot_glyph.X0 + dot_step * 3 - dot_spacing
glyph.Y1 = dot_glyph.Y1
glyph.Visible = true
glyph.PackId = pack_id
glyph = ImFontAtlasBakedAddFontGlyph(atlas, baked, nil, glyph)
dot_glyph = nil
dot_r = ImFontAtlasPackGetRect(atlas, dot_r_id)
local tex = atlas.TexData
for n = 0, 2 do
ImFontAtlasTextureBlockCopy(tex, dot_r.x, dot_r.y, tex, r.x + (dot_r.w + dot_spacing) * n, r.y, dot_r.w, dot_r.h)
end
ImFontAtlasTextureBlockQueueUpload(atlas, tex, r.x, r.y, r.w, r.h)
return glyph
end
-- Load fallback in order to obtain its index
-- (this is called from in hot-path so we avoid extraneous parameters to minimize impact on code size)
--- @param baked ImFontBaked
function ImFontAtlasBuildSetupFontBakedFallback(baked)
assert(baked.FallbackGlyphIndex == -1)
assert(baked.FallbackAdvanceX == 0.0)
local font = baked.OwnerFont
local fallback_glyph
if (font.FallbackChar ~= 0) then
fallback_glyph = baked:FindGlyphNoFallback(font.FallbackChar)
end
if (fallback_glyph == nil) then
local space_glyph = baked:FindGlyphNoFallback(32) -- ' '
local glyph = ImFontGlyph()
glyph.Codepoint = 0
glyph.AdvanceX = space_glyph and space_glyph.AdvanceX or IM_ROUND(baked.Size * 0.40)
fallback_glyph = ImFontAtlasBakedAddFontGlyph(font.OwnerAtlas, baked, nil, glyph)
end
baked.FallbackGlyphIndex = baked.Glyphs:index_from_ptr(fallback_glyph) + 1 -- Storing index avoid need to update pointer on growth and simplify inner loop code
baked.FallbackAdvanceX = fallback_glyph.AdvanceX
end
-- Setup main font loader for the atlas
-- Every font source (ImFontConfig) will use this unless ImFontConfig.FontLoader specify a custom loader.
function ImFontAtlasBuildSetupFontLoader(atlas, font_loader)
if atlas.FontLoader == font_loader then
return
end
assert(not atlas.Locked, "Cannot modify a locked ImFontAtlas!")
for _, font in atlas.Fonts:iter() do
ImFontAtlasFontDestroyOutput(atlas, font)
end
if atlas.Builder and atlas.FontLoader and atlas.FontLoader.LoaderShutdown then
atlas.FontLoader.LoaderShutdown(atlas)
end
atlas.FontLoader = font_loader
atlas.FontLoaderName = font_loader and font_loader.Name or "NULL"
assert(atlas.FontLoaderData == nil)
if atlas.Builder and atlas.FontLoader and atlas.FontLoader.LoaderInit then
atlas.FontLoader.LoaderInit(atlas)
end
for _, font in atlas.Fonts:iter() do
ImFontAtlasFontInitOutput(atlas, font)
end
for _, font in atlas.Fonts:iter() do
for _, src in font.Sources:iter() do
ImFontAtlasFontSourceAddToFont(atlas, font, src)
end
end
end
local function ImFontAtlasBuildUpdateRendererHasTexturesFromContext(atlas)
-- [LEGACY] Copy back the ImGuiBackendFlags.RendererHasTextures flag from ImGui context.
-- - This is the 1% exceptional case where that dependency if useful, to bypass an issue where otherwise at the
-- time of an early call to Build(), it would be impossible for us to tell if the backend supports texture update.
-- - Without this hack, we would have quite a pitfall as many legacy codebases have an early call to Build().
-- Whereas conversely, the portion of people using ImDrawList without ImGui is expected to be pathologically rare.
for _, shared_data in atlas.DrawListSharedDatas:iter() do
local imgui_ctx = shared_data.Context
if (imgui_ctx) then
atlas.RendererHasTextures = bit32.band(imgui_ctx.IO.BackendFlags, 16 --[[ImGuiBackendFlags.RendererHasTextures]]) ~= 0
break
end
end
end
local function ImFontAtlasBuildUpdatePointers(atlas)
-- for _, font in atlas.Fonts:iter() do
-- font.Sources:resize(0)
-- end
-- for _, src in atlas.Sources:iter() do
-- src.DstFont.Sources:push_back(src)
-- end
end
-- Update texture identifier in all active draw lists
--- @param atlas ImFontAtlas
--- @param old_tex ImTextureRef
--- @param new_tex ImTextureRef
local function ImFontAtlasUpdateDrawListsTextures(atlas, old_tex, new_tex)
for _, shared_data in atlas.DrawListSharedDatas:iter() do
repeat
-- If Context 2 uses font owned by Context 1 which already called EndFrame()/Render(), we don't want to mess with draw commands for Context 1
if (shared_data.Context and not shared_data.Context.WithinFrameScope) then
break
end
for _, draw_list in shared_data.DrawLists:iter() do
-- Replace in command-buffer
-- (there is not need to replace in ImDrawListSplitter: current channel is in ImDrawList's CmdBuffer[],
-- other channels will be on SetCurrentChannel() which already needs to compare CmdHeader anyhow)
if (draw_list.CmdBuffer.Size > 0 and draw_list._CmdHeader.TexRef == old_tex) then
draw_list:_SetTexture(new_tex)
end
-- Replace in stack
for i, stacked_tex in draw_list._TextureStack:iter() do
if (stacked_tex == old_tex) then
draw_list._TextureStack.Data[i] = new_tex
end
end
end
until true
end
end
-- Set current texture. This is mostly called from AddTexture() + to handle a failed resize.
function ImFontAtlasBuildSetTexture(atlas, tex)
local old_tex_ref = atlas.TexRef
atlas.TexData = tex
atlas.TexUvScale = ImVec2(1.0 / tex.Width, 1.0 / tex.Height)
atlas.TexRef._TexData = tex
--atlas.TexRef._TexID = tex.TexID -- <-- We intentionally don't do that. It would be misleading and betray promise that both fields aren't set.
ImFontAtlasUpdateDrawListsTextures(atlas, old_tex_ref, atlas.TexRef)
end
--- @param atlas ImFontAtlas
function ImFontAtlasBuildUpdateTexData(atlas)
ImFontAtlasBuildUpdateTexDataBasic(atlas)
ImFontAtlasBuildUpdateTexDataLines(atlas)
ImFontAtlasBuildUpdateTexDataCorners(atlas)
end
-- Create a new texture, discard previous one
function ImFontAtlasTextureAdd(atlas, w, h)
local old_tex = atlas.TexData
local new_tex
-- FIXME: Cannot reuse texture because old UV may have been used already (unless we remap UV).
--if (old_tex ~= nil and old_tex.Status == ImTextureStatus.WantCreate) then
-- -- Reuse texture not yet used by backend.
-- IM_ASSERT(old_tex.TexID == ImTextureID_Invalid and old_tex.BackendUserData == nil)
-- old_tex:DestroyPixels()
-- old_tex.Updates:clear()
-- new_tex = old_tex
-- old_tex = nil
--else
-- Add new
new_tex = ImTextureData()
new_tex.UniqueID = atlas.TexNextUniqueID
atlas.TexNextUniqueID = atlas.TexNextUniqueID + 1
atlas.TexList:push_back(new_tex)
if old_tex ~= nil then
-- Queue old as to destroy next frame
old_tex.WantDestroyNextFrame = true
assert(old_tex.Status == 0 --[[ImTextureStatus.OK]] or old_tex.Status == 2 --[[ImTextureStatus.WantCreate]] or old_tex.Status == 3 --[[ImTextureStatus.WantUpdates]])
end
new_tex:Create(atlas.TexDesiredFormat, w, h)
atlas.TexIsBuilt = false
ImFontAtlasBuildSetTexture(atlas, new_tex)
return new_tex
end
--- @param atlas ImFontAtlas
--- @return ImVec2
function ImFontAtlasTextureGetSizeEstimate(atlas)
local min_w = ImUpperPowerOfTwo(atlas.TexMinWidth)
local min_h = ImUpperPowerOfTwo(atlas.TexMinHeight)
if (atlas.Builder == nil or atlas.TexData == nil or atlas.TexData.Status == 4 --[[ImTextureStatus.WantDestroy]]) then
return ImVec2(min_w, min_h) -- ImVec2i
end
local builder = atlas.Builder
min_w = ImMax(ImUpperPowerOfTwo(builder.MaxRectSize.x), min_w)
min_h = ImMax(ImUpperPowerOfTwo(builder.MaxRectSize.y), min_h)
local surface_approx = builder.RectsPackedSurface - builder.RectsDiscardedSurface -- Expected surface after repack
local surface_sqrt = ImFloor(ImSqrt(surface_approx))
local new_tex_w
local new_tex_h
if (min_w >= min_h) then
new_tex_w = ImMax(min_w, ImUpperPowerOfTwo(surface_sqrt))
new_tex_h = ImMax(min_h, ImFloor((surface_approx + new_tex_w - 1) / new_tex_w))
if (bit32.band(atlas.Flags, 1 --[[ImFontAtlasFlags.NoPowerOfTwoHeight]]) == 0) then
new_tex_h = ImUpperPowerOfTwo(new_tex_h)
end
else
new_tex_h = ImMax(min_h, ImUpperPowerOfTwo(surface_sqrt))
if (bit32.band(atlas.Flags, 1 --[[ImFontAtlasFlags.NoPowerOfTwoHeight]]) == 0) then
new_tex_h = ImUpperPowerOfTwo(new_tex_h)
end
new_tex_w = ImMax(min_w, ImFloor((surface_approx + new_tex_h - 1) / new_tex_h))
end
assert(ImIsPowerOfTwo(new_tex_w) and ImIsPowerOfTwo(new_tex_h))
return ImVec2(new_tex_w, new_tex_h) -- ImVec2i
end
-- Clear all output. Invalidates all AddCustomRect() return values!
--- @param atlas ImFontAtlas
function ImFontAtlasBuildClear(atlas)
local new_tex_size = ImFontAtlasTextureGetSizeEstimate(atlas) -- ImVec2i
ImFontAtlasBuildDestroy(atlas)
ImFontAtlasTextureAdd(atlas, new_tex_size.x, new_tex_size.y)
ImFontAtlasBuildInit(atlas)
for _, src in atlas.Sources:iter() do
ImFontAtlasFontSourceInit(atlas, src)
end
for _, font in atlas.Fonts:iter() do
for _, src in font.Sources:iter() do
ImFontAtlasFontSourceAddToFont(atlas, font, src)
end
end
end
-- You should not need to call this manually!
-- If you think you do, let us know and we can advise about policies auto-compact.
--- @param atlas ImFontAtlas
function ImFontAtlasTextureCompact(atlas)
local builder = atlas.Builder
ImFontAtlasBuildDiscardBakes(atlas, 1)
local old_tex = atlas.TexData
local old_tex_size = ImVec2(old_tex.Width, old_tex.Height)
local new_tex_size = ImFontAtlasTextureGetSizeEstimate(atlas)
if (builder.RectsDiscardedCount == 0 and new_tex_size.x == old_tex_size.x and new_tex_size.y == old_tex_size.y) then
return
end
ImFontAtlasTextureRepack(atlas, new_tex_size.x, new_tex_size.y)
end
--- @param atlas ImFontAtlas
function ImFontAtlasPackInit(atlas)
local tex = atlas.TexData
local builder = atlas.Builder
-- In theory we could decide to reduce the number of nodes, e.g. halve them, and waste a little texture space, but it doesn't seem worth it.
local pack_node_count = ImFloor(tex.Width / 2)
builder.PackNodes:resize(pack_node_count)
for i = 1, builder.PackNodes.Size do builder.PackNodes.Data[i] = stbrp.node() end
stbrp.init_target(builder.PackContext, tex.Width, tex.Height, builder.PackNodes.Data, builder.PackNodes.Size)
builder.RectsPackedCount = 0
builder.RectsPackedSurface = 0
builder.MaxRectSize = ImVec2(0, 0) -- ImVec2i
builder.MaxRectBounds = ImVec2(0, 0) -- ImVec2i
end
--- @param atlas ImFontAtlas
-- #define IM_DRAWLIST_CORNER_POINTS_MAX (IM_DRAWLIST_ARCFAST_TABLE_SIZE / 4 + 3) // Max number of points in corner profiles
-- [Luau] ImVec2 line_normals[] / float line_distances[] / ImVec2 points[] are flat 1-based scratch arrays (x, y, distance).
local corner_points_x, corner_points_y = {}, {}
local corner_normals_x, corner_normals_y, corner_distances = {}, {}, {}
-- [Luau] line_normals is passed as two arrays (line_normals_x, line_normals_y), 1-based.
local function InitCornerGeometry(cx, cy, rounding, circle_segment_max_error, line_normals_x, line_normals_y, line_distances)
-- This tries to quite faithfully replicate how we render the corners using tessellation.
-- Older code sampled a circle SDF directly, but it had clearly visible discrepancy between the baked corners
-- and tessellated corners due to circle_segment_max_error.
local auto_seg_count = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(rounding, circle_segment_max_error)
local arc_step, arc_step_count = CalcCornerArcStepAndCount(auto_seg_count)
local arc_point_count = arc_step_count + 1
-- Build arc of points, with straight segments at each end.
-- . [n-2]..[n-1]
-- .`
-- .
-- [1] x (cx,cy)
-- :
-- [0]
local points_x, points_y = corner_points_x, corner_points_y
local num_points = 0
num_points = num_points + 1; points_x[num_points] = cx - rounding; points_y[num_points] = cy + 1.0
local da = (arc_step / 48 --[[IM_DRAWLIST_ARCFAST_TABLE_SIZE]]) * 3.141592653589793 --[[IM_PI]] * 2.0
local a = 3.141592653589793 --[[IM_PI]]
for _ = 0, arc_point_count - 1 do
local dx = ImCos(a)
local dy = ImSin(a)
num_points = num_points + 1; points_x[num_points] = cx + dx * rounding; points_y[num_points] = cy + dy * rounding
a = a + da
end
num_points = num_points + 1; points_x[num_points] = cx + 1.0; points_y[num_points] = cy - rounding
-- Turn segments to half spaces.
for i = 1, num_points - 1 do
local diff_x = points_x[i + 1] - points_x[i]
local diff_y = points_y[i + 1] - points_y[i]
local d2 = diff_x * diff_x + diff_y * diff_y
local inv_d = ImRsqrtPrecise(d2)
line_normals_x[i] = diff_y * inv_d -- Normal points outwards
line_normals_y[i] = -diff_x * inv_d
line_distances[i] = -(points_x[i] * line_normals_x[i] + points_y[i] * line_normals_y[i]) -- -ImDot()
end
return num_points - 1
end
-- Returns distance to convex shape limited by lines.
local function SampleConvex(x, y, line_normals_x, line_normals_y, line_distances, num_lines)
local result = -3.4028234663853e+38 --[[FLT_MAX]]
for i = 1, num_lines do
local d = line_normals_x[i] * x + line_normals_y[i] * y + line_distances[i]
if d > result then
result = d
end
end
return result
end
local function SampleCornerFillOrStroke(x, y, thickness, line_normals_x, line_normals_y, line_distances, num_lines)
local d = SampleConvex(x, y, line_normals_x, line_normals_y, line_distances, num_lines)
local aa_width = 1.0
if thickness == 0.0 then
-- Fill
return math.floor(ImClamp(1.0 - (d + aa_width * 0.5) / aa_width, 0.0, 1.0) * 255.0) -- (ImU8)
else
-- Stroke
local outer = 1.0 - (d + aa_width * 0.5) / aa_width
local inner = (d + thickness + aa_width * 0.5) / aa_width
return math.floor(ImClamp(ImMin(inner, outer), 0.0, 1.0) * 255.0) -- (ImU8)
end
end
-- [Port] #if 0 DEBUG_TEX_CORNER_U8/DEBUG_TEX_CORNER_U32 coverage visualization not ported (always 0xFF/0xFFFFFFFF).
function ImFontAtlasBuildUpdateTexDataCorners(atlas)
if bit32.band(atlas.Flags, 8 --[[ImFontAtlasFlags.NoBakedRoundCorners]]) ~= 0 then
return
end
-- Pack and store identifier so we can refresh UV coordinates on texture resize.
local tex = atlas.TexData
local builder = atlas.Builder
-- FIXME-TEXCORNERS: Ideally we should pick up the max error from the styles, but it does not seem be to set by the time we build the data the first time.
local circle_segment_max_error = 0.30 -- Default matching ImGuiStyle::CircleTessellationMaxError.
-- if (atlas->OwnerContext)
-- circle_segment_max_error = atlas->OwnerContext->DrawListSharedData.CircleSegmentMaxError;
-- Calculate size and pack
local r = ImFontAtlasRect()
local add_and_draw = (atlas:GetCustomRect(builder.PackIdCornersTexData, r) == false)
if add_and_draw then
-- (thickness 0 for filled, thickness >0 for strokes)
local pack_size_x, pack_size_y = 0, 0 -- ImVec2i
for thickness = 0, 4 --[[IM_DRAWLIST_TEX_CORNERS_THICKNESS_MAX]] - 1 do
local is_filled = (thickness == 0)
local row_width = 0
local row_height = 0
for n = 0, 16 --[[IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX]] - 1 do
local rounding = n + 1
local size = is_filled and (rounding + 1) or ImMax(rounding, thickness)
row_width = row_width + size + 2
row_height = ImMax(row_height, size + 2)
end
pack_size_x = ImMax(pack_size_x, row_width)
pack_size_y = pack_size_y + row_height
end
builder.PackIdCornersTexData = atlas:AddCustomRect(pack_size_x, pack_size_y, r)
assert(builder.PackIdCornersTexData ~= (-1) --[[ImFontAtlasRectId_Invalid]])
end
-- Render
local y = r.y
local line_normals_x, line_normals_y, line_distances = corner_normals_x, corner_normals_y, corner_distances
for thickness = 0, 4 --[[IM_DRAWLIST_TEX_CORNERS_THICKNESS_MAX]] - 1 do
local is_filled = (thickness == 0)
local x = r.x
local row_height = 0
for n = 0, 16 --[[IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX]] - 1 do
-- Filled: Always add 1px to the fill side of the rounded corner to prevent the corner AA to bleed into the rectangle edges.
local rounding = n + 1
local size = is_filled and (rounding + 1) or ImMax(rounding, thickness)
local w = size + 2
local h = size + 2
-- Corner circle
local cx = 1.0 + (n + 1)
local cy = 1.0 + (n + 1)
assert((x + w) <= (r.x + r.w) and (y + h) <= (r.y + r.h)) -- Make sure we're inside the texture bounds
local num_lines = InitCornerGeometry(cx, cy, rounding, circle_segment_max_error, line_normals_x, line_normals_y, line_distances)
-- Write each slice
if add_and_draw and tex.Format == 1 --[[ImTextureFormat.Alpha8]] then
local write_ptr, write_ptr_base = tex:GetPixelsAt(x, y)
local pitch = tex.Width
for ly = 0, h - 1 do
for lx = 0, w - 1 do
write_ptr[write_ptr_base + lx] = SampleCornerFillOrStroke(lx + 0.5, ly + 0.5, thickness, line_normals_x, line_normals_y, line_distances, num_lines)
end
write_ptr_base = write_ptr_base + pitch
end
elseif add_and_draw and tex.Format == 0 --[[ImTextureFormat.RGBA32]] then
local write_ptr, write_ptr_base = tex:GetPixelsAt(x, y)
local pitch = tex.Width * 4
for ly = 0, h - 1 do
for lx = 0, w - 1 do
write_u32(write_ptr, write_ptr_base + lx * 4, IM_COL32(255, 255, 255, SampleCornerFillOrStroke(lx + 0.5, ly + 0.5, thickness, line_normals_x, line_normals_y, line_distances, num_lines)))
end
write_ptr_base = write_ptr_base + pitch
end
end
-- Refresh UV coordinates
local uv0_x, uv0_y = (x + 1) * atlas.TexUvScale.x, (y + 1) * atlas.TexUvScale.y
local uv1_x, uv1_y = (x + 1 + size) * atlas.TexUvScale.x, (y + 1 + size) * atlas.TexUvScale.y
builder.TexUvCorners[n + thickness * 16 --[[IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX]]] = ImVec4(uv0_x, uv0_y, uv1_x, uv1_y)
x = x + w
row_height = ImMax(row_height, h)
end
y = y + row_height
end
end
function ImFontAtlasBuildUpdateTexDataLines(atlas)
if bit32.band(atlas.Flags, 4 --[[ImFontAtlasFlags.NoBakedLines]]) ~= 0 then
return
end
-- Pack and store identifier so we can refresh UV coordinates on texture resize.
local tex = atlas.TexData
local builder = atlas.Builder
local r = ImFontAtlasRect()
local add_and_draw = (atlas:GetCustomRect(builder.PackIdLinesTexData, r) == false)
if add_and_draw then
-- Register texture region for thick lines
-- The +2 here is to give space for the end caps, whilst height +1 is to accommodate the fact we have a zero-width row
local pack_size = ImVec2(IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 2, IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1 + 13 --[[IM_DRAWLIST_TEX_LINES_DETAILED_WIDTH_COUNT]]) -- ImVec2i
builder.PackIdLinesTexData = atlas:AddCustomRect(pack_size.x, pack_size.y, r)
assert(builder.PackIdLinesTexData ~= (-1) --[[ImFontAtlasRectId_Invalid]])
end
do
-- FIXME: Lines 0 to IM_DRAWLIST_TEX_LINES_DETAILED_WIDTH(4) are currently never used and could be ditched, saving DETAILED_WIDTH*WIDTH_MAX
for n = 0, IM_DRAWLIST_TEX_LINES_WIDTH_MAX do -- +1 because of the zero-width row
-- Each line consists of at least two empty pixels at the ends, with a line of solid pixels in the middle
local y = n
local line_width = n
local pad_left = math.floor((r.w - line_width) / 2)
local pad_right = r.w - (pad_left + line_width)
assert(pad_left + line_width + pad_right == r.w and y < r.h) -- Make sure we're inside the texture bounds before we start writing pixels
-- Write each slice
if add_and_draw and tex.Format == 1 --[[ImTextureFormat.Alpha8]] then
local write_ptr, write_ptr_base = tex:GetPixelsAt(r.x, r.y + y)
for i = 0, pad_left - 1 do
write_ptr[write_ptr_base + i] = 0x00
end
for i = 0, line_width - 1 do
write_ptr[write_ptr_base + pad_left + i] = 0xFF
end
for i = 0, pad_right - 1 do
write_ptr[write_ptr_base + pad_left + line_width + i] = 0x00
end
elseif add_and_draw and tex.Format == 0 --[[ImTextureFormat.RGBA32]] then
local write_ptr, write_ptr_base = tex:GetPixelsAt(r.x, r.y + y)
for i = 0, pad_left - 1 do
write_u32(write_ptr, write_ptr_base + i * 4, IM_COL32(255, 255, 255, 0))
end
for i = 0, line_width - 1 do
write_u32(write_ptr, write_ptr_base + (pad_left + i) * 4, 4294967295 --[[IM_COL32_WHITE]])
end
for i = 0, pad_right - 1 do
write_u32(write_ptr, write_ptr_base + (pad_left + line_width + i) * 4, IM_COL32(255, 255, 255, 0))
end
end
-- Refresh UV coordinates
local uv0_x, uv0_y = (r.x + pad_left - 0.5) * atlas.TexUvScale.x, (r.y + y) * atlas.TexUvScale.y
local uv1_x, uv1_y = (r.x + pad_left + line_width + 0.5) * atlas.TexUvScale.x, (r.y + y + 1) * atlas.TexUvScale.y
local half_v = (uv0_y + uv1_y) * 0.5 -- Calculate a constant V in the middle of the row to avoid sampling artifacts
-- Store inverse of the line-width (thickness) to the w component so that we can avoid division when selecting texture.
local inv_line_width = (line_width > 0) and (1.0 / line_width) or 0.0
builder.TexUvLines[n] = ImVec4(uv0_x, uv1_x, half_v, inv_line_width)
end
end
do
-- Calculate super sampled line textures for smaller line widths to make transition between sizes smoother.
-- Calculate ramp used for all the textures.
local ramp = {} -- ImU8[IM_DRAWLIST_TEX_LINES_SAMPLE_COUNT], 0-based
for n = 0, 4 --[[IM_DRAWLIST_TEX_LINES_SAMPLE_COUNT]] - 1 do
ramp[n] = math.floor((n / 4 --[[IM_DRAWLIST_TEX_LINES_SAMPLE_COUNT]]) * 255.0)
end
for n = 0, 13 --[[IM_DRAWLIST_TEX_LINES_DETAILED_WIDTH_COUNT]] - 1 do
local y = IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1 + n
-- For integer thickness lines the one pixel line texture looks like this:
-- [ 0 | 1 | 0 ]
-- :...........:
-- Since the texture samples are at the center of the texel, we only use range above.
-- The result after bilinear filtering is a triangle across the used uv range.
--
-- To super 2x sample that signal, we end up with following texture:
-- [ 0 | .5 | 1 | .5 | 0 ]
-- :.......................:
-- One might think that there should be 2 opaque pixels in the middle section, but no since we're super sampling the triangle signal.
local line_width = 1 + n
assert(4 --[[IM_DRAWLIST_TEX_LINES_SAMPLE_COUNT]] + line_width + 4 --[[IM_DRAWLIST_TEX_LINES_SAMPLE_COUNT]] <= r.w and y < r.h) -- Make sure we're inside the texture bounds before we start writing pixels
-- Write each slice
if add_and_draw and tex.Format == 1 --[[ImTextureFormat.Alpha8]] then
local write_ptr, write_ptr_base = tex:GetPixelsAt(r.x, r.y + y)
local idx = 0
for i = 0, 4 --[[IM_DRAWLIST_TEX_LINES_SAMPLE_COUNT]] - 1 do
write_ptr[write_ptr_base + idx] = ramp[i]; idx = idx + 1
end
for _ = 0, line_width - 1 do
write_ptr[write_ptr_base + idx] = 0xFF; idx = idx + 1
end
for i = 4 --[[IM_DRAWLIST_TEX_LINES_SAMPLE_COUNT]] - 1, 0, -1 do
write_ptr[write_ptr_base + idx] = ramp[i]; idx = idx + 1
end
while idx < r.w do
write_ptr[write_ptr_base + idx] = 0x0; idx = idx + 1
end
elseif add_and_draw and tex.Format == 0 --[[ImTextureFormat.RGBA32]] then
local write_ptr, write_ptr_base = tex:GetPixelsAt(r.x, r.y + y)
local idx = 0
for i = 0, 4 --[[IM_DRAWLIST_TEX_LINES_SAMPLE_COUNT]] - 1 do
write_u32(write_ptr, write_ptr_base + idx * 4, IM_COL32(255, 255, 255, ramp[i])); idx = idx + 1
end
for _ = 0, line_width - 1 do
write_u32(write_ptr, write_ptr_base + idx * 4, IM_COL32(255, 255, 255, 255)); idx = idx + 1
end
for i = 4 --[[IM_DRAWLIST_TEX_LINES_SAMPLE_COUNT]] - 1, 0, -1 do
write_u32(write_ptr, write_ptr_base + idx * 4, IM_COL32(255, 255, 255, ramp[i])); idx = idx + 1
end
while idx < r.w do
write_u32(write_ptr, write_ptr_base + idx * 4, IM_COL32(255, 255, 255, 0)); idx = idx + 1
end
end
-- Refresh UV coordinates
local uv0_x, uv0_y = (r.x + 0.5) * atlas.TexUvScale.x, (r.y + y) * atlas.TexUvScale.y
local uv1_x, uv1_y = (r.x - 0.5 + 4 --[[IM_DRAWLIST_TEX_LINES_SAMPLE_COUNT]] * 2 + line_width) * atlas.TexUvScale.x, (r.y + y + 1) * atlas.TexUvScale.y
local half_v = (uv0_y + uv1_y) * 0.5 -- Calculate a constant V in the middle of the row to avoid sampling artifacts
-- Store inverse of the line-width (thickness) to the w component so that we can avoid division when selecting texture.
local inv_line_width = 1.0 / (1.0 + n / 4 --[[IM_DRAWLIST_TEX_LINES_SAMPLE_COUNT]])
builder.TexUvLines[IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1 + n] = ImVec4(uv0_x, uv1_x, half_v, inv_line_width)
end
end
end
-- Render a white-colored bitmap encoded in a string
--- @param atlas ImFontAtlas
--- @param x int
--- @param y int
--- @param w int
--- @param h int
--- @param in_str string[]
--- @param in_marker_char string
function ImFontAtlasBuildRenderBitmapFromString(atlas, x, y, w, h, in_str, in_marker_char)
local tex = atlas.TexData
assert(x >= 0 and x + w <= tex.Width)
assert(y >= 0 and y + h <= tex.Height)
if tex.Format == 1 --[[ImTextureFormat.Alpha8]] then
local out_p, out_p_base = tex:GetPixelsAt(x, y)
local str_base = 1
for off_y = 0, h - 1 do
for off_x = 0, w - 1 do
out_p[out_p_base + off_x] = (in_str[str_base + off_x] == in_marker_char) and 0xFF or 0x00
end
out_p_base = out_p_base + tex.Width
str_base = str_base + w
end
elseif tex.Format == 0 --[[ImTextureFormat.RGBA32]] then
local out_p, out_p_base = tex:GetPixelsAt(x, y)
local str_base = 1
for off_y = 0, h - 1 do
for off_x = 0, w - 1 do
write_u32(out_p, out_p_base + off_x * 4, (in_str[str_base + off_x] == in_marker_char) and 4294967295 --[[IM_COL32_WHITE]] or 0 --[[IM_COL32_BLACK_TRANS]])
end
out_p_base = out_p_base + tex.Width * 4
str_base = str_base + w
end
end
end
--- @param atlas ImFontAtlas
function ImFontAtlasBuildUpdateTexDataBasic(atlas)
-- Pack and store identifier so we can refresh UV coordinates on texture resize.
-- FIXME-NEWATLAS: User/custom rects where user code wants to store UV coordinates will need to do the same thing.
local builder = atlas.Builder
local pack_size = (bit32.band(atlas.Flags, 2 --[[ImFontAtlasFlags.NoMouseCursors]]) ~= 0) and ImVec2(2, 2) or ImVec2(122 --[[FONT_ATLAS_DEFAULT_TEX_DATA_W]] * 2 + 1, 27 --[[FONT_ATLAS_DEFAULT_TEX_DATA_H]])
local r = ImFontAtlasRect()
local add_and_draw = (atlas:GetCustomRect(builder.PackIdMouseCursors, r) == false)
if (add_and_draw) then
builder.PackIdMouseCursors = atlas:AddCustomRect(pack_size.x, pack_size.y, r)
assert(builder.PackIdMouseCursors ~= (-1) --[[ImFontAtlasRectId_Invalid]])
-- Draw to texture
if bit32.band(atlas.Flags, 2 --[[ImFontAtlasFlags.NoMouseCursors]]) ~= 0 then
-- 2x2 white pixels
ImFontAtlasBuildRenderBitmapFromString(atlas, r.x, r.y, 2, 2, {"X", "X", "X", "X"}, "X")
else
-- 2x2 white pixels + mouse cursors
local x_for_white = r.x
local x_for_black = r.x + 122 --[[FONT_ATLAS_DEFAULT_TEX_DATA_W]] + 1
ImFontAtlasBuildRenderBitmapFromString(atlas, x_for_white, r.y, 122 --[[FONT_ATLAS_DEFAULT_TEX_DATA_W]], 27 --[[FONT_ATLAS_DEFAULT_TEX_DATA_H]], FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, ".")
ImFontAtlasBuildRenderBitmapFromString(atlas, x_for_black, r.y, 122 --[[FONT_ATLAS_DEFAULT_TEX_DATA_W]], 27 --[[FONT_ATLAS_DEFAULT_TEX_DATA_H]], FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, "X")
end
end
-- Refresh UV coordinates
atlas.TexUvWhitePixel = ImVec2((r.x + 0.5) * atlas.TexUvScale.x, (r.y + 0.5) * atlas.TexUvScale.y)
end
-- Update texture coordinates in all draw list shared context
-- FIXME-NEWATLAS FIXME-OPT: Doesn't seem necessary to update for all, only one bound to current context?
function ImFontAtlasUpdateDrawListsSharedData(atlas)
for _, shared_data in atlas.DrawListSharedDatas:iter() do
if (shared_data.FontAtlas == atlas) then
shared_data.TexUvWhitePixel = atlas.TexUvWhitePixel
shared_data.TexUvLines = atlas.Builder.TexUvLines
shared_data.TexUvCorners = atlas.Builder.TexUvCorners
end
end
end
-- Start packing over current empty texture
function ImFontAtlasBuildInit(atlas)
-- Select Backend
-- - Note that we do not reassign to atlas.FontLoader, since it is likely to point to static data which
-- may mess with some hot-reloading schemes. If you need to assign to this (for dynamic selection) AND are
-- using a hot-reloading scheme that messes up static data, store your own instance of FontLoader somewhere
-- and point to it instead of pointing directly to return value of the GetFontLoaderXXX functions.
if atlas.FontLoader == nil then
-- IMGUI_ENABLE_STB_TRUETYPE
atlas:SetFontLoader(ImFontAtlasGetFontLoaderForStbTruetype())
end
-- Create initial texture size
if atlas.TexData == nil or atlas.TexData.Pixels == nil then
ImFontAtlasTextureAdd(atlas, ImUpperPowerOfTwo(atlas.TexMinWidth), ImUpperPowerOfTwo(atlas.TexMinHeight))
end
atlas.Builder = ImFontAtlasBuilder()
if atlas.FontLoader.LoaderInit then
atlas.FontLoader.LoaderInit(atlas)
end
ImFontAtlasBuildUpdateRendererHasTexturesFromContext(atlas)
ImFontAtlasPackInit(atlas)
-- Add required texture data
ImFontAtlasBuildUpdateTexData(atlas)
-- Register fonts
ImFontAtlasBuildUpdatePointers(atlas)
-- Update UV coordinates etc. stored in bound ImDrawListSharedData instance
ImFontAtlasUpdateDrawListsSharedData(atlas)
--atlas.TexIsBuilt = true
-- Lazily initialize char/text classifier
-- FIXME: This could be practically anywhere, and should eventually be parameters to CalcTextSize/word-wrapping code, but there's no obvious spot now.
ImTextInitClassifiers()
end
-- Destroy builder and all cached glyphs. Do not destroy actual fonts.
--- @param atlas ImFontAtlas
function ImFontAtlasBuildDestroy(atlas)
for _, font in atlas.Fonts:iter() do
ImFontAtlasFontDestroyOutput(atlas, font)
end
if atlas.Builder and atlas.FontLoader and atlas.FontLoader.LoaderShutdown then
atlas.FontLoader.LoaderShutdown(atlas)
assert(atlas.FontLoaderData == nil)
end
atlas.Builder = nil
end
-- On font removal we need to remove references (otherwise we could queue removal?)
-- We allow old_font == new_font which forces updating all values (e.g. sizes)
--- @param atlas ImFontAtlas
--- @param old_font ImFont?
--- @param new_font ImFont?
function ImFontAtlasBuildNotifySetFont(atlas, old_font, new_font)
for _, shared_data in atlas.DrawListSharedDatas:iter() do
if shared_data.Font == old_font then
shared_data.Font = new_font
end
local ctx = shared_data.Context
if ctx then
repeat
-- While this should work either way, we save ourselves the bother / debugging confusion of running ImGui code so early when it is not needed.
-- Also fixes erroneously rewriting style.FontSizeBase during init if adding default fonts.
if old_font == nil and ctx.Font == nil and ctx.FontSizeBase == 0.0 then
break
end
if ctx.IO.FontDefault == old_font then
ctx.IO.FontDefault = new_font
end
if ctx.Font == old_font then
local curr_ctx = ImGui.GetCurrentContext()
local need_bind_ctx = ctx ~= curr_ctx
if need_bind_ctx then
ImGui.SetCurrentContext(ctx)
end
ImGui.SetCurrentFont(new_font, ctx.FontSizeBase, ctx.FontSize)
if need_bind_ctx then
ImGui.SetCurrentContext(curr_ctx)
end
end
for _, font_stack_data in ctx.FontStack:iter() do
if font_stack_data.Font == old_font then
font_stack_data.Font = new_font
end
end
until true
end
end
end
-- When atlas.RendererHasTextures = true, this is only called if no font were loaded.
--- @param atlas ImFontAtlas
function ImFontAtlasBuildMain(atlas)
assert(not atlas.Locked, "Cannot modify a locked ImFontAtlas!")
if (atlas.TexData and atlas.TexData.Format ~= atlas.TexDesiredFormat) then
ImFontAtlasBuildClear(atlas)
end
if atlas.Builder == nil then
ImFontAtlasBuildInit(atlas)
end
-- Default font is none are specified
if atlas.Sources.Size == 0 then
atlas:AddFontDefault()
end
-- [LEGACY] For backends not supporting RendererHasTextures: preload all glyphs
-- ImFontAtlasBuildUpdateRendererHasTexturesFromContext(atlas);
-- if atlas.RendererHasTextures == false then -- ~ImGuiBackendFlags.RendererHasTextures
-- ImFontAtlasBuildLegacyPreloadAllGlyphRanges(atlas)
-- end
atlas.TexIsBuilt = true
end
-- [Luau] Ported but its call above stays disabled: the Roblox/LÖVE backends support RendererHasTextures.
function ImFontAtlasBuildLegacyPreloadAllGlyphRanges(atlas)
atlas.Builder.PreloadedAllGlyphsRanges = true
for _, font in atlas.Fonts:iter() do
local baked = font:GetFontBaked(font.LegacySize)
if font.FallbackChar ~= 0 then
baked:FindGlyph(font.FallbackChar)
end
if font.EllipsisChar ~= 0 then
baked:FindGlyph(font.EllipsisChar)
end
for _, src in font.Sources:iter() do
local ranges = src.GlyphRanges and src.GlyphRanges or atlas:GetGlyphRangesDefault()
local i = 1
while ranges[i] ~= 0 do
local c = ranges[i]
while c <= ranges[i + 1] and c <= 65535 --[[IM_UNICODE_CODEPOINT_MAX]] do
baked:FindGlyph(c)
c = c + 1
end
i = i + 2
end
end
end
end
-------------------------------------------------------------------------------
-- [SECTION] ImTextureData
-------------------------------------------------------------------------------
-- - ImTextureData:Create()
-- - ImTextureData:DestroyPixels()
-------------------------------------------------------------------------------
--- @param status ImTextureStatus
function ImTextureDataGetStatusName(status)
if status == 0 --[[ImTextureStatus.OK]] then return "OK"
elseif status == 1 --[[ImTextureStatus.Destroyed]] then return "Destroyed"
elseif status == 2 --[[ImTextureStatus.WantCreate]] then return "WantCreate"
elseif status == 3 --[[ImTextureStatus.WantUpdates]] then return "WantUpdates"
elseif status == 4 --[[ImTextureStatus.WantDestroy]] then return "WantDestroy"
end
return "N/A"
end
--- @param format ImTextureFormat
function ImTextureDataGetFormatName(format)
if format == 1 --[[ImTextureFormat.Alpha8]] then return "Alpha8"
elseif format == 0 --[[ImTextureFormat.RGBA32]] then return "RGBA32"
end
return "N/A"
end
function MT.ImTextureData:DestroyPixels()
self.Pixels = nil
self.UseColors = false
end
function ImTextureDataGetFormatBytesPerPixel(format)
if format == 1 --[[ImTextureFormat.Alpha8]] then
return 1
elseif format == 0 --[[ImTextureFormat.RGBA32]] then
return 4
end
assert(false)
return 0
end
function MT.ImTextureData:Create(format, w, h)
assert(self.Status == 1 --[[ImTextureStatus.Destroyed]])
self:DestroyPixels()
self.Format = format
self.Status = 2 --[[ImTextureStatus.WantCreate]]
self.Width = w
self.Height = h
self.BytesPerPixel = ImTextureDataGetFormatBytesPerPixel(format)
self.UseColors = false
self.Pixels = {}
ImStd.memset(self.Pixels, 0, self.Width * self.Height * self.BytesPerPixel)
self.UsedRect.x = 0
self.UsedRect.y = 0
self.UsedRect.w = 0
self.UsedRect.h = 0
self.UpdateRect.x = 65535
self.UpdateRect.y = 65535
self.UpdateRect.w = 0
self.UpdateRect.h = 0
end
-------------------------------------------------------------------------------
-- [SECTION] ImFontAtlas: glyph ranges helpers
-------------------------------------------------------------------------------
-- - GetGlyphRangesDefault()
-- [Luau] The obsolete GetGlyphRangesXXX() (IMGUI_DISABLE_OBSOLETE_FUNCTIONS) are not ported.
-------------------------------------------------------------------------------
-- Retrieve list of range (2 int per range, values are inclusive)
local GlyphRangesDefault =
{
0x0020, 0x00FF, -- Basic Latin + Latin Supplement
0,
}
function MT.ImFontAtlas:GetGlyphRangesDefault()
return GlyphRangesDefault
end
-------------------------------------------------------------------------------
-- [SECTION] ImFontGlyphRangesBuilder
-------------------------------------------------------------------------------
--- @param text ImString
--- @param text_end? int # exclusive, 1-based
function MT.ImFontGlyphRangesBuilder:AddText(text, text_end)
if text_end == nil then
text_end = #text + 1
end
local s = 1
while s < text_end do
local c_len, c = ImStd.ImTextCharFromUtf8(text, s, text_end)
s = s + c_len
if c_len == 0 then
break
end
self:AddChar(c)
end
end
--- @param ranges ImWchar[] # 1-based, zero-terminated
function MT.ImFontGlyphRangesBuilder:AddRanges(ranges)
local i = 1
while ranges[i] ~= 0 do
local c = ranges[i]
while c <= ranges[i + 1] and c <= 65535 --[[IM_UNICODE_CODEPOINT_MAX]] do
self:AddChar(c)
c = c + 1
end
i = i + 2
end
end
--- @param out_ranges ImVector<ImWchar>
function MT.ImFontGlyphRangesBuilder:BuildRanges(out_ranges)
local max_codepoint = 65535 --[[IM_UNICODE_CODEPOINT_MAX]]
local n = 0
while n <= max_codepoint do
if self:GetBit(n) then
out_ranges:push_back(n)
while n < max_codepoint and self:GetBit(n + 1) do
n = n + 1
end
out_ranges:push_back(n)
end
n = n + 1
end
out_ranges:push_back(0)
end
-------------------------------------------------------------------------------
-- [SECTION] ImFontBaked, ImFont
-------------------------------------------------------------------------------
function MT.ImFontBaked:ClearOutputData()
self.FallbackAdvanceX = 0.0
self.Glyphs:clear()
self.IndexAdvanceX:clear()
self.IndexLookup:clear()
self.FallbackGlyphIndex = -1
self.Ascent = 0.0
self.Descent = 0.0
self.MetricsTotalSurface = 0
end
-- Find glyph, load if necessary, return fallback if missing
--- @param c ImWchar
--- @return ImFontGlyph?
function MT.ImFontBaked:FindGlyph(c)
if c < self.IndexLookup.Size then
local i = self.IndexLookup.Data[c + 1]
if i == IM_FONTGLYPH_INDEX_NOT_FOUND then
return self.Glyphs.Data[self.FallbackGlyphIndex]
end
if i ~= IM_FONTGLYPH_INDEX_UNUSED then
return self.Glyphs.Data[i]
end
end
local glyph = ImFontBaked_BuildLoadGlyph(self, c, nil)
return (glyph) and glyph or self.Glyphs.Data[self.FallbackGlyphIndex]
end
function MT.ImFontBaked:IsGlyphLoaded(c)
if c >= self.IndexLookup.Size then return false end
return self.IndexLookup.Data[c + 1] ~= IM_FONTGLYPH_INDEX_UNUSED
end
-- Attempt to load but when missing, return nil instead of FallbackGlyph
--- @param c ImWchar
--- @return ImFontGlyph?
function MT.ImFontBaked:FindGlyphNoFallback(c)
if c < self.IndexLookup.Size then -- IM_LIKELY
local i = self.IndexLookup.Data[c + 1]
if i == IM_FONTGLYPH_INDEX_NOT_FOUND then
return nil
end
if i ~= IM_FONTGLYPH_INDEX_UNUSED then
return self.Glyphs.Data[i]
end
end
self.LoadNoFallback = true -- This is actually a rare call, not done in hot-loop, so we prioritize not adding extra cruft to ImFontBaked_BuildLoadGlyph() call sites.
local glyph = ImFontBaked_BuildLoadGlyph(self, c, nil)
self.LoadNoFallback = false
return glyph
end
function MT.ImFont:ClearOutputData()
local atlas = self.OwnerAtlas
if atlas ~= nil then
ImFontAtlasFontDiscardBakes(atlas, self, 0)
end
--self.FallbackChar = 0 self.EllipsisChar = 0
for i = 1, (65535 --[[IM_UNICODE_CODEPOINT_MAX]] + 1) / 8192 / 8 do self.Used8kPagesMap[i] = 0 end
self.LastBaked = nil
end
-- API is designed this way to avoid exposing the 8K page size
-- e.g. use with IsGlyphRangeUnused(0, 255)
function MT.ImFont:IsGlyphRangeUnused(c_begin, c_last)
local page_begin = math.floor(c_begin / 8192)
local page_last = math.floor(c_last / 8192)
for page_n = page_begin, page_last do
if bit32.rshift(page_n, 3) < (65535 --[[IM_UNICODE_CODEPOINT_MAX]] + 1) / 8192 / 8 then -- sizeof(Used8kPagesMap)
if bit32.band(self.Used8kPagesMap[bit32.rshift(page_n, 3) + 1] or 0, bit32.lshift(1, bit32.band(page_n, 7))) ~= 0 then
return false
end
end
end
return true
end
--- @param font_id ImGuiID
--- @param baked_size float
--- @param rasterizer_density float
--- @return ImGuiID
function ImFontAtlasBakedGetId(font_id, baked_size, rasterizer_density)
local hashed_data = {font_id, baked_size, rasterizer_density}
return ImHashData(hashed_data)
end
--- @param atlas ImFontAtlas
--- @param font ImFont
--- @param font_size float
--- @param font_rasterizer_density float
--- @return ImFontBaked?
function ImFontAtlasBakedGetOrAdd(atlas, font, font_size, font_rasterizer_density)
-- FIXME-NEWATLAS: Design for picking a nearest size based on some criteria?
-- FIXME-NEWATLAS: Altering font density won't work right away.
assert(font_size > 0.0 and font_rasterizer_density > 0.0)
local baked_id = ImFontAtlasBakedGetId(font.FontId, font_size, font_rasterizer_density)
local builder = atlas.Builder
local baked = builder.BakedMap[baked_id]
if baked ~= nil then
assert(baked.Size == font_size and baked.OwnerFont == font and baked.BakedId == baked_id)
return baked
end
-- If atlas is locked, find closest match
-- FIXME-OPT: This is not an optimal query.
if (bit32.band(font.Flags, 8 --[[ImFontFlags.LockBakedSizes]]) ~= 0 or atlas.Locked) then
baked = ImFontAtlasBakedGetClosestMatch(atlas, font, font_size, font_rasterizer_density)
if baked ~= nil then
return baked
end
if atlas.Locked then
assert(not atlas.Locked, "Cannot use dynamic font size with a locked ImFontAtlas!") -- Locked because rendering backend does not support ImGuiBackendFlags.RendererHasTextures!
return nil
end
end
-- Create new
baked = ImFontAtlasBakedAdd(atlas, font, font_size, font_rasterizer_density, baked_id)
builder.BakedMap[baked_id] = baked
return baked
end
-- x0/y0/x1/y1 are offset from the character upper-left layout position, in pixels. Therefore x0/y0 are often fairly close to zero.
-- Not to be mistaken with texture coordinates, which are held by u0/v0/u1/v1 in normalized format (0.0..1.0 on each texture axis).
-- - 'src' is not necessarily == 'self.Sources' because multiple source fonts+configs can be used to build one target font.
--- @param atlas ImFontAtlas
--- @param baked ImFontBaked
--- @param src? ImFontConfig
--- @param in_glyph ImFontGlyph
--- @return ImFontGlyph
function ImFontAtlasBakedAddFontGlyph(atlas, baked, src, in_glyph)
local glyph_idx = baked.Glyphs.Size + 1
baked.Glyphs:push_back(in_glyph)
local glyph = baked.Glyphs.Data[glyph_idx]
assert(baked.Glyphs.Size < 0xFFFE) -- IndexLookup[] hold 16-bit values and -1/-2 are reserved.
-- Set UV from packed rectangle
if (glyph.PackId ~= (-1) --[[ImFontAtlasRectId_Invalid]]) then
local r = ImFontAtlasPackGetRect(atlas, glyph.PackId)
assert(glyph.U0 == 0.0 and glyph.V0 == 0.0 and glyph.U1 == 0.0 and glyph.V1 == 0.0)
glyph.U0 = (r.x) * atlas.TexUvScale.x
glyph.V0 = (r.y) * atlas.TexUvScale.y
glyph.U1 = (r.x + r.w) * atlas.TexUvScale.x
glyph.V1 = (r.y + r.h) * atlas.TexUvScale.y
baked.MetricsTotalSurface = baked.MetricsTotalSurface + r.w * r.h
end
if (src ~= nil) then
-- Clamp & recenter if needed
local ref_size = baked.OwnerFont.Sources[1].SizePixels
local offsets_scale = (ref_size ~= 0.0) and (baked.Size / ref_size) or 1.0
local advance_x = ImClamp(glyph.AdvanceX, src.GlyphMinAdvanceX * offsets_scale, src.GlyphMaxAdvanceX * offsets_scale)
if (advance_x ~= glyph.AdvanceX) then
local char_off_x = src.PixelSnapH and ImTrunc((advance_x - glyph.AdvanceX) * 0.5) or (advance_x - glyph.AdvanceX) * 0.5
glyph.X0 = glyph.X0 + char_off_x
glyph.X1 = glyph.X1 + char_off_x
end
-- Snap to pixel
if (src.PixelSnapH) then
advance_x = IM_ROUND(advance_x)
end
-- Bake spacing
glyph.AdvanceX = advance_x + src.GlyphExtraAdvanceX
end
if (glyph.Colored) then
atlas.TexData.UseColors = true
atlas.TexPixelsUseColors = true
end
-- Update lookup tables
local codepoint = glyph.Codepoint
ImFontBaked_BuildGrowIndex(baked, codepoint + 1)
baked.IndexAdvanceX.Data[codepoint + 1] = glyph.AdvanceX
baked.IndexLookup.Data[codepoint + 1] = glyph_idx -- (ImU16)
local page_n = math.floor(codepoint / 8192)
baked.OwnerFont.Used8kPagesMap[bit32.rshift(page_n, 3) + 1] = bit32.bor(baked.OwnerFont.Used8kPagesMap[bit32.rshift(page_n, 3) + 1], bit32.lshift(1, bit32.band(page_n, 7)))
return glyph
end
-- FIXME: Code is duplicated with code above.
--- @param atlas ImFontAtlas
--- @param baked ImFontBaked
--- @param src? ImFontConfig
--- @param codepoint ImWchar
--- @param advance_x float
function ImFontAtlasBakedAddFontGlyphAdvancedX(atlas, baked, src, codepoint, advance_x)
-- IM_UNUSED(atlas)
if (src ~= nil) then
-- Clamp & recenter if needed
local ref_size = baked.OwnerFont.Sources[1].SizePixels
local offsets_scale = (ref_size ~= 0.0) and (baked.Size / ref_size) or 1.0
advance_x = ImClamp(advance_x, src.GlyphMinAdvanceX * offsets_scale, src.GlyphMaxAdvanceX * offsets_scale)
-- Snap to pixel
if (src.PixelSnapH) then
advance_x = IM_ROUND(advance_x)
end
-- Bake spacing
advance_x = advance_x + src.GlyphExtraAdvanceX
end
ImFontBaked_BuildGrowIndex(baked, codepoint + 1)
baked.IndexAdvanceX.Data[codepoint + 1] = advance_x
end
-- ImFontBaked are valid for the entire frame but shall never be kept between frames.
--- @param size float
--- @param density float?
--- @return ImFontBaked?
function MT.ImFont:GetFontBaked(size, density)
if not density then density = -1.0 end
--- @type ImFontBaked?
local baked = self.LastBaked
-- Round font size
-- - ImGui.PushFont() will already round, but other paths calling GetFontBaked() directly also needs it (e.g. ImFontAtlasBuildPreloadAllGlyphRanges)
size = math.floor(size + 0.5) -- [Luau] inline ImGui.GetRoundedFontSize() == IM_ROUND()
if density < 0.0 then
density = self.CurrentRasterizerDensity
end
if baked and baked.Size == size and baked.RasterizerDensity == density then
return baked
end
local atlas = self.OwnerAtlas
local builder = atlas.Builder
baked = ImFontAtlasBakedGetOrAdd(atlas, self, size, density)
if baked == nil then
return nil
end
baked.LastUsedFrame = builder.FrameCount
self.LastBaked = baked
return baked
end
-- Makes 'from_codepoint' character points to 'to_codepoint' glyph.
-- [Luau] AddRemapCodepointToGlyphIndex() not ported: needs the 1.93 loader split (FontSrcGetGlyphIndexFromCodepoint).
function MT.ImFont:AddRemapCodepoint(from_codepoint, to_codepoint)
local data = to_codepoint
assert(bit32.band(data, bit32.lshift(1, 31)) == 0)
self.RemapPairs[from_codepoint] = data
end
--- @param atlas ImFontAtlas
--- @param font ImFont
--- @param c ImWchar
--- @return ImWchar
--- @nodiscard
local function ImFontAtlas_FontHookRemapCodepoint(atlas, font, c)
-- IM_UNUSED(atlas)
if next(font.RemapPairs) ~= nil then -- [Luau] font->RemapPairs.Data.Size != 0 (RemapPairs is a hash table)
local ret = font.RemapPairs[c]
return (ret ~= nil) and ret or c
end
return c
end
--- @param baked ImFontBaked
--- @param codepoint ImWchar
--- @param only_load_advance_x? float
--- @return ImFontGlyph?
--- @return float? # Updated `only_load_advance_x`
function ImFontBaked_BuildLoadGlyph(baked, codepoint, only_load_advance_x)
local font = baked.OwnerFont
local atlas = font.OwnerAtlas
if atlas.Locked or bit32.band(font.Flags, 4 --[[ImFontFlags.NoLoadGlyphs]]) ~= 0 then
-- Lazily load fallback glyph
if baked.FallbackGlyphIndex == -1 and baked.LoadNoFallback == false then
ImFontAtlasBuildSetupFontBakedFallback(baked)
end
return nil, only_load_advance_x
end
-- User remapping hooks
local src_codepoint = codepoint
codepoint = ImFontAtlas_FontHookRemapCodepoint(atlas, font, codepoint)
--IMGUI_DEBUG_LOG("[font] BuildLoadGlyph U+%04X (%s)", codepoint, ImTextCharToUtf8(codepoint))
-- Special hook
-- FIXME-NEWATLAS: it would be nicer if this used a more standardized way of hooking
if (codepoint == font.EllipsisChar and font.EllipsisAutoBake) then
local glyph = ImFontAtlasBuildSetupFontBakedEllipsis(atlas, baked)
if (glyph) then
return glyph, only_load_advance_x
end
end
-- Call backend
local loader_user_data_p = baked.FontLoaderDatas
local src_n = 1
for _, src in font.Sources:iter() do
local loader = src.FontLoader and src.FontLoader or atlas.FontLoader
if (not src.GlyphExcludeRanges or ImFontAtlasBuildAcceptCodepointForSource(src, codepoint)) then
if only_load_advance_x == nil then
local glyph_buf = ImFontGlyph()
if loader.FontBakedLoadGlyph(atlas, src, baked, loader_user_data_p, codepoint, glyph_buf, nil) then
-- FIXME: Add hooks for e.g. #7962
glyph_buf.Codepoint = src_codepoint
glyph_buf.SourceIdx = src_n
return ImFontAtlasBakedAddFontGlyph(atlas, baked, src, glyph_buf)
end
else
-- Special mode but only loading glyphs metrics. Will rasterize and pack later.
local ret --[[@as bool]]
ret, only_load_advance_x = loader.FontBakedLoadGlyph(atlas, src, baked, loader_user_data_p, codepoint, nil, only_load_advance_x)
if ret then
--- @cast only_load_advance_x float
ImFontAtlasBakedAddFontGlyphAdvancedX(atlas, baked, src, codepoint, only_load_advance_x)
return nil, only_load_advance_x
end
end
end
-- loader_user_data_p = loader_user_data_p + loader.FontBakedSrcLoaderDataSize
src_n = src_n + 1
end
-- Lazily load fallback glyph
if (baked.LoadNoFallback) then
return nil, only_load_advance_x
end
if (baked.FallbackGlyphIndex == -1) then
ImFontAtlasBuildSetupFontBakedFallback(baked)
end
-- Mark index as not found, so we don't attempt the search twice
ImFontBaked_BuildGrowIndex(baked, codepoint + 1);
baked.IndexAdvanceX.Data[codepoint + 1] = baked.FallbackAdvanceX
baked.IndexLookup.Data[codepoint + 1] = IM_FONTGLYPH_INDEX_NOT_FOUND
return nil, only_load_advance_x
end
--- @param baked ImFontBaked
--- @param codepoint ImWchar
--- @return float
function ImFontBaked_BuildLoadGlyphAdvanceX(baked, codepoint)
if (baked.Size >= 128 --[[IMGUI_FONT_SIZE_THRESHOLD_FOR_LOADADVANCEXONLYMODE]]) or baked.LoadNoRenderOnLayout then
-- First load AdvanceX value used by CalcTextSize() API then load the rest when loaded by drawing API.
local only_advance_x
local glyph
glyph, only_advance_x = ImFontBaked_BuildLoadGlyph(baked, codepoint, only_advance_x)
--- @cast only_advance_x float
return glyph and glyph.AdvanceX or only_advance_x
else
local glyph = ImFontBaked_BuildLoadGlyph(baked, codepoint, nil)
return glyph and glyph.AdvanceX or baked.FallbackAdvanceX
end
end
-- This is not fast query
--- @param c ImWchar
--- @return bool
function MT.ImFont:IsGlyphInFont(c)
local atlas = self.OwnerAtlas
c = ImFontAtlas_FontHookRemapCodepoint(atlas, self, c)
for _, src in self.Sources:iter() do
local loader = src.FontLoader and src.FontLoader or atlas.FontLoader
if loader.FontSrcContainsGlyph ~= nil and loader.FontSrcContainsGlyph(atlas, src, c) then
return true
end
end
return false
end
-- This is manually inlined in CalcTextSizeA() and CalcWordWrapPosition(), with a non-inline call to BuildLoadGlyphGetAdvanceOrFallback().
--- @param c ImWchar
--- @return float
function MT.ImFontBaked:GetCharAdvance(c)
if c < self.IndexAdvanceX.Size then
-- Missing glyphs fitting inside index will have stored FallbackAdvanceX already.
local x = self.IndexAdvanceX.Data[c + 1]
if x >= 0.0 then
return x
end
end
return ImFontBaked_BuildLoadGlyphAdvanceX(self, c)
end
-- The point of this indirection is to not be inlined in debug mode in order to not bloat inner loop.b
function BuildLoadGlyphGetAdvanceOrFallback(baked, codepoint)
return ImFontBaked_BuildLoadGlyphAdvanceX(baked, codepoint)
end
-- Trim trailing space and find beginning of next line
--- @param text ImString
--- @param text_begin int
--- @param text_end int
--- @param flags ImDrawTextFlags
--- @return int
local function ImTextCalcWordWrapNextLineStart(text, text_begin, text_end, flags)
local pos = text_begin
if bit32.band(flags, 2 --[[ImDrawTextFlags.WrapKeepBlanks]]) == 0 then
while pos < text_end and ImCharIsBlankA(ImStrByte(text, pos)) do
pos = pos + 1
end
end
if pos < text_end and ImStrByte(text, pos) == 10 then -- '\n'
pos = pos + 1
end
return pos
end
--- @param bits ImU32[]
--- @param codepoint_min unsigned_int
--- @param codepoint_end unsigned_int
--- @param char_class ImWcharClass
function ImTextClassifierClear(bits, codepoint_min, codepoint_end, char_class)
for c = codepoint_min, codepoint_end - 1 do
ImTextClassifierSetCharClass(bits, codepoint_min, codepoint_end, char_class, c)
end
end
--- @param bits ImU32[]
--- @param codepoint_min unsigned_int
--- @param codepoint_end unsigned_int
--- @param char_class ImWcharClass
--- @param c unsigned_int
function ImTextClassifierSetCharClass(bits, codepoint_min, codepoint_end, char_class, c)
assert(c >= codepoint_min and c < codepoint_end)
-- IM_UNUSED(codepoint_end)
c = c - codepoint_min
local shift = bit32.lshift(bit32.band(c, 15), 1)
bits[bit32.rshift(c, 4) + 1] = bit32.bor(bit32.band(bits[bit32.rshift(c, 4) + 1], bit32.bnot(bit32.lshift(0x03, shift))), bit32.lshift(char_class, shift))
end
function ImTextClassifierSetCharClassFromStr(bits, codepoint_min, codepoint_end, char_class, s)
local s_end = #s + 1
local pos = 1
while (pos < s_end) do
local wanted, c = ImStd.ImTextCharFromUtf8(s, pos, s_end)
pos = pos + wanted
ImTextClassifierSetCharClass(bits, codepoint_min, codepoint_end, char_class, c)
end
end
function ImTextClassifierGet(_BITS, _CHAR_OFFSET)
return bit32.band(bit32.rshift(_BITS[bit32.rshift(_CHAR_OFFSET, 4) + 1], bit32.lshift(bit32.band(_CHAR_OFFSET, 15), 1)), 0x03)
end
-- 2-bit per character
local g_CharClassifierIsSeparator_0000_007f = {} for i = 1, 128 / 16 do g_CharClassifierIsSeparator_0000_007f[i] = 0 end
local g_CharClassifierIsSeparator_3000_300f = {} for i = 1, 16 / 16 do g_CharClassifierIsSeparator_3000_300f[i] = 0 end
function ImTextInitClassifiers()
if (ImTextClassifierGet(g_CharClassifierIsSeparator_0000_007f, 44) ~= 0) then -- ','
return
end
-- List of hardcoded separators: .,;!?'"
-- Making this dynamic given known ranges is trivial BUT requires us to standardize where you pass them as parameters. (#3002, #8503)
ImTextClassifierClear(g_CharClassifierIsSeparator_0000_007f, 0, 128, 2 --[[ImWcharClass.Other]])
ImTextClassifierSetCharClassFromStr(g_CharClassifierIsSeparator_0000_007f, 0, 128, 0 --[[ImWcharClass.Blank]], " \t")
ImTextClassifierSetCharClassFromStr(g_CharClassifierIsSeparator_0000_007f, 0, 128, 1 --[[ImWcharClass.Punct]], ".,;!?\"")
ImTextClassifierClear(g_CharClassifierIsSeparator_3000_300f, 0x3000, 0x300F, 2 --[[ImWcharClass.Other]])
ImTextClassifierSetCharClass(g_CharClassifierIsSeparator_3000_300f, 0x3000, 0x300F, 0 --[[ImWcharClass.Blank]], 0x3000)
ImTextClassifierSetCharClass(g_CharClassifierIsSeparator_3000_300f, 0x3000, 0x300F, 1 --[[ImWcharClass.Punct]], 0x3001)
ImTextClassifierSetCharClass(g_CharClassifierIsSeparator_3000_300f, 0x3000, 0x300F, 1 --[[ImWcharClass.Punct]], 0x3002)
end
-- Simple word-wrapping for English, not full-featured. Please submit failing cases!
-- This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end.
-- Refer to imgui_test_suite's "drawlist_text_wordwrap_1" for tests.
--- @param font ImFont
--- @param size float
--- @param text ImString
--- @param pos int
--- @param text_end int
--- @param wrap_width float
--- @param flags ImDrawTextFlags
--- @return int
local ImFontCalcWordWrapPositionEx_Uncached
local WRAP_MEMO_MAX = 4096
function ImFontCalcWordWrapPositionEx(font, size, text, pos, text_end, wrap_width, flags)
local baked = font:GetFontBaked(size)
if type(text) ~= "string" then
return ImFontCalcWordWrapPositionEx_Uncached(baked, size, text, pos, text_end, wrap_width, flags)
end
-- [Luau] memo per baked font and string: CalcTextSize() and RenderText() of wrapped text ask for the same wrap points
-- (same text/pos/width) every frame. The result only depends on these inputs and the (immutable once loaded) glyph advances.
-- ponytail: whole-memo reset at WRAP_MEMO_MAX strings, LRU if dynamic text churns it
local memo = baked._WrapMemo
if memo == nil or baked._WrapMemoN >= WRAP_MEMO_MAX then
memo = {}; baked._WrapMemo = memo; baked._WrapMemoN = 0
end
local e = memo[text]
if e == nil or e.ww ~= wrap_width or e.size ~= size or e.te ~= text_end or e.fl ~= flags then
if e == nil then baked._WrapMemoN = baked._WrapMemoN + 1 end
e = { ww = wrap_width, size = size, te = text_end, fl = flags }
memo[text] = e
else
local r = e[pos]
if r then return r end
end
local r = ImFontCalcWordWrapPositionEx_Uncached(baked, size, text, pos, text_end, wrap_width, flags)
e[pos] = r
return r
end
@native
function ImFontCalcWordWrapPositionEx_Uncached(baked, size, text, pos, text_end, wrap_width, flags)
-- For references, possible wrap point marked with ^
-- "aaa bbb, ccc,ddd. eee fff. ggg!"
-- ^ ^ ^ ^ ^__ ^ ^
-- Skip extra blanks after a line returns (that includes not counting them in width computation)
-- e.g. "Hello world" --> "Hello" "World"
-- Cut words that cannot possibly fit within one line.
-- e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish"
-- [Luau] baked = font:GetFontBaked(size) is done by the caller
local scale = size / baked.Size
local line_width = 0.0
local blank_width = 0.0
wrap_width = wrap_width / scale -- We work with unscaled widths to avoid scaling every characters
local s = pos
assert(text_end ~= nil)
local prev_type = 2 --[[ImWcharClass.Other]]
local keep_blanks = bit32.band(flags, 2 --[[ImDrawTextFlags.WrapKeepBlanks]]) ~= 0
-- Find next wrapping point
--local span_begin = s
local span_end = s
local span_width = 0.0
local stop = false
local text_is_str = type(text) == "string" -- [Luau] ImStrByte() inlined (once per character)
local adv_data, adv_n = baked.IndexAdvanceX.Data, baked.IndexAdvanceX.Size -- [Luau] hoisted, refreshed after a glyph load
local CLASS_BLANK, CLASS_PUNCT, CLASS_OTHER = 0 --[[ImWcharClass.Blank]], 1 --[[ImWcharClass.Punct]], 2 --[[ImWcharClass.Other]]
local classifier_0000_007f = g_CharClassifierIsSeparator_0000_007f
while s < text_end do
repeat
--- @type unsigned_int
local c
if text_is_str then c = string.byte(text, s) else c = text[s] end
local next_s
if c < 0x80 then
next_s = s + 1
else
local wanted, out_char = ImStd.ImTextCharFromUtf8(text, s, text_end)
c = out_char
next_s = s + wanted
end
if c < 32 then
if c == 10 then -- '\n'
return s -- Direct return, skip "Wrap_width is too small to fit anything" path.
end
if c == 13 then -- '\r'
s = next_s -- Fast-skip
break
end
end
-- Optimized inline version of 'float char_width = GetCharAdvance((ImWchar)c);'
local char_width = (c < adv_n) and adv_data[c + 1] or -1.0
if char_width < 0.0 then
char_width = BuildLoadGlyphGetAdvanceOrFallback(baked, c)
adv_data, adv_n = baked.IndexAdvanceX.Data, baked.IndexAdvanceX.Size
end
-- Classify current character
local curr_type
if c < 128 then -- [Luau] ImTextClassifierGet() inlined
curr_type = bit32.band(bit32.rshift(classifier_0000_007f[bit32.rshift(c, 4) + 1], bit32.lshift(bit32.band(c, 15), 1)), 0x03)
elseif (c >= 0x3000 and c < 0x3010) then
curr_type = ImTextClassifierGet(g_CharClassifierIsSeparator_3000_300f, bit32.band(c, 15))
else
curr_type = CLASS_OTHER
end
if curr_type == CLASS_BLANK then
-- End span: 'A ' or '. '
if (prev_type ~= CLASS_BLANK and not keep_blanks) then
span_end = s
line_width = line_width + span_width
span_width = 0.0
end
blank_width = blank_width + char_width
else
-- End span: '.X' unless X is a digit
if (prev_type == CLASS_PUNCT and curr_type ~= CLASS_PUNCT and not (c >= 48 and c <= 57)) then -- FIXME: Digit checks might be removed if allow custom separators (#8503)
span_end = s
line_width = line_width + (span_width + blank_width)
blank_width = 0.0
span_width = 0.0
-- End span: 'A ' or '. '
elseif (prev_type == CLASS_BLANK and keep_blanks) then
span_end = s
line_width = line_width + (span_width + blank_width)
blank_width = 0.0
span_width = 0.0
end
span_width = span_width + char_width
end
if (span_width + blank_width + line_width > wrap_width) then
if span_width + blank_width > wrap_width then
stop = true; break
end
-- FIXME: Narrow wrapping e.g. "A quick brown" -> "Quic|k br|own", would require knowing if span is going to be longer than wrap_width.
--if (span_width > wrap_width and not is_blank and not was_blank) then
-- return s
--end
return span_end
end
prev_type = curr_type
s = next_s
until true
if stop then break end
end
-- Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
-- +1 may not be a character start point in UTF-8 but it's ok because caller loops use (text >= word_wrap_eol).
if s == pos and s < text_end then
local bytes = ImStd.ImTextCountUtf8BytesFromChar(text, s, text_end)
return s + bytes
end
return s
end
--- @param size float
--- @param text ImString
--- @param pos int # [Luau] 1-based start position (C++ 'text' pointer)
--- @param text_end int # exclusive
--- @param wrap_width float
--- @return int
function MT.ImFont:CalcWordWrapPosition(size, text, pos, text_end, wrap_width)
return ImFontCalcWordWrapPositionEx(self, size, text, pos, text_end, wrap_width, 0 --[[ImDrawTextFlags.None]])
end
--- @param font ImFont
--- @param size float
--- @param max_width float
--- @param wrap_width float
--- @param text ImString
--- @param text_begin int
--- @param text_end_display? int
--- @param text_end? int
--- @param out_offset? ImVec2
--- @param flags ImDrawTextFlags
--- @return ImVec2 text_size
--- @return int remaining
local TEXT_SIZE_CACHE_MAX = 4096
@native
function ImFontCalcTextSizeEx(font, size, max_width, wrap_width, text, text_begin, text_end_display, text_end, out_offset, flags)
local text_is_str = type(text) == "string"
-- [Luau] cache for the common case (whole string, single pass, no wrap/limit): per baked font, text -> unscaled width, line count.
-- Advances of a baked font never change once loaded, so entries stay valid. ponytail: whole-cache reset at TEXT_SIZE_CACHE_MAX entries, LRU if dynamic text churns it
local cache_key = nil
local wrap_cache = nil
local text_len1 = text_is_str and #text + 1 or -1
local cacheable = text_is_str and text_begin == 1 and out_offset == nil and max_width >= 3.4028234663853e+38 --[[FLT_MAX]]
and (text_end == nil or text_end == text_len1) and (text_end_display == nil or text_end_display == text_len1) and (flags == nil or bit32.band(flags, 4 --[[ImDrawTextFlags.StopOnNewLine]]) == 0)
if cacheable and wrap_width <= 0.0 then
local baked = font:GetFontBaked(size)
local cache = baked._TextSizeCache
if cache == nil or baked._TextSizeCacheN >= TEXT_SIZE_CACHE_MAX then
cache = {}; baked._TextSizeCache = cache; baked._TextSizeCacheN = 0
end
local e = cache[text]
if e then
return ImVec2(e[1] * (size / baked.Size), e[2] * size), text_len1
end
cache_key = text
elseif cacheable and wrap_width > 0.0 then
-- [Luau] same for wrapped text (TextWrapped() measures every line every frame): exact results per (size, wrap width, text)
local baked = font:GetFontBaked(size)
local cache = baked._TextSizeCache
if cache == nil or baked._TextSizeCacheN >= TEXT_SIZE_CACHE_MAX then
cache = {}; baked._TextSizeCache = cache; baked._TextSizeCacheN = 0
end
local by_size = cache[size]
if by_size == nil then by_size = {}; cache[size] = by_size end
local by_wrap = by_size[wrap_width]
if by_wrap == nil then by_wrap = {}; by_size[wrap_width] = by_wrap end
local e = by_wrap[text]
if e then
return ImVec2(e[1], e[2]), e[3]
end
wrap_cache = by_wrap
end
if not text_end then
text_end = #text + 1 -- FIXME-OPT: Need to avoid this.
end
if not text_end_display then
text_end_display = text_end
end
local baked = font:GetFontBaked(size)
local line_height = size
local scale = line_height / baked.Size
local text_size = ImVec2()
local line_width = 0.0
local word_wrap_enabled = (wrap_width > 0.0)
local word_wrap_eol
local s = text_begin
local stop = false
local adv_data, adv_n = baked.IndexAdvanceX.Data, baked.IndexAdvanceX.Size -- [Luau] hoisted, refreshed after a glyph load
while s < text_end_display do
repeat
-- Word-wrapping
if word_wrap_enabled then
-- Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
if not word_wrap_eol then
word_wrap_eol = ImFontCalcWordWrapPositionEx(font, size, text, s, text_end, wrap_width - line_width, flags)
adv_data, adv_n = baked.IndexAdvanceX.Data, baked.IndexAdvanceX.Size -- [Luau] may have loaded glyphs
end
if s >= word_wrap_eol then
if text_size.x < line_width then
text_size.x = line_width
end
text_size.y = text_size.y + line_height
line_width = 0.0
s = ImTextCalcWordWrapNextLineStart(text, s, text_end, flags) -- Wrapping skips upcoming blanks
if bit32.band(flags, 4 --[[ImDrawTextFlags.StopOnNewLine]]) ~= 0 then
stop = true; break
end
word_wrap_eol = nil
break
end
end
-- Decode and advance source
local prev_s = s
local c
if text_is_str then c = string.byte(text, s) else c = text[s] end -- [Luau] ImStrByte() inlined
if c < 0x80 then
s = s + 1
else
local wanted, out_char = ImStd.ImTextCharFromUtf8(text, s, text_end)
c = out_char
s = s + wanted
end
if c == 10 then -- '\n'
text_size.x = ImMax(text_size.x, line_width)
text_size.y = text_size.y + line_height
line_width = 0.0
if bit32.band(flags, 4 --[[ImDrawTextFlags.StopOnNewLine]]) ~= 0 then
stop = true; break
end
break
end
if c == 13 then -- '\r'
break
end
-- Optimized inline version of 'float char_width = GetCharAdvance((ImWchar)c);'
local char_width = (c < adv_n) and adv_data[c + 1] or -1.0
if (char_width < 0.0) then
char_width = BuildLoadGlyphGetAdvanceOrFallback(baked, c)
adv_data, adv_n = baked.IndexAdvanceX.Data, baked.IndexAdvanceX.Size
end
char_width = char_width * scale
if (line_width + char_width >= max_width) then
s = prev_s
stop = true; break
end
line_width = line_width + char_width
until true
if stop then break end
end
if (text_size.x < line_width) then
text_size.x = line_width
end
if out_offset then -- offset allow for the possibility of sitting after a trailing \n
out_offset.x = line_width
out_offset.y = text_size.y + line_height
end
if (line_width > 0 or text_size.y == 0.0) then -- whereas size.y will ignore the trailing \n
text_size.y = text_size.y + line_height
end
local out_remaining = s
if cache_key then
baked._TextSizeCache[cache_key] = { text_size.x / scale, text_size.y / line_height }
baked._TextSizeCacheN = baked._TextSizeCacheN + 1
elseif wrap_cache then
wrap_cache[text] = { text_size.x, text_size.y, out_remaining }
baked._TextSizeCacheN = baked._TextSizeCacheN + 1
end
return text_size, out_remaining
end
--- @param size float
--- @param max_width float
--- @param wrap_width float
--- @param text ImString
--- @param text_begin int
--- @param text_end? int
function MT.ImFont:CalcTextSizeA(size, max_width, wrap_width, text, text_begin, text_end)
return ImFontCalcTextSizeEx(self, size, max_width, wrap_width, text, text_begin, text_end, text_end, nil, 0 --[[ImDrawTextFlags.None]])
end
--- Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
--- @param draw_list ImDrawList
--- @param size float
--- @param pos ImVec2
--- @param col ImU32
--- @param c ImWchar
--- @param cpu_fine_clip ImVec4
local RenderChar_a, RenderChar_c, RenderChar_uv_a, RenderChar_uv_c = ImVec2(), ImVec2(), ImVec2(), ImVec2()
function MT.ImFont:RenderChar(draw_list, size, pos, col, c, cpu_fine_clip)
local baked = self:GetFontBaked(size)
local glyph = baked:FindGlyph(c)
if not glyph or not glyph.Visible then
return
end
if glyph.Colored then
col = bit32.bor(col, bit32.bnot(4278190080 --[[IM_COL32_A_MASK]]))
end
local scale
if size >= 0.0 then
scale = size / baked.Size
else
scale = 1.0
end
local x = pos.x
local y = pos.y
if bit32.band(draw_list.Flags, 1048576 --[[ImDrawFlags.TextNoPixelSnap]]) == 0 then
x = IM_TRUNC(x)
y = IM_TRUNC(y)
end
local x1 = x + glyph.X0 * scale
local x2 = x + glyph.X1 * scale
if cpu_fine_clip and (x1 > cpu_fine_clip.z or x2 < cpu_fine_clip.x) then
return
end
local y1 = y + glyph.Y0 * scale
local y2 = y + glyph.Y1 * scale
local u1 = glyph.U0
local v1 = glyph.V0
local u2 = glyph.U1
local v2 = glyph.V1
-- Always CPU fine clip. Code extracted from RenderText().
-- CPU side clipping used to fit text in their frame when the frame is too small. Only does clipping for axis aligned quads.
if cpu_fine_clip then
if x1 < cpu_fine_clip.x then u1 = u1 + (1.0 - (x2 - cpu_fine_clip.x) / (x2 - x1)) * (u2 - u1); x1 = cpu_fine_clip.x end
if y1 < cpu_fine_clip.y then v1 = v1 + (1.0 - (y2 - cpu_fine_clip.y) / (y2 - y1)) * (v2 - v1); y1 = cpu_fine_clip.y end
if x2 > cpu_fine_clip.z then u2 = u1 + ((cpu_fine_clip.z - x1) / (x2 - x1)) * (u2 - u1); x2 = cpu_fine_clip.z end
if y2 > cpu_fine_clip.w then v2 = v1 + ((cpu_fine_clip.w - y1) / (y2 - y1)) * (v2 - v1); y2 = cpu_fine_clip.w end
if y1 >= y2 then return end
end
draw_list:PrimReserve(6, 4)
-- [Luau] scratch vectors, PrimRectUV() only reads them
local a, c, uv_a, uv_c = RenderChar_a, RenderChar_c, RenderChar_uv_a, RenderChar_uv_c
a.x = x1; a.y = y1; c.x = x2; c.y = y2; uv_a.x = u1; uv_a.y = v1; uv_c.x = u2; uv_c.y = v2
draw_list:PrimRectUV(a, c, uv_a, uv_c, col)
end
function MT.ImFontAtlas:AddFont(font_cfg_in)
-- Sanity Checks
assert(not self.Locked, "Cannot modify a locked ImFontAtlas!")
assert((font_cfg_in.FontData ~= nil and font_cfg_in.FontDataSize > 0) or (font_cfg_in.FontLoader ~= nil))
assert(font_cfg_in.SizePixels > 0.0, "Is ImFontConfig struct correctly initialized?")
assert(font_cfg_in.RasterizerDensity > 0.0, "Is ImFontConfig struct correctly initialized?")
if font_cfg_in.GlyphOffset.x ~= 0.0 or font_cfg_in.GlyphOffset.y ~= 0.0 or
font_cfg_in.GlyphMinAdvanceX ~= 0.0 or font_cfg_in.GlyphMaxAdvanceX ~= 3.4028234663853e+38 --[[FLT_MAX]] then
assert(font_cfg_in.SizePixels ~= 0.0,
"Specifying glyph offset/advances requires a reference size to base it on.")
end
-- Lazily create builder on the first call to AddFont
if self.Builder == nil then
ImFontAtlasBuildInit(self)
end
-- Create new font
local is_first_font = (self.Fonts.Size == 0)
local font
if not font_cfg_in.MergeMode then
font = ImFont()
font.FontId = self.FontNextUniqueID
self.FontNextUniqueID = self.FontNextUniqueID + 1
font.Flags = font_cfg_in.Flags
font.LegacySize = font_cfg_in.SizePixels
font.CurrentRasterizerDensity = font_cfg_in.RasterizerDensity
self.Fonts:push_back(font)
else
assert(self.Fonts.Size > 0, "Cannot use MergeMode for the first font!") -- When using MergeMode make sure that a font has already been added before.
font = (font_cfg_in.DstFont ~= nil) and font_cfg_in.DstFont or self.Fonts:back()
ImFontAtlasFontDiscardBakes(self, font, 0) -- Need to discard bakes if the font was already used, because baked->FontLoaderDatas[] will change size. (#9162)
end
-- Add to list
self.Sources:push_back(font_cfg_in)
local font_cfg = self.Sources:back()
if (font_cfg.DstFont == nil) then
font_cfg.DstFont = font
end
font.Sources:push_back(font_cfg)
ImFontAtlasBuildUpdatePointers(self)
-- Sanity check
-- We don't round cfg.SizePixels yet as relative size of merged fonts are used afterwards.
if font_cfg.GlyphExcludeRanges ~= nil then
local size = 0
for _, v in ipairs(font_cfg.GlyphExcludeRanges) do if v == 0 then break end size = size + 1 end
assert(bit32.band(size, 1) == 0, "GlyphExcludeRanges[] size must be multiple of two!")
assert(size <= 64, "GlyphExcludeRanges[] size must be small!")
end
if font_cfg.FontLoader ~= nil then
assert(font_cfg.FontLoader.FontBakedLoadGlyph ~= nil)
assert(font_cfg.FontLoader.LoaderInit == nil and font_cfg.FontLoader.LoaderShutdown == nil) -- FIXME-NEWATLAS: Unsupported yet.
end
-- | Target w/ Implicit RefSize | Target w/ Explicit RefSize |
-- Adding w/ Implicit RefSize: | OK (same scale) | OK (same scale) |
-- Adding w/ Explicit RefSize: | KO | OK (custom scale) |
if font_cfg_in.MergeMode and font_cfg_in.SizePixels > 0 then
assert(bit32.band(font.Flags, 16 --[[ImFontFlags.ImplicitRefSize]]) == 0, "Cannot use MergeMode with an explicit reference size when the destination font used an implicit reference size!")
end
assert(font_cfg.FontLoaderData == nil)
if not ImFontAtlasFontSourceInit(self, font_cfg) then
-- Rollback (this is a fragile/rarely exercised code-path. TestSuite's "misc_atlas_add_invalid_font" aim to test this)
ImFontAtlasFontDestroySourceData(self, font_cfg)
self.Sources:pop_back()
font.Sources:pop_back()
if not font_cfg.MergeMode then
font = nil
self.Fonts:pop_back()
end
return nil
end
ImFontAtlasFontSourceAddToFont(self, font, font_cfg)
if (is_first_font) then
ImFontAtlasBuildNotifySetFont(self, nil, font)
end
return font
end
-- Default font TTF is compressed with stb_compress then base85 encoded (see misc/fonts/binary_to_compressed_c.cpp for encoder)
local stb_decompress_length
local stb_decompress
local function Decode85Byte(c) return (c >= 92) and (c - 36) or (c - 35) end -- c >= '\\'
--- @param src string
--- @param dst buffer
local function Decode85(src, dst)
local d = 0
local src_len = #src
local s = 1
while s + 4 <= src_len do -- [Luau] while (*src): stop at the last complete 5-char group
local b0, b1, b2, b3, b4 = string.byte(src, s, s + 4)
local tmp = (Decode85Byte(b0) + 85 * (Decode85Byte(b1) + 85 * (Decode85Byte(b2) + 85 * (Decode85Byte(b3) + 85 * Decode85Byte(b4))))) % 4294967296
buffer.writeu8(dst, d + 0, tmp % 256); buffer.writeu8(dst, d + 1, math.floor(tmp / 256) % 256); buffer.writeu8(dst, d + 2, math.floor(tmp / 65536) % 256); buffer.writeu8(dst, d + 3, math.floor(tmp / 16777216) % 256) -- We can't assume little-endianness.
s = s + 5
d = d + 4
end
end
-- This duplicates some of the logic in UpdateFontsNewFrame() which is a bit chicken-and-eggy/tricky to extract due to variety of codepaths and possible initialization ordering.
--- @param ctx ImGuiContext
--- @return float
local function GetExpectedContextFontSize(ctx)
return ((ctx.Style.FontSizeBase > 0.0) and ctx.Style.FontSizeBase or 13.0) * ctx.Style.FontScaleMain * ctx.Style.FontScaleDpi
end
-- Legacy function with heuristic to select Pixel or Vector font.
-- The selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold at the time of adding the default font.
-- Prefer calling AddFontDefaultVector() or AddFontDefaultBitmap() based on your own logic.
--- @param font_cfg ImFontConfig
function MT.ImFontAtlas:AddFontDefault(font_cfg)
if self.OwnerContext == nil or GetExpectedContextFontSize(self.OwnerContext) >= 15.0 then
return self:AddFontDefaultVector(font_cfg)
else
return self:AddFontDefaultBitmap(font_cfg)
end
end
function MT.ImFontAtlas:AddFontFromMemoryTTF(font_data, font_data_size, size_pixels, font_cfg_template, glyph_ranges)
assert(not self.Locked, "Cannot modify a locked ImFontAtlas!")
local font_cfg = font_cfg_template and font_cfg_template or ImFontConfig()
assert(font_cfg.FontData == nil)
assert(font_data_size > 100, "Incorrect value for font_data_size!") -- Heuristic to prevent accidentally passing a wrong value to font_data_size.
font_cfg.FontData = font_data
font_cfg.FontDataSize = font_data_size
font_cfg.SizePixels = (size_pixels > 0.0) and size_pixels or font_cfg.SizePixels
if glyph_ranges then
font_cfg.GlyphRanges = glyph_ranges
end
return self:AddFont(font_cfg)
end
--- @param compressed_ttf_data buffer|string|ImU8[] # [Luau] stb_compress() output: buffer, string or 1-based byte table
--- @param compressed_ttf_size int
function MT.ImFontAtlas:AddFontFromMemoryCompressedTTF(compressed_ttf_data, compressed_ttf_size, size_pixels, font_cfg_template, glyph_ranges)
-- [Luau] normalize input to a buffer
if type(compressed_ttf_data) == "string" then
compressed_ttf_data = buffer.fromstring(compressed_ttf_data)
elseif type(compressed_ttf_data) == "table" then
local b = buffer.create(compressed_ttf_size)
for n = 1, compressed_ttf_size do buffer.writeu8(b, n - 1, compressed_ttf_data[n]) end
compressed_ttf_data = b
end
local buf_decompressed_size = stb_decompress_length(compressed_ttf_data)
local buf_decompressed_data = buffer.create(buf_decompressed_size)
stb_decompress(buf_decompressed_data, compressed_ttf_data, compressed_ttf_size)
-- [Luau] FontData is a 1-based byte table (see stbtt / ImFileLoadToMemory)
local ttf_data = table.create and table.create(buf_decompressed_size) or {}
for n = 1, buf_decompressed_size do ttf_data[n] = buffer.readu8(buf_decompressed_data, n - 1) end
local font_cfg = font_cfg_template and font_cfg_template or ImFontConfig()
assert(font_cfg.FontData == nil)
font_cfg.FontDataOwnedByAtlas = true
return self:AddFontFromMemoryTTF(ttf_data, buf_decompressed_size, size_pixels or 0.0, font_cfg, glyph_ranges)
end
--- @param compressed_ttf_data_base85 string
function MT.ImFontAtlas:AddFontFromMemoryCompressedBase85TTF(compressed_ttf_data_base85, size_pixels, font_cfg, glyph_ranges)
local compressed_ttf_size = math.floor((#compressed_ttf_data_base85 + 4) / 5) * 4
local compressed_ttf = buffer.create(compressed_ttf_size)
Decode85(compressed_ttf_data_base85, compressed_ttf)
local font = self:AddFontFromMemoryCompressedTTF(compressed_ttf, compressed_ttf_size, size_pixels, font_cfg, glyph_ranges)
return font
end
--- @param font ImFont
function MT.ImFontAtlas:RemoveFont(font)
assert(not self.Locked, "Cannot modify a locked ImFontAtlas!")
ImFontAtlasFontDestroyOutput(self, font)
for _, src in font.Sources:iter() do
ImFontAtlasFontDestroySourceData(self, src)
end
local src_n = 1
while src_n <= self.Sources.Size do
if self.Sources.Data[src_n].DstFont == font then
self.Sources:erase(src_n)
else
src_n = src_n + 1
end
end
local removed = self.Fonts:find_erase(font)
assert(removed)
ImFontAtlasBuildUpdatePointers(self)
font.OwnerAtlas = nil
-- Notify external systems
local new_current_font = nil
if self.Fonts.Size > 0 then new_current_font = self.Fonts.Data[1] end
ImFontAtlasBuildNotifySetFont(self, font, new_current_font)
end
function MT.ImFontAtlas:AddFontFromFileTTF(filename, size_pixels, font_cfg_template, glyph_ranges)
assert(not self.Locked, "Cannot modify a locked ImFontAtlas!")
local data, data_size = ImStd.ImFileLoadToMemory(filename, "rb")
if not data then
if (font_cfg_template == nil or (bit32.band(font_cfg_template.Flags, 2 --[[ImFontFlags.NoLoadError]]) == 0)) then
-- IMGUI_DEBUG_LOG("While loading '%s'\n", filename)
IM_ASSERT_USER_ERROR(0, "Could not load font file!")
end
return nil
end
local font_cfg = font_cfg_template and font_cfg_template or ImFontConfig()
if not font_cfg.Name or font_cfg.Name == "" then
-- Store a short copy of filename into the font name for convenience
for i = #filename, 1, -1 do
local c = string.byte(filename, i, i)
if (c == 47 or c == 92) then -- '/' or '\\'
font_cfg.Name = string.sub(filename, i + 1)
break
end
end
end
return self:AddFontFromMemoryTTF(data, data_size, size_pixels, font_cfg, glyph_ranges)
end
function GetDefaultFontDataProggyClean()
return ImStd.ImFileLoadToMemory("fonts/ProggyClean.ttf", "rb")
end
function GetDefaultFontDataProggyForever()
return ImStd.ImFileLoadToMemory("fonts/ProggyForever.ttf", "rb")
end
-- TODO: IMGUI_DISABLE_DEFAULT_FONT, IMGUI_DISABLE_DEFAULT_FONT_BITMAP
-- Load ProggyClean.ttf. Default size 13, disable oversampling.
-- If you want a similar font which may be better scaled, consider using AddFontDefaultVector().
--- @param font_cfg_template ImFontConfig
function MT.ImFontAtlas:AddFontDefaultBitmap(font_cfg_template)
local font_cfg = font_cfg_template and font_cfg_template or ImFontConfig()
if not font_cfg_template then
font_cfg.PixelSnapH = true -- Prevents sub-integer scaling factors at lower-level layers.
end
if font_cfg.SizePixels <= 0.0 then
font_cfg.SizePixels = 13.0 -- This only serves (1) as a reference for GlyphOffset.y setting and (2) as a default for pre-1.92 backend.
font_cfg.Flags = bit32.bor(font_cfg.Flags, 16 --[[ImFontFlags.ImplicitRefSize]])
end
if not font_cfg.Name or font_cfg.Name == "" then
font_cfg.Name = "ProggyClean.ttf"
end
font_cfg.EllipsisChar = 0x0085
font_cfg.GlyphOffset.y = font_cfg.GlyphOffset.y + 1.0 * (font_cfg.SizePixels / 13.0) -- Add +1 offset per 13 units
local ttf_data, ttf_data_size = GetDefaultFontDataProggyClean()
return self:AddFontFromMemoryTTF(ttf_data, ttf_data_size, font_cfg.SizePixels, font_cfg)
end
-- Load a minimal version of ProggyForever, designed to match our good old ProggyClean, but nicely scalable.
-- (See build script in https://github.com/ocornut/proggyforever for details)
--- @param font_cfg_template ImFontConfig
function MT.ImFontAtlas:AddFontDefaultVector(font_cfg_template)
local font_cfg = font_cfg_template and font_cfg_template or ImFontConfig()
if not font_cfg_template then
font_cfg.PixelSnapH = true -- Precisely match ProggyClean, but prevents sub-integer scaling factors at lower-level layers.
end
if font_cfg.SizePixels <= 0.0 then
font_cfg.SizePixels = 13.0
font_cfg.Flags = bit32.bor(font_cfg.Flags, 16 --[[ImFontFlags.ImplicitRefSize]])
end
if not font_cfg.Name or font_cfg.Name == "" then
font_cfg.Name = "ProggyForever.ttf"
end
font_cfg.ExtraSizeScale = font_cfg.ExtraSizeScale * 1.015 -- Match ProggyClean
font_cfg.GlyphOffset.y = font_cfg.GlyphOffset.y + 0.5 * (font_cfg.SizePixels / 16.0) -- Closer match ProggyClean + avoid descenders going too high (with current code).
local ttf_data, ttf_data_size = GetDefaultFontDataProggyForever()
return self:AddFontFromMemoryTTF(ttf_data, ttf_data_size, font_cfg.SizePixels, font_cfg)
end
function MT.ImFontAtlas:GetCustomRect(id, out_r)
local r = ImFontAtlasPackGetRectSafe(self, id)
if r == nil then
return false
end
assert(self.TexData.Width > 0 and self.TexData.Height > 0) -- Font atlas needs to be built before we can calculate UV coordinates
if out_r == nil then
return true
end
out_r.x = r.x
out_r.y = r.y
out_r.w = r.w
out_r.h = r.h
ImVec2_Copy(out_r.uv0, ImVec2((r.x), (r.y)) * self.TexUvScale)
ImVec2_Copy(out_r.uv1, ImVec2((r.x + r.w), (r.y + r.h)) * self.TexUvScale)
return true
end
--- @param atlas ImFontAtlas
--- @param cursor_type ImGuiMouseCursor
--- @param out_offset ImVec2
--- @param out_size ImVec2
--- @param out_uv_border ImVec2[]
--- @param out_uv_fill ImVec2[]
function ImFontAtlasGetMouseCursorTexData(atlas, cursor_type, out_offset, out_size, out_uv_border, out_uv_fill)
if cursor_type <= (-1) --[[ImGuiMouseCursor.None]] or cursor_type >= 11 --[[ImGuiMouseCursor.COUNT]] then
return false
end
if bit32.band(atlas.Flags, 2 --[[ImFontAtlasFlags.NoMouseCursors]]) ~= 0 then
return false
end
local r = ImFontAtlasPackGetRect(atlas, atlas.Builder.PackIdMouseCursors)
local pos = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type + 1][1] + ImVec2(r.x, r.y)
local size = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type + 1][2]
ImVec2_Copy(out_size, size)
ImVec2_Copy(out_offset, FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type + 1][3])
ImVec2_Copy(out_uv_border[1], (pos) * atlas.TexUvScale)
ImVec2_Copy(out_uv_border[2], (pos + size) * atlas.TexUvScale)
pos.x = pos.x + (122 --[[FONT_ATLAS_DEFAULT_TEX_DATA_W]] + 1)
ImVec2_Copy(out_uv_fill[1], (pos) * atlas.TexUvScale)
ImVec2_Copy(out_uv_fill[2], (pos + size) * atlas.TexUvScale)
return true
end
-- At it is common to do an AddCustomRect() followed by a GetCustomRect(), we provide an optional 'out_r' argument to retrieve the info straight away.
function MT.ImFontAtlas:AddCustomRect(width, height, out_r)
assert(width > 0 and width <= 0xFFFF)
assert(height > 0 and height <= 0xFFFF)
if (self.Builder == nil) then
ImFontAtlasBuildInit(self)
end
local r_id = ImFontAtlasPackAddRect(self, width, height)
if (r_id == (-1) --[[ImFontAtlasRectId_Invalid]]) then
return (-1) --[[ImFontAtlasRectId_Invalid]]
end
if (out_r ~= nil) then
self:GetCustomRect(r_id, out_r)
end
if (self.RendererHasTextures) then
local r = ImFontAtlasPackGetRect(self, r_id)
ImFontAtlasTextureBlockQueueUpload(self, self.TexData, r.x, r.y, r.w, r.h)
end
return r_id
end
function MT.ImFontAtlas:RemoveCustomRect(id)
if ImFontAtlasPackGetRectSafe(self, id) == nil then
return
end
ImFontAtlasPackDiscardRect(self, id)
end
local function IM_NORMALIZE2F_OVER_ZERO(VX, VY)
local d2 = VX * VX + VY * VY
if d2 > 0.0 then
local inv_len = ImRsqrt(d2)
VX = VX * inv_len
VY = VY * inv_len
end
return VX, VY
end
local function IM_FIXNORMAL2F(VX, VY)
local IM_FIXNORMAL2F_MAX_INVLEN2 = 100 -- 500.0f (see #4053, #3366)
local d2 = VX * VX + VY * VY
if d2 > 0.000001 then
local inv_len2 = 1.0 / d2
if inv_len2 > IM_FIXNORMAL2F_MAX_INVLEN2 then
inv_len2 = IM_FIXNORMAL2F_MAX_INVLEN2
end
VX = VX * inv_len2
VY = VY * inv_len2
end
return VX, VY
end
-- - The miter limit describes how much the miter cross section can be scaled before we turn it into a bevel instead.
-- The minimum value of miter limit is 1.0, which translates to 180 (all corners are bevels), the higher the number,
-- the closer to zero the angle is (never bevel a corner). The miter limit angle can be calculated as:
-- miter_limit_angle = 2 * acos(1 / miter_limit)
-- - We can use the miter limit angle to figure maximum number of beveled corners in a convex polygon,
-- to prevent having allocate much extra memory (for a reasonable miter limit).
-- (n - 2) * 360 > k * miter_limit_angle + (n - k) * 360
-- Where, n = number of corners in a polygon, k = beveled corners. Solved for k, simplifies to:
-- k < 360 / (180 - miter_limit_angle)
-- To find value strictly less than:
-- max_bevels = k = floor(360/(180-miter_limit_angle))
local IM_POLYLINE_MITER_ANGLE_LIMIT = -0.9999619 -- Safeguard for miter corner calculation to avoid div by zero. cos(179.5)
local IM_POLYLINE_MITER_LIMIT = 4.0 -- (~29 deg corner)
local IM_POLYLINE_CONVEX_POLY_MAX_BEVELS = 2 -- For miter value 4.0
--- @param col ImU32
--- @param alpha_mul float
local function ImAlphaMultiply(col, alpha_mul)
IM_ASSERT_PARANOID(alpha_mul >= 0.0 and alpha_mul < 1.0) -- We don't clamp!
local a = bit32.rshift(bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]), 24 --[[IM_COL32_A_SHIFT]])
a = math.floor(a * alpha_mul) -- We don't need to clamp 0..255 because alpha is in 0..1 range.
return bit32.bor(bit32.band(col, bit32.bnot(4278190080 --[[IM_COL32_A_MASK]])), bit32.lshift(a, 24 --[[IM_COL32_A_SHIFT]]))
end
-- #define _GetStrokePos(_FLAGS, _DEFAULT_STROKE_POS)
local function _GetStrokePos(flags, default_stroke_pos)
local stroke_pos = bit32.band(flags, 917504 --[[ImDrawFlags.StrokeMask_]])
if stroke_pos ~= 0 then return stroke_pos end
return default_stroke_pos
end
-------------------------------------------------------------------------------
-- [SECTION] ImDrawData
-------------------------------------------------------------------------------
function MT.ImDrawData:Clear()
self.Valid = false
self.FrameCount = 0; self.TotalIdxCount = 0; self.TotalVtxCount = 0
self.CmdLists:resize(0) -- The ImDrawList are NOT owned by ImDrawData but e.g. by ImGuiContext, so we don't clear them.
self.DisplayPos = ImVec2(0.0, 0.0)
self.DisplaySize = ImVec2(0.0, 0.0)
self.FramebufferScale = ImVec2(0.0, 0.0)
self.OwnerViewport = nil
self.Textures = nil
end
-- Important: 'out_list' is generally going to be draw_data.CmdLists, but may be another temporary list
-- as long at it is expected that the result will be later merged into draw_data.CmdLists[].
function ImGui.AddDrawListToDrawDataEx(draw_data, out_list, draw_list)
if draw_list.CmdBuffer.Size == 0 then
return
end
if draw_list.CmdBuffer.Size == 1 and draw_list.CmdBuffer.Data[1].ElemCount == 0 and draw_list.CmdBuffer.Data[1].UserCallback == nil then
return
end
-- Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
-- May trigger for you if you are using PrimXXX functions incorrectly.
assert(draw_list.VtxBuffer.Size == 0 or draw_list._VtxWritePtr == draw_list.VtxBuffer.Size + 1)
assert(draw_list.IdxBuffer.Size == 0 or draw_list._IdxWritePtr == draw_list.IdxBuffer.Size + 1)
if (bit32.band(draw_list.Flags, 4194304 --[[ImDrawFlags.UseVtxOffset]]) == 0) then
assert(draw_list._VtxCurrentIdx == draw_list.VtxBuffer.Size + 1)
end
-- Add to output list + records state in ImDrawData
out_list:push_back(draw_list)
draw_data.TotalVtxCount = draw_data.TotalVtxCount + draw_list.VtxBuffer.Size
draw_data.TotalIdxCount = draw_data.TotalIdxCount + draw_list.IdxBuffer.Size
end
function MT.ImDrawData:AddDrawList(draw_list)
draw_list:_PopUnusedDrawCmd()
ImGui.AddDrawListToDrawDataEx(self, self.CmdLists, draw_list)
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) * 36 --[[VTX_STRIDE]], src, (idx[j] - 1) * 36 --[[VTX_STRIDE]], 36 --[[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_MAX = 512
function IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(_RAD, _MAXERROR) return ImClamp(IM_ROUNDUP_TO_EVEN(ImCeil(3.141592653589793 --[[IM_PI]] / ImAcos(1 - ImMin(_MAXERROR, _RAD) / _RAD))), IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX) end
local function IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(_N, _MAXERROR) return (_MAXERROR / (1 - ImCos(3.141592653589793 --[[IM_PI]] / ImMax(_N, 3.141592653589793 --[[IM_PI]])))) end
local function IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_ERROR(_N, _RAD) return ((1 - ImCos(3.141592653589793 --[[IM_PI]] / ImMax(_N, 3.141592653589793 --[[IM_PI]]))) / _RAD) end
-- Calculates arc step and step count for 90 degree arc. The calculated arc step will complete a 90 degree arc when stepped arc_step_count times.
-- [Luau] returns arc_step, arc_step_count (C++ out-references)
function CalcCornerArcStepAndCount(segment_count)
local quarter = 48 --[[IM_DRAWLIST_ARCFAST_TABLE_SIZE]] / 4
local arc_step = ImClamp(math.floor(48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] / segment_count), 1, quarter)
local arc_step_count = math.floor(quarter / arc_step)
if (arc_step * arc_step_count) ~= quarter then -- Make sure that the arc step can iterate 90 degree arc.
arc_step = math.floor(quarter / arc_step_count)
end
return arc_step, arc_step_count
end
local function ApproxApothem(num_segments)
-- Calculates approximation (within 0.001%) distance from the center of the circumscribed circle to the edge of the polygon, cos(pi / n)
IM_ASSERT_PARANOID(num_segments >= 3)
local u = 1.0 / num_segments
return ((4.058712 * u * u) - 4.934802) * u * u + 1.0
end
-------------------------------------------------------------------------------
-- [SECTION] ImDrawList
-------------------------------------------------------------------------------
function MT.ImDrawListSharedData:SetCircleTessellationMaxError(max_error)
if self.CircleTessellationMaxError == max_error then
return
end
assert(max_error > 0)
self.CircleTessellationMaxError = max_error
for i = 1, 64 do -- IM_COUNTOF(CircleSegmentCounts)
local radius = (i - 1)
self.CircleSegmentCounts[i] = (i > 1) and IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, self.CircleTessellationMaxError) or 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]]
end
self.ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]], self.CircleTessellationMaxError)
end
--- @param data ImDrawListSharedData
function MT.ImDrawList:_SetDrawListSharedData(data)
if self._Data ~= nil then
self._Data.DrawLists:find_erase_unsorted(self)
end
self._Data = data
if self._Data ~= nil then
self._Data.DrawLists:push_back(self)
end
end
-- Initialize before use in a new frame. We always have a command ready in the buffer.
-- In the majority of cases, you would want to call PushClipRect() and PushTexture() after this.
-- TODO:
function MT.ImDrawList:_ResetForNewFrame()
self._ContentClipX0 = nil -- set again by Begin() (Roblox backend scroll reuse)
if self._Splitter._Count > 1 then
self._Splitter:Merge(self)
end
self._Splitter._Current = 0 -- [Lua] recover from an error thrown mid-split
self._Splitter._Count = 1
self.CmdBuffer:resize(0)
self.IdxBuffer:resize(0)
self.VtxBuffer:resize(0)
self.Flags = self._Data.InitialDrawFlags
assert(bit32.band(self.Flags, bit32.bnot(16675312 --[[ImDrawFlags.AllowInFrameScope_]])) == 0)
self._CmdHeader = ImDrawCmdHeader() -- memset(&_CmdHeader, 0, sizeof(_CmdHeader))
self._VtxCurrentIdx = 1
self._VtxWritePtr = 1
self._IdxWritePtr = 1
self._ClipRectStack:resize(0)
self._TextureStack:resize(0)
self._DrawFlagsStack.Size = 0 -- _DrawFlagsStack.clear() (ImSmallStack_)
self._Path:resize(0)
self.CmdBuffer:push_back(ImDrawCmd())
-- Caller needs to bet _SetPixelDensity() as well.
end
function MT.ImDrawList:_ClearFreeMemory()
self.CmdBuffer:clear()
self.IdxBuffer:clear()
self.VtxBuffer:clear()
self.Flags = 0 --[[ImDrawFlags.None]]
self._VtxCurrentIdx = 1
self._VtxWritePtr = 1
self._IdxWritePtr = 1
self._ClipRectStack:clear()
self._TextureStack:clear()
-- self._CallbacksDataBuf:clear()
self._DrawFlagsStack.Size = 0 -- _DrawFlagsStack.clear() (ImSmallStack_)
self._Path:clear()
self._Splitter:ClearFreeMemory()
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 * 36 --[[VTX_STRIDE]])
dst.Flags = self.Flags
return dst
end
function MT.ImDrawList:AddDrawCmd()
local draw_cmd = ImDrawCmd()
ImVec4_Copy(draw_cmd.ClipRect, self._CmdHeader.ClipRect) -- Same as calling ImDrawCmd_HeaderCopy()
draw_cmd.TexRef = self._CmdHeader.TexRef
draw_cmd.VtxOffset = self._CmdHeader.VtxOffset
draw_cmd.IdxOffset = self.IdxBuffer.Size
assert(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z and draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w)
-- [Luau] inline self.CmdBuffer:push_back(draw_cmd)
local cmd_buffer = self.CmdBuffer
local n = cmd_buffer.Size + 1
if n > cmd_buffer.Capacity then cmd_buffer:push_back(draw_cmd) else cmd_buffer.Data[n] = draw_cmd; cmd_buffer.Size = n end
end
-- Pop trailing draw command (used before merging or presenting to user)
-- Note that this leaves the ImDrawList in a state unfit for further commands, as most code assume that CmdBuffer.Size > 0 && CmdBuffer.back().UserCallback == nil
function MT.ImDrawList:_PopUnusedDrawCmd()
while self.CmdBuffer.Size > 0 do
local curr_cmd = self.CmdBuffer.Data[self.CmdBuffer.Size]
if curr_cmd.ElemCount ~= 0 or curr_cmd.UserCallback ~= nil then
break -- return;// break;
end
self.CmdBuffer:pop_back()
end
end
--- @param callback ImDrawCallback
--- @param userdata any
--- @param userdata_size? size_t
function MT.ImDrawList:AddCallback(callback, userdata, userdata_size)
if userdata_size == nil then userdata_size = 0 end
assert(callback ~= nil)
IM_ASSERT_PARANOID(self.CmdBuffer.Size > 0)
local curr_cmd = self.CmdBuffer.Data[self.CmdBuffer.Size]
assert(curr_cmd.UserCallback == nil)
if curr_cmd.ElemCount ~= 0 then
self:AddDrawCmd()
curr_cmd = self.CmdBuffer.Data[self.CmdBuffer.Size]
end
curr_cmd.UserCallback = callback
-- TODO: _CallbacksDataBuf?
if userdata_size == 0 then
-- Store user data directly in command (no indirection)
curr_cmd.UserCallbackData = userdata
else
assert(userdata ~= nil)
assert(userdata_size < bit32.lshift(1, 31))
end
self:AddDrawCmd() -- Force a new command after us (see comment below)
end
--- Compare ClipRect, TexRef, VtxOffset
--- @param CMD_LHS ImDrawCmd|ImDrawCmdHeader
--- @param CMD_RHS ImDrawCmd|ImDrawCmdHeader
--- @return bool
local function ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS)
local lcr, rcr = CMD_LHS.ClipRect, CMD_RHS.ClipRect -- [Luau] cache
if lcr.x ~= rcr.x or
lcr.y ~= rcr.y or
lcr.z ~= rcr.z or
lcr.w ~= rcr.w then
return false
end
local ltex, rtex = CMD_LHS.TexRef, CMD_RHS.TexRef
if ltex ~= rtex and (ltex._TexData ~= rtex._TexData or
ltex._TexID ~= rtex._TexID) then
return false
end
if CMD_LHS.VtxOffset ~= CMD_RHS.VtxOffset then
return false
end
return true
end
--- Copy ClipRect, TexRef, VtxOffset
--- @param CMD_DST ImDrawCmd|ImDrawCmdHeader
--- @param CMD_SRC ImDrawCmd|ImDrawCmdHeader
local function ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC)
CMD_DST.ClipRect.x = CMD_SRC.ClipRect.x
CMD_DST.ClipRect.y = CMD_SRC.ClipRect.y
CMD_DST.ClipRect.z = CMD_SRC.ClipRect.z
CMD_DST.ClipRect.w = CMD_SRC.ClipRect.w
CMD_DST.TexRef._TexData = CMD_SRC.TexRef._TexData
CMD_DST.TexRef._TexID = CMD_SRC.TexRef._TexID
CMD_DST.VtxOffset = CMD_SRC.VtxOffset
end
--- @param CMD_0 ImDrawCmd
--- @param CMD_1 ImDrawCmd
local function ImDrawCmd_AreSequentialIdxOffset(CMD_0, CMD_1)
return CMD_0.IdxOffset + CMD_0.ElemCount == CMD_1.IdxOffset
end
-- Try to merge two last draw commands
function MT.ImDrawList:_TryMergeDrawCmds()
IM_ASSERT_PARANOID(self.CmdBuffer.Size > 0)
local curr_cmd = self.CmdBuffer.Data[self.CmdBuffer.Size]
local prev_cmd = self.CmdBuffer.Data[self.CmdBuffer.Size - 1]
if ImDrawCmd_HeaderCompare(curr_cmd, prev_cmd) and ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) and curr_cmd.UserCallback == nil and prev_cmd.UserCallback == nil then
prev_cmd.ElemCount = prev_cmd.ElemCount + curr_cmd.ElemCount
self.CmdBuffer:pop_back()
end
end
-- Our scheme may appears a bit unusual, basically we want the most-common calls AddLine AddRect etc. to not have to perform any check so we always have a command ready in the stack.
-- The cost of figuring out if a new command has to be added or if we can merge is paid in those Update** functions only.
function MT.ImDrawList:_OnChangedClipRect()
-- If current command is used with different settings we need to add a new command
IM_ASSERT_PARANOID(self.CmdBuffer.Size > 0)
local curr_cmd = self.CmdBuffer.Data[self.CmdBuffer.Size]
if curr_cmd.ElemCount ~= 0 then
-- [Luau] inline IM_VEC4.__eq
local hcr, ccr = self._CmdHeader.ClipRect, curr_cmd.ClipRect
if hcr.x ~= ccr.x or hcr.y ~= ccr.y or hcr.z ~= ccr.z or hcr.w ~= ccr.w then
self:AddDrawCmd()
return
end
end
assert(curr_cmd.UserCallback == nil)
-- Try to merge with previous command if it matches, else use current command
local prev_cmd = self.CmdBuffer.Data[self.CmdBuffer.Size - 1]
if (curr_cmd.ElemCount == 0 and self.CmdBuffer.Size > 1 and ImDrawCmd_HeaderCompare(self._CmdHeader, prev_cmd) and ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) and prev_cmd.UserCallback == nil) then
self.CmdBuffer:pop_back()
return
end
ImVec4_Copy(curr_cmd.ClipRect, self._CmdHeader.ClipRect)
end
function MT.ImDrawList:_OnChangedTexture()
-- If current command is used with different settings we need to add a new command
IM_ASSERT_PARANOID(self.CmdBuffer.Size > 0)
local curr_cmd = self.CmdBuffer.Data[self.CmdBuffer.Size]
if curr_cmd.ElemCount ~= 0 and curr_cmd.TexRef ~= self._CmdHeader.TexRef then
self:AddDrawCmd()
return
end
-- Unlike other _OnChangedXXX functions this may be called by ImFontAtlasUpdateDrawListsTextures() in more locations so we need to handle this case.
if curr_cmd.UserCallback ~= nil then
return
end
-- Try to merge with previous command if it matches, else use current command
local prev_cmd = self.CmdBuffer.Data[self.CmdBuffer.Size - 1]
if curr_cmd.ElemCount == 0 and self.CmdBuffer.Size > 1 and ImDrawCmd_HeaderCompare(self._CmdHeader, prev_cmd) and ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) and prev_cmd.UserCallback == nil then
self.CmdBuffer:pop_back()
return
end
curr_cmd.TexRef = self._CmdHeader.TexRef
end
function MT.ImDrawList:_OnChangedVtxOffset()
-- We don't need to compare curr_cmd.VtxOffset ~= _CmdHeader.VtxOffset because we know it'll be different at the time we call this.
self._VtxCurrentIdx = 1
IM_ASSERT_PARANOID(self.CmdBuffer.Size > 0)
local curr_cmd = self.CmdBuffer.Data[self.CmdBuffer.Size]
--IM_ASSERT(curr_cmd.VtxOffset ~= self._CmdHeader.VtxOffset) -- See #3349
if curr_cmd.ElemCount ~= 0 then
self:AddDrawCmd()
return
end
assert(curr_cmd.UserCallback == nil)
curr_cmd.VtxOffset = self._CmdHeader.VtxOffset
end
-------------------------------------------------------------------------------
-- [SECTION] ImDrawListSplitter
-------------------------------------------------------------------------------
-- FIXME: This may be a little confusing, trying to be a little too low-level/optimal instead of just doing vector swap..
-------------------------------------------------------------------------------
-- Lua: channels swap ImVector objects with the draw list (C++ memcpy of the vector headers).
-- Channel 0 holds nothing while it is the current channel (its buffers live in the draw list).
-- Copy ClipRect, TexRef, VtxOffset (TexRef is assigned by reference, never mutated)
local function ImDrawCmd_HeaderCopyRef(dst, src)
ImVec4_Copy(dst.ClipRect, src.ClipRect)
dst.TexRef = src.TexRef
dst.VtxOffset = src.VtxOffset
end
function MT.ImDrawListSplitter:Clear()
self._Current = 0
self._Count = 1
end
function MT.ImDrawListSplitter:ClearFreeMemory()
for i = 1, self._Channels.Size do
local ch = self._Channels.Data[i]
if i - 1 == self._Current then
-- Current channel is a copy of CmdBuffer/IdxBuffer, don't destruct again
self._Channels.Data[i] = ImDrawChannel()
else
ch._CmdBuffer:clear()
ch._IdxBuffer:clear()
end
end
self._Current = 0
self._Count = 1
self._Channels:clear()
end
--- @param draw_list ImDrawList
--- @param channels_count int
function MT.ImDrawListSplitter:Split(draw_list, channels_count)
assert(self._Current == 0 and self._Count <= 1, "Nested channel splitting is not supported. Please use separate instances of ImDrawListSplitter.")
local old_channels_count = self._Channels.Size
if old_channels_count < channels_count then
self._Channels:reserve(channels_count) -- Avoid over reserving since this is likely to stay stable
self._Channels:resize(channels_count)
end
self._Count = channels_count
-- Channels[] hold storage that we'll swap with draw_list._CmdBuffer/_IdxBuffer
-- The content of Channels[0] at this point doesn't matter. We clear it to make state tidy in a debugger but we don't strictly need to.
-- When we switch to the next channel, we'll copy draw_list._CmdBuffer/_IdxBuffer into Channels[0] and then Channels[1] into draw_list.CmdBuffer/_IdxBuffer
-- Channel 0 is the draw list itself (memset to 0 in C++)
local channels = self._Channels.Data
channels[1] = { _CmdBuffer = nil, _IdxBuffer = nil }
for i = 2, channels_count do
if i > old_channels_count or channels[i] == nil then
channels[i] = ImDrawChannel()
else
channels[i]._CmdBuffer.Size = 0 -- [Luau] inline resize(0)
channels[i]._IdxBuffer.Size = 0
end
end
end
--- @param draw_list ImDrawList
function MT.ImDrawListSplitter:Merge(draw_list)
-- Note that we never use or rely on _Channels.Size because it is merely a buffer that we never shrink back to 0 to keep all sub-buffers ready for use.
if self._Count <= 1 then
return
end
self:SetCurrentChannel(draw_list, 0)
draw_list:_PopUnusedDrawCmd()
-- Calculate our final buffer sizes. Also fix the incorrect IdxOffset values in each command.
local new_cmd_buffer_count = 0
local new_idx_buffer_count = 0
local last_cmd = (self._Count > 0 and draw_list.CmdBuffer.Size > 0) and draw_list.CmdBuffer.Data[draw_list.CmdBuffer.Size] or nil
local idx_offset = last_cmd and (last_cmd.IdxOffset + last_cmd.ElemCount) or 0
local channels = self._Channels.Data
for i = 2, self._Count do
local ch = channels[i]
local cmds = ch._CmdBuffer
if cmds.Size > 0 and cmds.Data[cmds.Size].ElemCount == 0 and cmds.Data[cmds.Size].UserCallback == nil then -- Equivalent of PopUnusedDrawCmd()
cmds:pop_back()
end
if cmds.Size > 0 and last_cmd ~= nil then
-- Do not include ImDrawCmd_AreSequentialIdxOffset() in the compare as we rebuild IdxOffset values ourselves.
-- Manipulating IdxOffset (e.g. by reordering draw commands like done by RenderDimmedBackgroundBehindWindow()) is not supported within a splitter.
local next_cmd = cmds.Data[1]
if ImDrawCmd_HeaderCompare(last_cmd, next_cmd) and last_cmd.UserCallback == nil and next_cmd.UserCallback == nil then
-- Merge previous channel last draw command with current channel first draw command if matching.
last_cmd.ElemCount = last_cmd.ElemCount + next_cmd.ElemCount
idx_offset = idx_offset + next_cmd.ElemCount
cmds:erase(1) -- FIXME-OPT: Improve for multiple merges.
end
end
if cmds.Size > 0 then
last_cmd = cmds.Data[cmds.Size]
end
new_cmd_buffer_count = new_cmd_buffer_count + cmds.Size
new_idx_buffer_count = new_idx_buffer_count + ch._IdxBuffer.Size
for cmd_n = 1, cmds.Size do
cmds.Data[cmd_n].IdxOffset = idx_offset
idx_offset = idx_offset + cmds.Data[cmd_n].ElemCount
end
end
local cmd_write = draw_list.CmdBuffer.Size + 1
local idx_write = draw_list.IdxBuffer.Size + 1
draw_list.CmdBuffer:resize(draw_list.CmdBuffer.Size + new_cmd_buffer_count)
draw_list.IdxBuffer:resize(draw_list.IdxBuffer.Size + new_idx_buffer_count)
-- Write commands and indices in order (they are fairly small structures, we don't copy vertices only indices)
local dst_cmds = draw_list.CmdBuffer.Data
local dst_idx = draw_list.IdxBuffer.Data
for i = 2, self._Count do
local ch = channels[i]
-- [Luau] skip empty buffers: their .Data may be nil (reading it goes through IM_VECTOR.__index)
if ch._CmdBuffer.Size > 0 then
local src_cmds = ch._CmdBuffer.Data
for n = 1, ch._CmdBuffer.Size do
dst_cmds[cmd_write] = src_cmds[n]
src_cmds[n] = nil -- ownership moved to the draw list
cmd_write = cmd_write + 1
end
end
if ch._IdxBuffer.Size > 0 then
local src_idx = ch._IdxBuffer.Data
for n = 1, ch._IdxBuffer.Size do
dst_idx[idx_write] = src_idx[n]
idx_write = idx_write + 1
end
end
end
draw_list._IdxWritePtr = idx_write
-- Ensure there's always a non-callback draw command trailing the command-buffer
if draw_list.CmdBuffer.Size == 0 or draw_list.CmdBuffer.Data[draw_list.CmdBuffer.Size].UserCallback ~= nil then
draw_list:AddDrawCmd()
end
-- If current command is used with different settings we need to add a new command
local curr_cmd = draw_list.CmdBuffer.Data[draw_list.CmdBuffer.Size]
if curr_cmd.ElemCount == 0 then
ImDrawCmd_HeaderCopyRef(curr_cmd, draw_list._CmdHeader) -- Copy ClipRect, TexRef, VtxOffset
elseif not ImDrawCmd_HeaderCompare(curr_cmd, draw_list._CmdHeader) then
draw_list:AddDrawCmd()
end
self._Count = 1
end
--- @param draw_list ImDrawList
--- @param idx int # 0-based channel index
@native
function MT.ImDrawListSplitter:SetCurrentChannel(draw_list, idx)
assert(idx >= 0 and idx < self._Count)
if self._Current == idx then
return
end
-- Overwrite ImVector (12/16 bytes), four times. This is merely a silly optimization instead of doing .swap()
local channels = self._Channels.Data
local cur = channels[self._Current + 1]
cur._CmdBuffer = draw_list.CmdBuffer
cur._IdxBuffer = draw_list.IdxBuffer
self._Current = idx
local ch = channels[idx + 1]
local cmd_buffer = ch._CmdBuffer
local idx_buffer = ch._IdxBuffer
draw_list.CmdBuffer = cmd_buffer
draw_list.IdxBuffer = idx_buffer
draw_list._IdxWritePtr = idx_buffer.Size + 1
-- If current command is used with different settings we need to add a new command
-- [Luau] ImDrawCmd_HeaderCopyRef() / ImDrawCmd_HeaderCompare() inlined
local cmd_count = cmd_buffer.Size
if cmd_count == 0 then
draw_list:AddDrawCmd()
return
end
local curr_cmd = cmd_buffer.Data[cmd_count]
local header = draw_list._CmdHeader
local hcr, ccr = header.ClipRect, curr_cmd.ClipRect
if curr_cmd.ElemCount == 0 then
ccr.x = hcr.x; ccr.y = hcr.y; ccr.z = hcr.z; ccr.w = hcr.w -- Copy ClipRect, TexRef, VtxOffset
curr_cmd.TexRef = header.TexRef
curr_cmd.VtxOffset = header.VtxOffset
elseif ccr.x ~= hcr.x or ccr.y ~= hcr.y or ccr.z ~= hcr.z or ccr.w ~= hcr.w
or curr_cmd.TexRef._TexData ~= header.TexRef._TexData or curr_cmd.TexRef._TexID ~= header.TexRef._TexID
or curr_cmd.VtxOffset ~= header.VtxOffset then
draw_list:AddDrawCmd()
end
end
--- @param count int
function MT.ImDrawList:ChannelsSplit(count) self._Splitter:Split(self, count) end
function MT.ImDrawList:ChannelsMerge() self._Splitter:Merge(self) end
--- @param n int # 0-based
function MT.ImDrawList:ChannelsSetCurrent(n) self._Splitter:SetCurrentChannel(self, n) end
function MT.ImDrawList:PushTexture(tex_ref)
self._TextureStack:push_back(tex_ref)
self._CmdHeader.TexRef = tex_ref
if (tex_ref._TexData ~= nil) then
assert(tex_ref._TexData.WantDestroyNextFrame == false)
end
self:_OnChangedTexture()
end
function MT.ImDrawList:PopTexture()
self._TextureStack:pop_back()
self._CmdHeader.TexRef = (self._TextureStack.Size == 0) and ImTextureRef() or self._TextureStack.Data[self._TextureStack.Size]
self:_OnChangedTexture()
end
-- IMPORTANT: currently limited to 3 deep
--- @param flags ImDrawFlags
--- @param enabled bool
function MT.ImDrawList:PushDrawFlag(flags, enabled)
assert(bit32.band(flags, bit32.bnot(8286704 --[[ImDrawFlags.AllowInPushScope_]])) == 0)
assert(bit32.band(flags, 917504 --[[ImDrawFlags.StrokeMask_]]) == 0 or bit32.band(flags, 917504 --[[ImDrawFlags.StrokeMask_]]) == 917504 --[[ImDrawFlags.StrokeLegacy]])
IM_ASSERT_USER_ERROR_RET(self._DrawFlagsStack.Size < 3, "PushDrawFlag() has a depth limit. Contact us.") -- _DrawFlagsStack.capacity() == 3
self._DrawFlagsStack:push_back(self.Flags)
if enabled then
self.Flags = bit32.bor(self.Flags, flags)
else
self.Flags = bit32.band(self.Flags, bit32.bnot(flags))
end
end
function MT.ImDrawList:PopDrawFlag()
IM_ASSERT_USER_ERROR_RET(self._DrawFlagsStack.Size > 0, "Calling PopDrawFlag() too many times!")
self.Flags = self._DrawFlagsStack:back()
self._DrawFlagsStack:pop_back()
end
-- This is used by ImGui.PushFont()/PopFont(). It works because we never use _TextureIdStack[] elsewhere than in PushTexture()/PopTexture().
function MT.ImDrawList:_SetTexture(tex_ref)
if (self._CmdHeader.TexRef == tex_ref) then
return
end
self._CmdHeader.TexRef = tex_ref
self._TextureStack.Data[self._TextureStack.Size] = tex_ref
self:_OnChangedTexture()
end
--- @param pixel_density float
function MT.ImDrawList:_SetPixelDensity(pixel_density)
assert(pixel_density > 0.0)
self._FringeScale = 1.0 / pixel_density
self._InvFringeScale = pixel_density
self._FringeScaleIsInteger = ImIsTruncated(self._InvFringeScale)
end
-- Reserve space for a number of vertices and indices.
-- You must finish filling your reserved data before calling PrimReserve() again, as it may reallocate or
-- submit the intermediate results. PrimUnreserve() can be used to release unused allocations.
-- [Luau] local function (also MT.ImDrawList.PrimReserve): hot callers in this file skip the method lookup
@native
local function PrimReserve(self, idx_count, vtx_count)
-- Large mesh support (when enabled)
IM_ASSERT_PARANOID(idx_count >= 0 and vtx_count >= 0)
if (self._VtxCurrentIdx + vtx_count >= bit32.lshift(1, 16)) and (bit32.band(self.Flags, 4194304 --[[ImDrawFlags.UseVtxOffset]]) ~= 0) then
-- FIXME: In theory we should be testing that vtx_count <64k here.
-- In practice, RenderText() relies on reserving ahead for a worst case scenario so it is currently useful for us
-- to not make that check until we rework the text functions to handle clipping and large horizontal lines better.
self._CmdHeader.VtxOffset = self.VtxBuffer.Size + 1
self:_OnChangedVtxOffset()
end
local draw_cmd = self.CmdBuffer.Data[self.CmdBuffer.Size]
draw_cmd.ElemCount = draw_cmd.ElemCount + idx_count
-- [Luau] inline resize() when no growth is needed (IdxBuffer:resize goes through IM_VECTOR.__index)
local vtx_buffer = self.VtxBuffer
local vtx_buffer_old_size = vtx_buffer.Size
local vtx_new_size = vtx_buffer_old_size + vtx_count
if vtx_new_size > vtx_buffer.Capacity then vtx_buffer:resize(vtx_new_size) else vtx_buffer.Size = vtx_new_size end
self._VtxWritePtr = vtx_buffer_old_size + 1
local idx_buffer = self.IdxBuffer
local idx_buffer_old_size = idx_buffer.Size
local idx_new_size = idx_buffer_old_size + idx_count
if idx_new_size > idx_buffer.Capacity then idx_buffer:resize(idx_new_size) else idx_buffer.Size = idx_new_size end
self._IdxWritePtr = idx_buffer_old_size + 1
end
MT.ImDrawList.PrimReserve = PrimReserve
-- Release the number of reserved vertices/indices from the end of the last reservation made with PrimReserve().
function MT.ImDrawList:PrimUnreserve(idx_count, vtx_count)
IM_ASSERT_PARANOID(idx_count >= 0 and vtx_count >= 0)
local draw_cmd = self.CmdBuffer.Data[self.CmdBuffer.Size]
draw_cmd.ElemCount = draw_cmd.ElemCount - idx_count
self.VtxBuffer:shrink(self.VtxBuffer.Size - vtx_count)
self.IdxBuffer:shrink(self.IdxBuffer.Size - idx_count)
self._VtxWritePtr = self.VtxBuffer.Size + 1
self._IdxWritePtr = self.IdxBuffer.Size + 1
end
-- Fully unrolled with inline call to keep our debug builds decently fast.
--- @param a ImVec2
--- @param c ImVec2
--- @param col ImU32
-- [Luau] scalar PrimRect (a = ax,ay; c = cx,cy): no ImVec2 needed by callers, vertex writes inlined
@native
local function PrimRectXY(self, ax, ay, cx, cy, col)
local uv = self._Data.TexUvWhitePixel
local idx = self._VtxCurrentIdx
local idx_data = self.IdxBuffer.Data; local b = 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
local u, v = uv.x, uv.y
local o = (vtx_write_ptr - 1) * 36
writef64(b, o, ax); writef64(b, o + 8, ay); writef64(b, o + 16, u); writef64(b, o + 24, v); writeu32(b, o + 32, col)
writef64(b, o + 36, cx); writef64(b, o + 44, ay); writef64(b, o + 52, u); writef64(b, o + 60, v); writeu32(b, o + 68, col)
writef64(b, o + 72, cx); writef64(b, o + 80, cy); writef64(b, o + 88, u); writef64(b, o + 96, v); writeu32(b, o + 104, col)
writef64(b, o + 108, ax); writef64(b, o + 116, cy); writef64(b, o + 124, u); writef64(b, o + 132, v); writeu32(b, o + 140, col)
self._VtxWritePtr = vtx_write_ptr + 4
self._VtxCurrentIdx = idx + 4
self._IdxWritePtr = idx_write_ptr + 6
end
function MT.ImDrawList:PrimRect(a, c, col)
PrimRectXY(self, a.x, a.y, c.x, c.y, col)
end
--- @param a ImVec2
--- @param c ImVec2
--- @param uv_a ImVec2
--- @param uv_c ImVec2
--- @param col any
function MT.ImDrawList:PrimRectUV(a, c, uv_a, uv_c, 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, c.x, a.y, uv_c.x, uv_a.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, a.x, c.y, uv_a.x, uv_c.y, col)
self._VtxWritePtr = vtx_write_ptr + 4
self._VtxCurrentIdx = idx + 4
self._IdxWritePtr = idx_write_ptr + 6
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
--- @return float
-- [Luau] local function (also MT.ImDrawList._CalculateCenterBiasedOffset)
local function _CalculateCenterBiasedOffset(self, thickness)
-- Old jumpy one.
-- return IM_TRUNC(thickness * 0.5f / _FringeScale) * _FringeScale;
-- Calculate outside offset for stroke position ImDrawFlags_StrokeCenterBiased
-- so that one side the of the line is always at pixel boundary.
-- On integer thickness both sides of the line are on pixel boundary.
local screen_thickness = thickness * self._InvFringeScale
local s_thickness = math.floor(screen_thickness) -- (int) of a positive value
return (math.floor(s_thickness / 2) + (s_thickness % 2) * (screen_thickness - s_thickness)) * self._FringeScale
end
MT.ImDrawList._CalculateCenterBiasedOffset = _CalculateCenterBiasedOffset
-- Check if R is considered to be worthy rounding. Can be only used within ImDrawList due to use of _FringeScale.
-- #define IM_HAS_ROUNDING(RAD) ((RAD) >= (0.25f * _FringeScale)) -- [Luau] written inline at each use
-- Select texture and adjust fringe size for specified line thickness.
-- [Luau] returns uv0.x, uv0.y, uv1.x, uv1.y, fringe instead of writing to out parameters
--- @param screen_thickness float
--- @param flags ImDrawFlags
local function _SelectLineTexture(self, screen_thickness, flags)
-- We assume that the screen_thickness has been clamped to 1.0 by the caller.
IM_ASSERT_PARANOID(screen_thickness >= 1.0)
if bit32.band(flags, 8192 --[[ImDrawFlags.AALines]]) ~= 0 then
-- Handle the thickness between 1.0 - IM_DRAWLIST_TEX_LINES_DETAILED_WIDTH].
-- We use super sampled textures in this range to make texture changes less noticeable.
-- There are IM_DRAWLIST_TEX_LINES_DETAILED_WIDTH_COUNT textures, where 0 maps to 1.0 and IM_DRAWLIST_TEX_LINES_DETAILED_WIDTH_COUNT-1 maps to IM_DRAWLIST_TEX_LINES_DETAILED_WIDTH (4.0).
local texture_idx
if screen_thickness <= 4 --[[IM_DRAWLIST_TEX_LINES_DETAILED_WIDTH]] then
texture_idx = math.floor((screen_thickness - 1.0) * 4 --[[IM_DRAWLIST_TEX_LINES_SAMPLE_COUNT]] + 0.5) + (IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1)
elseif screen_thickness < IM_DRAWLIST_TEX_LINES_WIDTH_MAX - 1.5 then
texture_idx = math.floor(screen_thickness + 0.5)
else
texture_idx = IM_DRAWLIST_TEX_LINES_WIDTH_MAX - 1 -- Catches large values of screen_thickness (and +inf/NaN) that a ImMin() wouldn't.
end
-- Scale the fringe so that the texture matches the line thickness.
local tex_uvs = self._Data.TexUvLines[texture_idx] -- 0-based table
return tex_uvs.x, tex_uvs.z, tex_uvs.y, tex_uvs.z, self._FringeScale * screen_thickness * tex_uvs.w -- tex_uvs.w = 1 / thickness
else
-- For no anti-alias we use fully opaque texture, and no fringe expansion.
local uv = self._Data.TexUvWhitePixel
return uv.x, uv.y, uv.x, uv.y, 0.0
end
end
MT.ImDrawList._SelectLineTexture = _SelectLineTexture
-- [Luau] scratch arrays (normals x/y and squared segment lengths) used instead of _Data->TempBuffer: no ImVec2 per point
local poly_normals_x, poly_normals_y, poly_sqr_lengths = {}, {}, {}
--- @param points ImVec2[] # 1-based
--- @param points_count int
--- @param col ImU32
--- @param thickness float
--- @param flags ImDrawFlags
--- @param max_inner_offset float
-- [Luau] IM_APPEND_VTX()/IM_APPEND_TRI() are expanded inline: vertex/index write pointers and _VtxCurrentIdx kept in locals, stored back at the end
@native
function MT.ImDrawList:_AddPolyline(points, points_count, col, thickness, flags, max_inner_offset)
if points_count < 2 or bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
local screen_thickness = thickness * self._InvFringeScale
if screen_thickness < 1.0 then
if screen_thickness < 1.0 / 255.0 then
return
end
col = ImAlphaMultiply(col, screen_thickness)
screen_thickness = 1.0
thickness = self._FringeScale
end
-- If this assert triggers on legacy code:
-- - 1.92.8 (2025/05): swapped two last parameters order: flags, thickness --> thickness, flags. This should normally be caught by compile-time type-checking.
-- - 1.92.8 (2025/05): changed value of ImDrawList_Closed which was previously guaranteed to be == 1. Hardcoded use of 1 or true should be replaced.
-- Read more details near AddRect() + see "API BREAKING CHANGES" section for 1.82, 1.90 and 1.92.8.
flags = bit32.bor(flags, self.Flags)
IM_ASSERT_USER_ERROR_RET(bit32.band(flags, 2147483663 --[[ImDrawFlags.InvalidMask_]]) == 0, "Incorrect parameter. Did you swap 'thickness' and 'flags'?")
-- Allocate data for temp buffers
local normals_x, normals_y, sqr_lengths = poly_normals_x, poly_normals_y, poly_sqr_lengths
-- Calculate normals for each line segment
for i = 1, points_count - 1 do
local p0 = points[i]
local p1 = points[i + 1]
local dx = p1.x - p0.x
local dy = p1.y - p0.y
local d2 = dx * dx + dy * dy
local inv_len = 0.0
if d2 > 0.0 then inv_len = 1.0 / math.sqrt(d2) end -- ImRsqrtPrecise()
normals_x[i] = -dy * inv_len
normals_y[i] = dx * inv_len
sqr_lengths[i] = d2
end
if bit32.band(flags, 512 --[[ImDrawFlags.Closed]]) ~= 0 then
local p0 = points[points_count]
local p1 = points[1]
local dx = p1.x - p0.x
local dy = p1.y - p0.y
local d2 = dx * dx + dy * dy
local inv_len = 0.0
if d2 > 0.0 then inv_len = 1.0 / math.sqrt(d2) end -- ImRsqrtPrecise()
normals_x[points_count] = -dy * inv_len
normals_y[points_count] = dx * inv_len
sqr_lengths[points_count] = d2
else
normals_x[points_count] = normals_x[points_count - 1]
normals_y[points_count] = normals_y[points_count - 1]
sqr_lengths[points_count] = 0.0
end
local u0, v0, u1, v1, fringe = _SelectLineTexture(self, screen_thickness, flags)
local col_trans = bit32.band(col, bit32.bnot(4278190080 --[[IM_COL32_A_MASK]]))
local stroke_pos = _GetStrokePos(flags, 262144 --[[ImDrawFlags.StrokeCenter]])
if stroke_pos == 917504 --[[ImDrawFlags.StrokeLegacy]] then
flags = bit32.band(bit32.bor(flags, 1024 --[[ImDrawFlags.JoinMiter]]), bit32.bnot(16384 --[[ImDrawFlags.AALineEnds]]))
end
local closed = bit32.band(flags, 512 --[[ImDrawFlags.Closed]]) ~= 0
local miters_only = bit32.band(flags, 1024 --[[ImDrawFlags.JoinMiter]]) ~= 0
local miter_distance_limit_sqr = IM_POLYLINE_MITER_LIMIT * IM_POLYLINE_MITER_LIMIT
local thickness0 = (thickness + fringe) * 0.5 -- Center (or legacy)
if stroke_pos == 524288 --[[ImDrawFlags.StrokeOutside]] then
thickness0 = thickness + fringe * 0.5
elseif stroke_pos == 393216 --[[ImDrawFlags.StrokeCenterBiased]] then
thickness0 = _CalculateCenterBiasedOffset(self, thickness) + fringe * 0.5
elseif stroke_pos == 131072 --[[ImDrawFlags.StrokeInside]] then
thickness0 = fringe * 0.5
end
local thickness1 = (thickness + fringe) - thickness0
local idx_count = 0
local vtx_count = 0
local max_verts_per_point = miters_only and 2 or 4
local max_tris_per_point = miters_only and 2 or 5
if closed then
vtx_count = --[[body]] points_count * max_verts_per_point + --[[closing]] 2
idx_count = (--[[body]] points_count * max_tris_per_point + --[[closing]] 2) * 3
else
vtx_count = --[[body]] (points_count - 2) * max_verts_per_point + --[[caps]] (4 * 2)
idx_count = (--[[body]] (points_count - 2) * max_tris_per_point + --[[last seg]] 2 + --[[caps]] (2 * 2)) * 3
end
PrimReserve(self, idx_count, vtx_count)
local ratio = thickness0 / (thickness0 + thickness1) -- The points that use uv2 are placed directly on the path (offset = 0), calculate the texture position from stroke offsets.
local u2, v2 = u0 + (u1 - u0) * ratio, v0
-- Clamp inner offset and adjust texture coordinates to match the change.
-- This is used by some of the convex shape rendering to avoid rendering artifacts when thickness is about to fill the whole shape.
if thickness1 > max_inner_offset then
thickness1 = max_inner_offset
local s = (thickness0 + thickness1) / (thickness + fringe)
local du = u1 - u0
u1 = u0 + du * s
end
local half_aa = self._FringeScale * 0.5 -- Used for end caps, does not use "fringe" since end cap AA is not using the texture.
local use_aa_ends = bit32.band(flags, bit32.bor(8192 --[[ImDrawFlags.AALines]], 16384 --[[ImDrawFlags.AALineEnds]])) == bit32.bor(8192 --[[ImDrawFlags.AALines]], 16384 --[[ImDrawFlags.AALineEnds]])
local vb = self.VtxBuffer.Buf
local ib = self.IdxBuffer.Data
local vw = self._VtxWritePtr -- 1-based write position in VtxBuffer
local iw = self._IdxWritePtr -- 1-based write position in IdxBuffer
local vtx_idx = self._VtxCurrentIdx
local first_vtx_offset = vw
local first_vtx_idx = vtx_idx -- [Luau] VtxBuffer position of index 'n' is first_vtx_offset + (n - first_vtx_idx)
local base_idx = vtx_idx
local p1x, p1y
local n1x, n1y
local len_sqr1
local point_idx = 1 -- [Luau] 1-based, iterate point_idx..point_last inclusive
local point_last = points_count
if not closed then
-- Start cap
point_idx = point_idx + 1
point_last = point_last - 1
local p = points[1]
p1x, p1y = p.x, p.y
n1x, n1y = normals_x[1], normals_y[1]
len_sqr1 = sqr_lengths[1]
local dir_x = n1y
local dir_y = -n1x
if bit32.band(flags, 2048 --[[ImDrawFlags.CapSquare]]) ~= 0 then
p1x = p1x - dir_x * thickness * 0.5
p1y = p1y - dir_y * thickness * 0.5
end
if not use_aa_ends then
base_idx = vtx_idx
WV(vb, vw + 0, p1x - n1x * thickness0, p1y - n1y * thickness0, u0, v0, col)
WV(vb, vw + 1, p1x + n1x * thickness1, p1y + n1y * thickness1, u1, v1, col)
vw = vw + 2; vtx_idx = vtx_idx + 2
else
local pa_x = p1x - dir_x * half_aa
local pa_y = p1y - dir_y * half_aa
local pb_x = p1x + dir_x * half_aa
local pb_y = p1y + dir_y * half_aa
base_idx = vtx_idx
WV(vb, vw + 0, pa_x - n1x * thickness0, pa_y - n1y * thickness0, u0, v0, col_trans)
WV(vb, vw + 1, pa_x + n1x * thickness1, pa_y + n1y * thickness1, u1, v1, col_trans)
local next_base_idx = vtx_idx + 2
WV(vb, vw + 2, pb_x - n1x * thickness0, pb_y - n1y * thickness0, u0, v0, col)
WV(vb, vw + 3, pb_x + n1x * thickness1, pb_y + n1y * thickness1, u1, v1, col)
vw = vw + 4; vtx_idx = vtx_idx + 4
-- AA cap
ib[iw + 0] = base_idx + 0; ib[iw + 1] = base_idx + 2; ib[iw + 2] = base_idx + 3
ib[iw + 3] = base_idx + 0; ib[iw + 4] = base_idx + 3; ib[iw + 5] = base_idx + 1
iw = iw + 6
base_idx = next_base_idx
end
else
-- Wrap around segment
local p = points[points_count]
p1x, p1y = p.x, p.y
n1x, n1y = normals_x[points_count], normals_y[points_count]
len_sqr1 = sqr_lengths[points_count]
-- This will be filled later, allocate space.
base_idx = vtx_idx
WV(vb, vw + 0, 0, 0, u0, v0, col)
WV(vb, vw + 1, 0, 0, u1, v1, col)
vw = vw + 2; vtx_idx = vtx_idx + 2
end
if miters_only then
for pi = point_idx, point_last do
local n0x, n0y = n1x, n1y
local p = points[pi]
p1x, p1y = p.x, p.y
n1x, n1y = normals_x[pi], normals_y[pi]
-- theta is the angle between two segments
local cos_theta = n0x * n1x + n0y * n1y
-- miter offset formula is derived here: https://www.angusj.com/clipper2/Docs/Trigonometry.htm
local cos_theta_clamped = (IM_POLYLINE_MITER_ANGLE_LIMIT >= cos_theta) and IM_POLYLINE_MITER_ANGLE_LIMIT or cos_theta -- Avoid div by 0.
local miter_scale_factor = 1.0 / (1.0 + cos_theta_clamped)
if miter_scale_factor > 1000.0 then miter_scale_factor = 1000.0 end
local miter_offset_x = (n0x + n1x) * miter_scale_factor
local miter_offset_y = (n0y + n1y) * miter_scale_factor
-- Miter
local next_base_idx = vtx_idx
WV(vb, vw + 0, p1x - miter_offset_x * thickness0, p1y - miter_offset_y * thickness0, u0, v0, col) -- 2
WV(vb, vw + 1, p1x + miter_offset_x * thickness1, p1y + miter_offset_y * thickness1, u1, v1, col) -- 3
vw = vw + 2; vtx_idx = vtx_idx + 2
-- Connect prev to next
ib[iw + 0] = base_idx + 0; ib[iw + 1] = base_idx + 2; ib[iw + 2] = base_idx + 3
ib[iw + 3] = base_idx + 0; ib[iw + 4] = base_idx + 3; ib[iw + 5] = base_idx + 1
iw = iw + 6
base_idx = next_base_idx
end
else
for pi = point_idx, point_last do
local n0x, n0y = n1x, n1y
local len_sqr0 = len_sqr1
local p = points[pi]
p1x, p1y = p.x, p.y
n1x, n1y = normals_x[pi], normals_y[pi]
len_sqr1 = sqr_lengths[pi]
-- theta is the angle between two segments
local cos_theta = n0x * n1x + n0y * n1y
local sin_theta = n0y * n1x - n0x * n1y
-- miter offset formula is derived here: https://www.angusj.com/clipper2/Docs/Trigonometry.htm
local cos_theta_clamped = (IM_POLYLINE_MITER_ANGLE_LIMIT >= cos_theta) and IM_POLYLINE_MITER_ANGLE_LIMIT or cos_theta -- Avoid div by 0.
local miter_scale_factor = 1.0 / (1.0 + cos_theta_clamped)
if miter_scale_factor > 1000.0 then miter_scale_factor = 1000.0 end
local miter_offset_x = (n0x + n1x) * miter_scale_factor
local miter_offset_y = (n0y + n1y) * miter_scale_factor
local miter_distance_sqr = miter_offset_x * miter_offset_x + miter_offset_y * miter_offset_y
if miter_distance_sqr > miter_distance_limit_sqr then
-- Clipped bevel
-- Limit the inner miter offset to avoid overshooting.
local ref_thickness = (sin_theta < 0.0) and thickness1 or thickness0
local ref_thickness_sqr = ref_thickness * ref_thickness
local limit_sqr = ImMax(ImMax(len_sqr0, len_sqr1), ref_thickness_sqr)
local ref_miter_dist_sqr = miter_distance_sqr * ref_thickness_sqr
if ref_miter_dist_sqr > limit_sqr then
local scale = ImSqrt(limit_sqr / ref_miter_dist_sqr)
miter_offset_x = miter_offset_x * scale
miter_offset_y = miter_offset_y * scale
end
local bn_x, bn_y = IM_NORMALIZE2F_OVER_ZERO(n0x + n1x, n0y + n1y)
local side_offset = ((n0x * bn_x + n0y * bn_y) - 1.0) / (n0y * bn_x - n0x * bn_y)
local sd_x = bn_y * side_offset
local sd_y = -bn_x * side_offset
-- Bevel
local next_base_idx = vtx_idx + 2
if sin_theta < 0.0 then
WV(vb, vw + 0, p1x - (bn_x + sd_x) * thickness0, p1y - (bn_y + sd_y) * thickness0, u0, v0, col) -- 2
WV(vb, vw + 1, p1x, p1y, u2, v2, col) -- 3
WV(vb, vw + 2, p1x - (bn_x - sd_x) * thickness0, p1y - (bn_y - sd_y) * thickness0, u0, v0, col) -- 4
WV(vb, vw + 3, p1x + miter_offset_x * thickness1, p1y + miter_offset_y * thickness1, u1, v1, col) -- 5
-- Connect prev to next
ib[iw + 0] = base_idx + 0; ib[iw + 1] = base_idx + 2; ib[iw + 2] = base_idx + 5
ib[iw + 3] = base_idx + 0; ib[iw + 4] = base_idx + 5; ib[iw + 5] = base_idx + 1
-- Bevel
ib[iw + 6] = base_idx + 3; ib[iw + 7] = base_idx + 5; ib[iw + 8] = base_idx + 2
ib[iw + 9] = base_idx + 3; ib[iw + 10] = base_idx + 2; ib[iw + 11] = base_idx + 4
ib[iw + 12] = base_idx + 3; ib[iw + 13] = base_idx + 4; ib[iw + 14] = base_idx + 5
else
WV(vb, vw + 0, p1x + (bn_x + sd_x) * thickness1, p1y + (bn_y + sd_y) * thickness1, u1, v1, col) -- 2
WV(vb, vw + 1, p1x, p1y, u2, v2, col) -- 3
WV(vb, vw + 2, p1x - miter_offset_x * thickness0, p1y - miter_offset_y * thickness0, u0, v0, col) -- 4
WV(vb, vw + 3, p1x + (bn_x - sd_x) * thickness1, p1y + (bn_y - sd_y) * thickness1, u1, v1, col) -- 5
-- Connect prev to next
ib[iw + 0] = base_idx + 0; ib[iw + 1] = base_idx + 4; ib[iw + 2] = base_idx + 2
ib[iw + 3] = base_idx + 0; ib[iw + 4] = base_idx + 2; ib[iw + 5] = base_idx + 1
-- Bevel
ib[iw + 6] = base_idx + 3; ib[iw + 7] = base_idx + 2; ib[iw + 8] = base_idx + 4
ib[iw + 9] = base_idx + 3; ib[iw + 10] = base_idx + 4; ib[iw + 11] = base_idx + 5
ib[iw + 12] = base_idx + 3; ib[iw + 13] = base_idx + 5; ib[iw + 14] = base_idx + 2
end
vw = vw + 4; vtx_idx = vtx_idx + 4
iw = iw + 15
base_idx = next_base_idx
else
-- Miter
local next_base_idx = vtx_idx
WV(vb, vw + 0, p1x - miter_offset_x * thickness0, p1y - miter_offset_y * thickness0, u0, v0, col) -- 2
WV(vb, vw + 1, p1x + miter_offset_x * thickness1, p1y + miter_offset_y * thickness1, u1, v1, col) -- 3
vw = vw + 2; vtx_idx = vtx_idx + 2
-- Connect prev to next
ib[iw + 0] = base_idx + 0; ib[iw + 1] = base_idx + 2; ib[iw + 2] = base_idx + 3
ib[iw + 3] = base_idx + 0; ib[iw + 4] = base_idx + 3; ib[iw + 5] = base_idx + 1
iw = iw + 6
base_idx = next_base_idx
end
end
end
-- End cap
if not closed then
local p = points[points_count]
p1x, p1y = p.x, p.y
n1x, n1y = normals_x[points_count], normals_y[points_count]
-- End cap
local dir_x = n1y
local dir_y = -n1x
if bit32.band(flags, 2048 --[[ImDrawFlags.CapSquare]]) ~= 0 then
p1x = p1x + dir_x * thickness * 0.5
p1y = p1y + dir_y * thickness * 0.5
end
if not use_aa_ends then
WV(vb, vw + 0, p1x - n1x * thickness0, p1y - n1y * thickness0, u0, v0, col)
WV(vb, vw + 1, p1x + n1x * thickness1, p1y + n1y * thickness1, u1, v1, col)
vw = vw + 2; vtx_idx = vtx_idx + 2
-- Connect
ib[iw + 0] = base_idx + 0; ib[iw + 1] = base_idx + 2; ib[iw + 2] = base_idx + 3
ib[iw + 3] = base_idx + 0; ib[iw + 4] = base_idx + 3; ib[iw + 5] = base_idx + 1
iw = iw + 6
else
local pa_x = p1x - dir_x * half_aa
local pa_y = p1y - dir_y * half_aa
local pb_x = p1x + dir_x * half_aa
local pb_y = p1y + dir_y * half_aa
local next_base_idx = vtx_idx
WV(vb, vw + 0, pa_x - n1x * thickness0, pa_y - n1y * thickness0, u0, v0, col)
WV(vb, vw + 1, pa_x + n1x * thickness1, pa_y + n1y * thickness1, u1, v1, col)
WV(vb, vw + 2, pb_x - n1x * thickness0, pb_y - n1y * thickness0, u0, v0, col_trans)
WV(vb, vw + 3, pb_x + n1x * thickness1, pb_y + n1y * thickness1, u1, v1, col_trans)
vw = vw + 4; vtx_idx = vtx_idx + 4
-- Connect
ib[iw + 0] = base_idx + 0; ib[iw + 1] = base_idx + 2; ib[iw + 2] = base_idx + 3
ib[iw + 3] = base_idx + 0; ib[iw + 4] = base_idx + 3; ib[iw + 5] = base_idx + 1
base_idx = next_base_idx
-- AA cap
ib[iw + 6] = base_idx + 0; ib[iw + 7] = base_idx + 2; ib[iw + 8] = base_idx + 3
ib[iw + 9] = base_idx + 0; ib[iw + 10] = base_idx + 3; ib[iw + 11] = base_idx + 1
iw = iw + 12
end
else
-- Connect the path.
-- [Luau] VtxBuffer.Data[first_vtx_offset + 0..1] = VtxBuffer.Data[_CmdHeader.VtxOffset + base_idx + 0..1] (2 vertices of 36 bytes)
buffer.copy(vb, (first_vtx_offset - 1) * 36, vb, (first_vtx_offset + (base_idx - first_vtx_idx) - 1) * 36, 2 * 36)
end
self._VtxWritePtr = vw
self._IdxWritePtr = iw
self._VtxCurrentIdx = vtx_idx
-- Restore unused memory
local remaining_idx_count = self.IdxBuffer.Size - (iw - 1)
local remaining_vtx_count = self.VtxBuffer.Size - (vw - 1)
IM_ASSERT_PARANOID(remaining_idx_count >= 0 and remaining_vtx_count >= 0)
if remaining_idx_count > 0 or remaining_vtx_count > 0 then -- [Luau] skip the call when nothing is left
self:PrimUnreserve(remaining_idx_count, remaining_vtx_count)
end
end
-- - We intentionally avoid using ImVec2 and its math operators here to reduce cost to a minimum for debug/non-inlined builds.
-- - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing.
--- @param points ImVec2[] # 1-based
--- @param points_count int
--- @param col ImU32
--- @param flags? ImDrawFlags
@native
function MT.ImDrawList:AddConvexPolyFilled(points, points_count, col, flags)
if points_count < 3 or bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
if flags == nil then flags = 0 end
local uv = self._Data.TexUvWhitePixel
local uv_x, uv_y = uv.x, uv.y
flags = bit32.bor(flags, self.Flags)
if bit32.band(flags, 4096 --[[ImDrawFlags.AAFill]]) ~= 0 then
local half_aa = self._FringeScale * 0.5
local col_trans = bit32.band(col, bit32.bnot(4278190080 --[[IM_COL32_A_MASK]]))
local miters_only = bit32.band(flags, 1024 --[[ImDrawFlags.JoinMiter]]) ~= 0
local miter_distance_limit_sqr = IM_POLYLINE_MITER_LIMIT * IM_POLYLINE_MITER_LIMIT
local idx_count = ((points_count - 2) + points_count * 2 + IM_POLYLINE_CONVEX_POLY_MAX_BEVELS) * 3
local vtx_count = points_count * 2 + IM_POLYLINE_CONVEX_POLY_MAX_BEVELS
PrimReserve(self, idx_count, vtx_count)
local vb = self.VtxBuffer.Buf
local ib = self.IdxBuffer.Data
local start_vtx_ptr = self._VtxWritePtr
local start_idx_ptr = self._IdxWritePtr
local iw = start_idx_ptr
-- Compute normals and segment lengths
local temp_normals_x, temp_normals_y, temp_sqr_lengths = poly_normals_x, poly_normals_y, poly_sqr_lengths
local i0 = points_count
for i1 = 1, points_count do
local p0 = points[i0]
local p1 = points[i1]
local dx = p1.x - p0.x
local dy = p1.y - p0.y
local d2 = dx * dx + dy * dy
if d2 > 0.0 then
local inv_len = 1.0 / math.sqrt(d2) -- ImRsqrtPrecise()
dx = dx * inv_len
dy = dy * inv_len
end
temp_normals_x[i0] = dy
temp_normals_y[i0] = -dx
temp_sqr_lengths[i0] = d2
i0 = i1
end
local vtx_inner_idx = self._VtxCurrentIdx
local inner_vtx_ptr = start_vtx_ptr
local vw = start_vtx_ptr + points_count
local vtx_idx = vtx_inner_idx + points_count
local prev_outer_idx = 0 -- This should be the index of the last vertex, but we don't know it yet. Will need to patch once we're done.
if miters_only then
i0 = points_count
for i1 = 1, points_count do
local p1 = points[i1]
local p1x, p1y = p1.x, p1.y
local n0x, n0y = temp_normals_x[i0], temp_normals_y[i0]
local n1x, n1y = temp_normals_x[i1], temp_normals_y[i1]
-- theta is the angle between two segments
local cos_theta = n0x * n1x + n0y * n1y
-- miter offset formula is derived here: https://www.angusj.com/clipper2/Docs/Trigonometry.htm
local cos_theta_clamped = (IM_POLYLINE_MITER_ANGLE_LIMIT >= cos_theta) and IM_POLYLINE_MITER_ANGLE_LIMIT or cos_theta -- Avoid div by 0.
local miter_scale_factor = 1.0 / (1.0 + cos_theta_clamped)
if miter_scale_factor > 1000.0 then miter_scale_factor = 1000.0 end
local miter_offset_x = (n0x + n1x) * miter_scale_factor * half_aa
local miter_offset_y = (n0y + n1y) * miter_scale_factor * half_aa
-- Inner
WV(vb, inner_vtx_ptr, p1x - miter_offset_x, p1y - miter_offset_y, uv_x, uv_y, col)
inner_vtx_ptr = inner_vtx_ptr + 1
local prev_inner_idx = vtx_inner_idx + (i0 - 1)
local inner_idx = vtx_inner_idx + (i1 - 1)
local outer_idx = vtx_idx
-- Outer
WV(vb, vw, p1x + miter_offset_x, p1y + miter_offset_y, uv_x, uv_y, col_trans)
vw = vw + 1; vtx_idx = vtx_idx + 1
-- Connect with previous
ib[iw + 0] = prev_outer_idx; ib[iw + 1] = outer_idx; ib[iw + 2] = inner_idx
ib[iw + 3] = prev_outer_idx; ib[iw + 4] = inner_idx; ib[iw + 5] = prev_inner_idx
iw = iw + 6
prev_outer_idx = outer_idx
i0 = i1
end
else
local bevel_count = 0
i0 = points_count
for i1 = 1, points_count do
local p1 = points[i1]
local p1x, p1y = p1.x, p1.y
local n0x, n0y = temp_normals_x[i0], temp_normals_y[i0]
local n1x, n1y = temp_normals_x[i1], temp_normals_y[i1]
-- theta is the angle between two segments
local cos_theta = n0x * n1x + n0y * n1y
-- miter offset formula is derived here: https://www.angusj.com/clipper2/Docs/Trigonometry.htm
local cos_theta_clamped = (IM_POLYLINE_MITER_ANGLE_LIMIT >= cos_theta) and IM_POLYLINE_MITER_ANGLE_LIMIT or cos_theta -- Avoid div by 0.
local miter_scale_factor = 1.0 / (1.0 + cos_theta_clamped)
if miter_scale_factor > 1000.0 then miter_scale_factor = 1000.0 end
local miter_offset_x = (n0x + n1x) * miter_scale_factor
local miter_offset_y = (n0y + n1y) * miter_scale_factor
local miter_distance_sqr = miter_offset_x * miter_offset_x + miter_offset_y * miter_offset_y
local bevel = miter_distance_sqr > miter_distance_limit_sqr
if bevel then
-- Limit inner bevel so that it is does not shoot out outside the polygon.
-- (using ImMax is intentional: using min limited too much in Mikko's tests when you have short edge next to long one.
-- The idea of that check is to keep that worst case overshoot no bigger than the polygon)
local ref_thickness_sqr = half_aa * half_aa
local limit_sqr = ImMax(temp_sqr_lengths[i0], temp_sqr_lengths[i1])
local ref_miter_dist_sqr = miter_distance_sqr * ref_thickness_sqr
if ref_miter_dist_sqr > limit_sqr then
local scale = ImSqrt(limit_sqr / ref_miter_dist_sqr)
miter_offset_x = miter_offset_x * scale
miter_offset_y = miter_offset_y * scale
end
end
miter_offset_x = miter_offset_x * half_aa
miter_offset_y = miter_offset_y * half_aa
-- Inner
WV(vb, inner_vtx_ptr, p1x - miter_offset_x, p1y - miter_offset_y, uv_x, uv_y, col)
inner_vtx_ptr = inner_vtx_ptr + 1
local prev_inner_idx = vtx_inner_idx + (i0 - 1)
local inner_idx = vtx_inner_idx + (i1 - 1)
local outer_idx = vtx_idx
-- Outer
if bevel and bevel_count < IM_POLYLINE_CONVEX_POLY_MAX_BEVELS then
-- Because the polygon is convex, we know the maximum number of bevel corners we can hit (which is very small number).
-- We keep track of them just in case the calculations disagree.
bevel_count = bevel_count + 1
WV(vb, vw + 0, p1x + n0x * half_aa, p1y + n0y * half_aa, uv_x, uv_y, col_trans)
WV(vb, vw + 1, p1x + n1x * half_aa, p1y + n1y * half_aa, uv_x, uv_y, col_trans)
vw = vw + 2; vtx_idx = vtx_idx + 2
-- Connect with previous
ib[iw + 0] = prev_outer_idx; ib[iw + 1] = outer_idx; ib[iw + 2] = inner_idx
ib[iw + 3] = prev_outer_idx; ib[iw + 4] = inner_idx; ib[iw + 5] = prev_inner_idx
-- Fill bevel
ib[iw + 6] = outer_idx; ib[iw + 7] = outer_idx + 1; ib[iw + 8] = inner_idx
iw = iw + 9
outer_idx = outer_idx + 1
else
WV(vb, vw, p1x + miter_offset_x, p1y + miter_offset_y, uv_x, uv_y, col_trans)
vw = vw + 1; vtx_idx = vtx_idx + 1
-- Connect with previous
ib[iw + 0] = prev_outer_idx; ib[iw + 1] = outer_idx; ib[iw + 2] = inner_idx
ib[iw + 3] = prev_outer_idx; ib[iw + 4] = inner_idx; ib[iw + 5] = prev_inner_idx
iw = iw + 6
end
prev_outer_idx = outer_idx
i0 = i1
end
end
-- Patch first segment to wrap around
ib[start_idx_ptr + 0] = prev_outer_idx
ib[start_idx_ptr + 3] = prev_outer_idx
-- Add indices for fill
for i = 2, points_count - 1 do
ib[iw + 0] = vtx_inner_idx; ib[iw + 1] = vtx_inner_idx + i - 1; ib[iw + 2] = vtx_inner_idx + i
iw = iw + 3
end
self._VtxWritePtr = vw
self._IdxWritePtr = iw
self._VtxCurrentIdx = vtx_idx
local idx_used = iw - start_idx_ptr
local vtx_used = vw - start_vtx_ptr
assert(idx_used <= idx_count and vtx_used <= vtx_count)
if idx_used < idx_count or vtx_used < vtx_count then
self:PrimUnreserve(idx_count - idx_used, vtx_count - vtx_used)
end
else
-- Non Anti-aliased Fill
local idx_count = (points_count - 2) * 3
local vtx_count = points_count
PrimReserve(self, idx_count, vtx_count)
local vb = self.VtxBuffer.Buf
local ib = self.IdxBuffer.Data
local vw = self._VtxWritePtr
local iw = self._IdxWritePtr
local base_idx = self._VtxCurrentIdx
for i = 1, vtx_count do
local p = points[i]
WV(vb, vw, p.x, p.y, uv_x, uv_y, col)
vw = vw + 1
end
for i = 2, points_count - 1 do
ib[iw + 0] = base_idx; ib[iw + 1] = base_idx + i - 1; ib[iw + 2] = base_idx + i
iw = iw + 3
end
self._VtxWritePtr = vw
self._IdxWritePtr = iw
self._VtxCurrentIdx = base_idx + vtx_count
end
end
-- [Luau] scalar PathLineTo(): no temporary ImVec2 (the path slot is written in place)
local function PathLineToXY(self, x, y)
local path = self._Path
local n = path.Size + 1
if n > path.Capacity then path:push_back(ImVec2(x, y)) return end
local dest = path.Data[n]
dest.x = x; dest.y = y
path.Size = n
end
local _PathArcToFastExXY -- [Luau] forward declaration, defined below with _PathArcToFastEx()
-- [Luau] scalar PathRect (a = ax,ay; b = bx,by); PathRect() below wraps it
local function PathRectXY(self, ax, ay, bx, by, rounding, flags)
if not rounding then rounding = 0.0 end
if not flags then flags = 0 end
local has_rounding = rounding >= 0.25 * self._FringeScale -- IM_HAS_ROUNDING(rounding)
if has_rounding then
if bit32.band(flags, 496 --[[ImDrawFlags.RoundMask_]]) == 0 then
flags = bit32.bor(flags, 240 --[[ImDrawFlags.RoundAll]])
end
rounding = ImMin(rounding, ImAbs(bx - ax) * (((bit32.band(flags, 48 --[[ImDrawFlags.RoundTop]]) == 48 --[[ImDrawFlags.RoundTop]]) or (bit32.band(flags, 192 --[[ImDrawFlags.RoundBottom]]) == 192 --[[ImDrawFlags.RoundBottom]])) and 0.5 or 1.0) - 1.0)
rounding = ImMin(rounding, ImAbs(by - ay) * (((bit32.band(flags, 80 --[[ImDrawFlags.RoundLeft]]) == 80 --[[ImDrawFlags.RoundLeft]]) or (bit32.band(flags, 160 --[[ImDrawFlags.RoundRight]]) == 160 --[[ImDrawFlags.RoundRight]])) and 0.5 or 1.0) - 1.0)
end
if not has_rounding or (bit32.band(flags, 496 --[[ImDrawFlags.RoundMask_]]) == 256 --[[ImDrawFlags.RoundNone]]) then
PathLineToXY(self, ax, ay)
PathLineToXY(self, bx, ay)
PathLineToXY(self, bx, by)
PathLineToXY(self, ax, by)
else
local rounding_tl = (bit32.band(flags, 16 --[[ImDrawFlags.RoundTopLeft]]) ~= 0) and rounding or 0.0
local rounding_tr = (bit32.band(flags, 32 --[[ImDrawFlags.RoundTopRight]]) ~= 0) and rounding or 0.0
local rounding_br = (bit32.band(flags, 128 --[[ImDrawFlags.RoundBottomRight]]) ~= 0) and rounding or 0.0
local rounding_bl = (bit32.band(flags, 64 --[[ImDrawFlags.RoundBottomLeft]]) ~= 0) and rounding or 0.0
-- [Luau] inline PathArcToFast() (small radius handled by _PathArcToFastExXY())
_PathArcToFastExXY(self, ax + rounding_tl, ay + rounding_tl, rounding_tl, 6 * 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] / 12, 9 * 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] / 12, 0)
_PathArcToFastExXY(self, bx - rounding_tr, ay + rounding_tr, rounding_tr, 9 * 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] / 12, 12 * 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] / 12, 0)
_PathArcToFastExXY(self, bx - rounding_br, by - rounding_br, rounding_br, 0 * 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] / 12, 3 * 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] / 12, 0)
_PathArcToFastExXY(self, ax + rounding_bl, by - rounding_bl, rounding_bl, 3 * 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] / 12, 6 * 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] / 12, 0)
end
end
function MT.ImDrawList:PathRect(a, b, rounding, flags)
PathRectXY(self, a.x, a.y, b.x, b.y, rounding, flags)
end
-- [Luau] scalar _AddLine (p1 = p1x,p1y; p2 = p2x,p2y); _AddLine() below wraps it
@native
local function _AddLineXY(self, p1x, p1y, p2x, p2y, col, thickness, flags)
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
local screen_thickness = thickness * self._InvFringeScale
if screen_thickness < 1.0 then
if screen_thickness < 1.0 / 255.0 then
return
end
col = ImAlphaMultiply(col, screen_thickness)
screen_thickness = 1.0
thickness = self._FringeScale
end
flags = bit32.bor(flags, self.Flags)
local u0, v0, u1, v1, fringe = _SelectLineTexture(self, screen_thickness, flags)
local dir_x = p2x - p1x
local dir_y = p2y - p1y
local d2 = dir_x * dir_x + dir_y * dir_y
local inv_len = 0.0
if d2 > 0.0 then inv_len = 1.0 / math.sqrt(d2) end -- ImRsqrtPrecise()
dir_x = dir_x * inv_len
dir_y = dir_y * inv_len
local norm_x = -dir_y
local norm_y = dir_x
local col_trans = bit32.band(col, bit32.bnot(4278190080 --[[IM_COL32_A_MASK]]))
local stroke_pos = _GetStrokePos(flags, 262144 --[[ImDrawFlags.StrokeCenter]])
if stroke_pos == 917504 --[[ImDrawFlags.StrokeLegacy]] then
flags = bit32.band(bit32.bor(flags, 1024 --[[ImDrawFlags.JoinMiter]]), bit32.bnot(16384 --[[ImDrawFlags.AALineEnds]]))
end
local thickness0 = (thickness + fringe) * 0.5 -- Center (or legacy)
if stroke_pos == 524288 --[[ImDrawFlags.StrokeOutside]] then
thickness0 = thickness + fringe * 0.5
elseif stroke_pos == 393216 --[[ImDrawFlags.StrokeCenterBiased]] then
thickness0 = _CalculateCenterBiasedOffset(self, thickness) + fringe * 0.5
elseif stroke_pos == 131072 --[[ImDrawFlags.StrokeInside]] then
thickness0 = fringe * 0.5
end
local thickness1 = (thickness + fringe) - thickness0
-- [Luau] uv2 (ratio) is unused by _AddLine(): not computed
local half_aa = self._FringeScale * 0.5 -- Used for end caps, does not use "fringe" since end cap AA is not using the texture.
local use_aa_ends = bit32.band(flags, bit32.bor(8192 --[[ImDrawFlags.AALines]], 16384 --[[ImDrawFlags.AALineEnds]])) == bit32.bor(8192 --[[ImDrawFlags.AALines]], 16384 --[[ImDrawFlags.AALineEnds]])
if not use_aa_ends then
PrimReserve(self, 2 * 3, 4)
else
PrimReserve(self, 6 * 3, 8)
end
local vb = self.VtxBuffer.Buf
local ib = self.IdxBuffer.Data
local vw = self._VtxWritePtr
local iw = self._IdxWritePtr
local base_idx = self._VtxCurrentIdx
if bit32.band(flags, 2048 --[[ImDrawFlags.CapSquare]]) ~= 0 then
local half_thickness = thickness * 0.5
p1x = p1x - dir_x * half_thickness
p1y = p1y - dir_y * half_thickness
p2x = p2x + dir_x * half_thickness
p2y = p2y + dir_y * half_thickness
end
-- Start cap
if not use_aa_ends then
WV(vb, vw + 0, p1x - norm_x * thickness0, p1y - norm_y * thickness0, u0, v0, col)
WV(vb, vw + 1, p1x + norm_x * thickness1, p1y + norm_y * thickness1, u1, v1, col)
WV(vb, vw + 2, p2x - norm_x * thickness0, p2y - norm_y * thickness0, u0, v0, col)
WV(vb, vw + 3, p2x + norm_x * thickness1, p2y + norm_y * thickness1, u1, v1, col)
-- Connect
ib[iw + 0] = base_idx + 0; ib[iw + 1] = base_idx + 2; ib[iw + 2] = base_idx + 3
ib[iw + 3] = base_idx + 0; ib[iw + 4] = base_idx + 3; ib[iw + 5] = base_idx + 1
self._VtxWritePtr = vw + 4
self._IdxWritePtr = iw + 6
self._VtxCurrentIdx = base_idx + 4
else
-- Start
local pa_x = p1x - dir_x * half_aa
local pa_y = p1y - dir_y * half_aa
local pb_x = p1x + dir_x * half_aa
local pb_y = p1y + dir_y * half_aa
WV(vb, vw + 0, pa_x - norm_x * thickness0, pa_y - norm_y * thickness0, u0, v0, col_trans)
WV(vb, vw + 1, pa_x + norm_x * thickness1, pa_y + norm_y * thickness1, u1, v1, col_trans)
WV(vb, vw + 2, pb_x - norm_x * thickness0, pb_y - norm_y * thickness0, u0, v0, col)
WV(vb, vw + 3, pb_x + norm_x * thickness1, pb_y + norm_y * thickness1, u1, v1, col)
-- AA cap
ib[iw + 0] = base_idx + 0; ib[iw + 1] = base_idx + 2; ib[iw + 2] = base_idx + 3
ib[iw + 3] = base_idx + 0; ib[iw + 4] = base_idx + 3; ib[iw + 5] = base_idx + 1
base_idx = base_idx + 2
-- End
pa_x = p2x - dir_x * half_aa
pa_y = p2y - dir_y * half_aa
pb_x = p2x + dir_x * half_aa
pb_y = p2y + dir_y * half_aa
WV(vb, vw + 4, pa_x - norm_x * thickness0, pa_y - norm_y * thickness0, u0, v0, col)
WV(vb, vw + 5, pa_x + norm_x * thickness1, pa_y + norm_y * thickness1, u1, v1, col)
WV(vb, vw + 6, pb_x - norm_x * thickness0, pb_y - norm_y * thickness0, u0, v0, col_trans)
WV(vb, vw + 7, pb_x + norm_x * thickness1, pb_y + norm_y * thickness1, u1, v1, col_trans)
-- Connect
ib[iw + 6] = base_idx + 0; ib[iw + 7] = base_idx + 2; ib[iw + 8] = base_idx + 3
ib[iw + 9] = base_idx + 0; ib[iw + 10] = base_idx + 3; ib[iw + 11] = base_idx + 1
base_idx = base_idx + 2
-- AA cap
ib[iw + 12] = base_idx + 0; ib[iw + 13] = base_idx + 2; ib[iw + 14] = base_idx + 3
ib[iw + 15] = base_idx + 0; ib[iw + 16] = base_idx + 3; ib[iw + 17] = base_idx + 1
self._VtxWritePtr = vw + 8
self._IdxWritePtr = iw + 18
self._VtxCurrentIdx = base_idx + 4
end
end
function MT.ImDrawList:_AddLine(p1, p2, col, thickness, flags)
_AddLineXY(self, p1.x, p1.y, p2.x, p2.y, col, thickness, flags)
end
--- @param p1 ImVec2
--- @param p2 ImVec2
--- @param col ImU32
--- @param thickness? float
--- @param flags? ImDrawFlags
function MT.ImDrawList:AddLine(p1, p2, col, thickness, flags)
if thickness == nil then thickness = 1.0 end
if flags == nil then flags = 0 end
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
flags = bit32.bor(flags, self.Flags)
local stroke_pos = _GetStrokePos(flags, 262144 --[[ImDrawFlags.StrokeCenter]])
if stroke_pos == 917504 --[[ImDrawFlags.StrokeLegacy]] then
_AddLineXY(self, p1.x + 0.5, p1.y + 0.5, p2.x + 0.5, p2.y + 0.5, col, thickness, flags)
return
end
_AddLineXY(self, p1.x, p1.y, p2.x, p2.y, col, thickness, flags)
end
-- AddLineH(10, 20, 5, col, 1.0f) == AddLine( {10,5.5}, {20,5.5}, col, 1.0f)
-- == AddLine( {10,5}, {20,5}, col, 1.0f, ImDrawFlags_StrokeLegacy); // or before 1.93.0
-- Thickness extend inside/bottom by default. Faster than equivalent AddLine() call.
--- @param min_x float
--- @param max_x float
--- @param y float
--- @param col ImU32
--- @param thickness? float
--- @param flags? ImDrawFlags
@native
function MT.ImDrawList:AddLineH(min_x, max_x, y, col, thickness, flags)
if thickness == nil then thickness = 1.0 end
if flags == nil then flags = 0 end
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
flags = bit32.bor(flags, self.Flags)
local stroke_pos = _GetStrokePos(flags, 131072 --[[ImDrawFlags.StrokeInside]])
if stroke_pos == 917504 --[[ImDrawFlags.StrokeLegacy]] then
_AddLineXY(self, min_x + 0.5, y + 0.5, max_x + 0.5, y + 0.5, col, thickness, flags)
return
end
local top_y = y -- Inside
if stroke_pos == 262144 --[[ImDrawFlags.StrokeCenter]] then
top_y = top_y - thickness * 0.5
elseif stroke_pos == 393216 --[[ImDrawFlags.StrokeCenterBiased]] then
top_y = top_y - _CalculateCenterBiasedOffset(self, thickness)
elseif stroke_pos == 524288 --[[ImDrawFlags.StrokeOutside]] then
top_y = top_y - thickness
end
local is_truncated = min_x % 1 == 0 and max_x % 1 == 0 and top_y % 1 == 0 and thickness % 1 == 0 -- IM_IS_TRUNCATED4()
if self._FringeScaleIsInteger and is_truncated and bit32.band(flags, 2048 --[[ImDrawFlags.CapSquare]]) == 0 then
if thickness < self._FringeScale then
if thickness < (1.0 / 255.0) * self._FringeScale then
return
end
col = ImAlphaMultiply(col, thickness * self._InvFringeScale)
thickness = self._FringeScale
end
-- For pixel aligned case, use simple rectangle.
PrimReserve(self, 6, 4)
PrimRectXY(self, min_x, top_y, max_x, top_y + thickness, col)
else
-- Replace stroke pos with the already calculated one, as AddPolyline() default is different.
flags = bit32.bor(bit32.band(flags, bit32.bnot(917504 --[[ImDrawFlags.StrokeMask_]])), stroke_pos)
-- For generic case use line, since it needs less triangles than AA rectangle.
_AddLineXY(self, min_x, y, max_x, y, col, thickness, flags)
end
end
-- AddLineV(5, 10, 20, col, 1.0f) == AddLine( {5.5,10}, {5.5,20}, col, 1.0f)
-- == AddLine( {5,10}, {5,20}, col, 1.0f, ImDrawFlags_StrokeLegacy); // or before 1.93.0
-- Thickness extend inside/right by default. Faster than equivalent AddLine() call.
--- @param x float
--- @param min_y float
--- @param max_y float
--- @param col ImU32
--- @param thickness? float
--- @param flags? ImDrawFlags
@native
function MT.ImDrawList:AddLineV(x, min_y, max_y, col, thickness, flags)
if thickness == nil then thickness = 1.0 end
if flags == nil then flags = 0 end
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
flags = bit32.bor(flags, self.Flags)
local stroke_pos = _GetStrokePos(flags, 131072 --[[ImDrawFlags.StrokeInside]])
if stroke_pos == 917504 --[[ImDrawFlags.StrokeLegacy]] then
_AddLineXY(self, x + 0.5, min_y + 0.5, x + 0.5, max_y + 0.5, col, thickness, flags)
return
end
local left_x = x -- Inside
if stroke_pos == 262144 --[[ImDrawFlags.StrokeCenter]] then
left_x = left_x - thickness * 0.5
elseif stroke_pos == 393216 --[[ImDrawFlags.StrokeCenterBiased]] then
left_x = left_x - _CalculateCenterBiasedOffset(self, thickness)
elseif stroke_pos == 524288 --[[ImDrawFlags.StrokeOutside]] then
left_x = left_x - thickness
end
local is_truncated = left_x % 1 == 0 and min_y % 1 == 0 and max_y % 1 == 0 and thickness % 1 == 0 -- IM_IS_TRUNCATED4()
if self._FringeScaleIsInteger and is_truncated and bit32.band(flags, 2048 --[[ImDrawFlags.CapSquare]]) == 0 then
if thickness < self._FringeScale then
if thickness < (1.0 / 255.0) * self._FringeScale then
return
end
col = ImAlphaMultiply(col, thickness * self._InvFringeScale)
thickness = self._FringeScale
end
-- For pixel aligned case, use simple rectangle.
PrimReserve(self, 6, 4)
PrimRectXY(self, left_x, min_y, left_x + thickness, max_y, col)
else
-- Replace stroke pos with the already calculated one, as AddPolyline() default is different.
flags = bit32.bor(bit32.band(flags, bit32.bnot(917504 --[[ImDrawFlags.StrokeMask_]])), stroke_pos)
-- For generic case use line, since it needs less triangles than AA rectangle.
-- Drawing for bottom to top, so that the inside stroke pos expands to right.
_AddLineXY(self, x, max_y, x, min_y, col, thickness, flags)
end
end
-- [Luau] IM_APPEND_VTX() x4 + IM_APPEND_TRI(idx + 0, idx + 1, idx + 2) + IM_APPEND_TRI(idx + 0, idx + 2, idx + 3)
local function AppendQuadUV(self, x0, y0, u0, v0, x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, col)
local vb = self.VtxBuffer.Buf
local ib = self.IdxBuffer.Data
local vw = self._VtxWritePtr
local iw = self._IdxWritePtr
local idx = self._VtxCurrentIdx
WV(vb, vw + 0, x0, y0, u0, v0, col)
WV(vb, vw + 1, x1, y1, u1, v1, col)
WV(vb, vw + 2, x2, y2, u2, v2, col)
WV(vb, vw + 3, x3, y3, u3, v3, col)
ib[iw + 0] = idx + 0; ib[iw + 1] = idx + 1; ib[iw + 2] = idx + 2
ib[iw + 3] = idx + 0; ib[iw + 4] = idx + 2; ib[iw + 5] = idx + 3
self._VtxWritePtr = vw + 4
self._IdxWritePtr = iw + 6
self._VtxCurrentIdx = idx + 4
end
-- [Luau] _AddRectBaked() taking p_min/p_max as scalars
@native
local function _AddRectBakedXY(self, p_min_x, p_min_y, p_max_x, p_max_y, col, r, t, tex_uvs, flags)
local uv_l, uv_t, uv_r, uv_b = tex_uvs.x, tex_uvs.y, tex_uvs.z, tex_uvs.w -- uv_tl = (l,t), uv_tr = (r,t), uv_br = (r,b), uv_bl = (l,b)
local idx_allocated = 8 * 2 * 3
local vtx_allocated = 8 * 4
PrimReserve(self, idx_allocated, vtx_allocated)
local start_vtx_ptr = self._VtxWritePtr
local start_idx_ptr = self._IdxWritePtr
local round_tl = bit32.band(flags, 16 --[[ImDrawFlags.RoundTopLeft]]) ~= 0
local round_tr = bit32.band(flags, 32 --[[ImDrawFlags.RoundTopRight]]) ~= 0
local round_br = bit32.band(flags, 128 --[[ImDrawFlags.RoundBottomRight]]) ~= 0
local round_bl = bit32.band(flags, 64 --[[ImDrawFlags.RoundBottomLeft]]) ~= 0
if round_tl then
AppendQuadUV(self,
p_min_x, p_min_y, uv_l, uv_t,
p_min_x + r, p_min_y, uv_r, uv_t,
p_min_x + r, p_min_y + r, uv_r, uv_b,
p_min_x, p_min_y + r, uv_l, uv_b, col)
end
if round_tr then
AppendQuadUV(self,
p_max_x - r, p_min_y, uv_r, uv_t,
p_max_x, p_min_y, uv_l, uv_t,
p_max_x, p_min_y + r, uv_l, uv_b,
p_max_x - r, p_min_y + r, uv_r, uv_b, col)
end
if round_br then
AppendQuadUV(self,
p_max_x - r, p_max_y - r, uv_r, uv_b,
p_max_x, p_max_y - r, uv_l, uv_b,
p_max_x, p_max_y, uv_l, uv_t,
p_max_x - r, p_max_y, uv_r, uv_t, col)
end
if round_bl then
AppendQuadUV(self,
p_min_x, p_max_y - r, uv_l, uv_b,
p_min_x + r, p_max_y - r, uv_r, uv_b,
p_min_x + r, p_max_y, uv_r, uv_t,
p_min_x, p_max_y, uv_l, uv_t, col)
end
local r_tl = round_tl and r or 0
local r_tr = round_tr and r or 0
local r_br = round_br and r or 0
local r_bl = round_bl and r or 0
local opaque_uv = self._Data.TexUvWhitePixel
local ou, ov = opaque_uv.x, opaque_uv.y
do
-- Top
local x0 = p_min_x + r_tl
local x1 = p_max_x - r_tr
AppendQuadUV(self, x0, p_min_y, ou, ov, x1, p_min_y, ou, ov, x1, p_min_y + t, ou, ov, x0, p_min_y + t, ou, ov, col)
end
do
-- Left
local y0 = p_min_y + ImMax(r_tl, t)
local y1 = p_max_y - ImMax(r_bl, t)
AppendQuadUV(self, p_min_x, y0, ou, ov, p_min_x + t, y0, ou, ov, p_min_x + t, y1, ou, ov, p_min_x, y1, ou, ov, col)
end
do
-- Right
local y0 = p_min_y + ImMax(r_tr, t)
local y1 = p_max_y - ImMax(r_br, t)
AppendQuadUV(self, p_max_x - t, y0, ou, ov, p_max_x, y0, ou, ov, p_max_x, y1, ou, ov, p_max_x - t, y1, ou, ov, col)
end
do
-- Bottom
local x0 = p_min_x + r_bl
local x1 = p_max_x - r_br
AppendQuadUV(self, x0, p_max_y - t, ou, ov, x1, p_max_y - t, ou, ov, x1, p_max_y, ou, ov, x0, p_max_y, ou, ov, col)
end
local idx_used = self._IdxWritePtr - start_idx_ptr
local vtx_used = self._VtxWritePtr - start_vtx_ptr
IM_ASSERT_PARANOID(idx_used <= idx_allocated and vtx_used <= vtx_allocated)
if idx_used < idx_allocated or vtx_used < vtx_allocated then
self:PrimUnreserve(idx_allocated - idx_used, vtx_allocated - vtx_used)
end
end
--- @param p_min ImVec2
--- @param p_max ImVec2
--- @param col ImU32
--- @param r float
--- @param t float
--- @param tex_uvs ImVec4
--- @param flags ImDrawFlags
function MT.ImDrawList:_AddRectBaked(p_min, p_max, col, r, t, tex_uvs, flags)
_AddRectBakedXY(self, p_min.x, p_min.y, p_max.x, p_max.y, col, r, t, tex_uvs, flags)
end
local corner_offset_x = { 1, -1, -1, 1 } -- static const ImVec2 corner_offset[4] = { {1,1}, {-1,1}, {-1,-1}, {1,-1} };
local corner_offset_y = { 1, 1, -1, -1 }
local corner_flags = { ImDrawFlags.RoundTopLeft, ImDrawFlags.RoundTopRight, ImDrawFlags.RoundBottomRight, ImDrawFlags.RoundBottomLeft }
local corner_pos_x, corner_pos_y = { 0, 0, 0, 0 }, { 0, 0, 0, 0 } -- [Luau] scratch
-- Draws rounded rectangle where thickness > rounding. If rendered using the regular polyline, the stroke will fold and leave artifacts on the corners if rendered with transparency.
-- The stroke is positioned inside the rectangle.
--- @param p_min ImVec2
--- @param p_max ImVec2
--- @param col ImU32
--- @param rounding float
--- @param thickness float
--- @param flags ImDrawFlags
function MT.ImDrawList:_AddRectTinyRounding(p_min, p_max, col, rounding, thickness, flags)
local screen_thickness = thickness * self._InvFringeScale
if screen_thickness < 1.0 then
if screen_thickness < 1.0 / 255.0 then
return
end
col = ImAlphaMultiply(col, screen_thickness)
screen_thickness = 1.0
thickness = self._FringeScale
end
flags = bit32.bor(flags, self.Flags)
local outer_u, outer_v, inner_u, inner_v, fringe = _SelectLineTexture(self, screen_thickness, flags)
local auto_seg_count = self:_CalcCircleAutoSegmentCount(rounding)
local arc_step, arc_step_count = CalcCornerArcStepAndCount(auto_seg_count)
local arc_point_count = arc_step_count + 1
local vtx_count = (2 + arc_point_count) * 4 + 3
local idx_count = (4 + arc_point_count - 1) * 4 * 3
PrimReserve(self, idx_count, vtx_count)
local vb = self.VtxBuffer.Buf
local ib = self.IdxBuffer.Data
local start_vtx_ptr = self._VtxWritePtr
local start_idx_ptr = self._IdxWritePtr
local vw, iw = start_vtx_ptr, start_idx_ptr
local vtx_idx = self._VtxCurrentIdx
local first_vtx_idx = vtx_idx -- [Luau] VtxBuffer position of index 'n' is start_vtx_ptr + (n - first_vtx_idx)
local half_fringe = fringe * 0.5
corner_pos_x[1] = p_min.x - half_fringe; corner_pos_y[1] = p_min.y - half_fringe
corner_pos_x[2] = p_max.x + half_fringe; corner_pos_y[2] = p_min.y - half_fringe
corner_pos_x[3] = p_max.x + half_fringe; corner_pos_y[3] = p_max.y + half_fringe
corner_pos_x[4] = p_min.x - half_fringe; corner_pos_y[4] = p_max.y + half_fringe
thickness = thickness + fringe
rounding = rounding + half_fringe
local stem_offset = ImMin(rounding, thickness) -- The function might get called with rounding slightly bigger than thickness, which is ok, but we should keep the stem inside the shape.
local ratio = stem_offset / thickness
local stem_u, stem_v = outer_u + (inner_u - outer_u) * ratio, outer_v
-- Previous vertices, copied from the last vertices.
local base_idx = vtx_idx
WV(vb, vw + 0, 0, 0, inner_u, inner_v, col)
WV(vb, vw + 1, 0, 0, stem_u, stem_v, col)
WV(vb, vw + 2, 0, 0, outer_u, outer_v, col)
vw = vw + 3; vtx_idx = vtx_idx + 3
local arc_fast_vtx = self._Data.ArcFastVtx -- 1-based
for corner = 1, 4 do
local outer_x = corner_pos_x[corner]
local outer_y = corner_pos_y[corner]
local cox, coy = corner_offset_x[corner], corner_offset_y[corner]
local stem_x = outer_x + cox * stem_offset
local stem_y = outer_y + coy * stem_offset
local inner_x = outer_x + cox * thickness
local inner_y = outer_y + coy * thickness
local is_rounded = bit32.band(flags, corner_flags[corner]) ~= 0
local arc_idx = vtx_idx
if is_rounded then
local center_x = outer_x + cox * rounding
local center_y = outer_y + coy * rounding
local a = (48 --[[IM_DRAWLIST_ARCFAST_TABLE_SIZE]] / 4) * (corner - 1)
for _ = 0, arc_point_count - 1 do
local s = arc_fast_vtx[a + 1]
WV(vb, vw, center_x - s.x * rounding, center_y - s.y * rounding, outer_u, outer_v, col)
vw = vw + 1; vtx_idx = vtx_idx + 1
a = (a + arc_step) % 48 --[[IM_DRAWLIST_ARCFAST_TABLE_SIZE]]
end
else
WV(vb, vw, outer_x, outer_y, outer_u, outer_v, col)
vw = vw + 1; vtx_idx = vtx_idx + 1
end
local stem_idx = vtx_idx
WV(vb, vw, stem_x, stem_y, stem_u, stem_v, col)
local inner_idx = vtx_idx + 1
WV(vb, vw + 1, inner_x, inner_y, inner_u, inner_v, col)
vw = vw + 2; vtx_idx = vtx_idx + 2
-- Arc
if is_rounded then
for i = 0, arc_point_count - 2 do
ib[iw + 0] = stem_idx; ib[iw + 1] = arc_idx + i; ib[iw + 2] = arc_idx + i + 1
iw = iw + 3
end
end
-- Connect with previous
ib[iw + 0] = base_idx + 0; ib[iw + 1] = arc_idx; ib[iw + 2] = stem_idx
ib[iw + 3] = base_idx + 0; ib[iw + 4] = stem_idx; ib[iw + 5] = base_idx + 1
ib[iw + 6] = base_idx + 1; ib[iw + 7] = stem_idx; ib[iw + 8] = inner_idx
ib[iw + 9] = base_idx + 1; ib[iw + 10] = inner_idx; ib[iw + 11] = base_idx + 2
iw = iw + 12
base_idx = stem_idx - 1
end
-- Close
-- [Luau] start_vtx_ptr[0..2] = VtxBuffer.Data[_CmdHeader.VtxOffset + base_idx + 0..2] (3 vertices of 36 bytes)
buffer.copy(vb, (start_vtx_ptr - 1) * 36, vb, (start_vtx_ptr + (base_idx - first_vtx_idx) - 1) * 36, 3 * 36)
self._VtxWritePtr = vw
self._IdxWritePtr = iw
self._VtxCurrentIdx = vtx_idx
local idx_used = iw - start_idx_ptr
local vtx_used = vw - start_vtx_ptr
IM_ASSERT_PARANOID(idx_used <= idx_count and vtx_used <= vtx_count)
if idx_used < idx_count or vtx_used < vtx_count then
self:PrimUnreserve(idx_count - idx_used, vtx_count - vtx_used)
end
end
local prim_points = { ImVec2(), ImVec2(), ImVec2(), ImVec2() } -- [Luau] scratch for C++ 'const ImVec2 points[N]' locals (callees only read them)
-- p_min = upper-left, p_max = lower-right
--- @param p_min ImVec2
--- @param p_max ImVec2
--- @param col ImU32
--- @param rounding? float
--- @param thickness? float
--- @param flags? ImDrawFlags
@native
function MT.ImDrawList:AddRect(p_min, p_max, col, rounding, thickness, flags)
if rounding == nil then rounding = 0.0 end
if thickness == nil then thickness = 1.0 end
if flags == nil then flags = 0 end
-- If this assert triggers on legacy code:
-- - 1.92.8 (2025/05): swapped two last parameters order: flags, thickness --> thickness, flags. This should normally be caught by compile-time type-checking.
-- - 1.92.8 (2025/05): changed value of ImDrawList_Closed which was previously guaranteed to be == 1. Hardcoded use of 1 or true should be replaced.
-- - 1.82.0 (2021/03): changed ImDrawCornerFlags to ImDrawFlags_RoundXXX values.
-- If you used hard-coded 1 to 15 or ~0 in flags to configure corner rounding use the new flags!
-- - Hard coded support for ~0 == ImDrawFlags_RoundAll.
-- - Hard coded support for values 0x01 to 0x0F (matching 15 out of 16 old flags combinations) --> see FixRectCornerFlags() in <1.90 code.
-- - Hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundNone or use 'float rounding = 0.0f'.
-- See "API BREAKING CHANGES" section for 1.82, 1.90 and 1.92.8.
IM_ASSERT_USER_ERROR_RET(bit32.band(flags, 2147483663 --[[ImDrawFlags.InvalidMask_]]) == 0, "Incorrect parameter. Did you swap 'thickness' and 'flags'?") -- Or misuse of legacy hard-coded ImDrawCornerFlags values
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
flags = bit32.bor(flags, self.Flags)
local stroke_pos = _GetStrokePos(flags, 131072 --[[ImDrawFlags.StrokeInside]])
if stroke_pos == 917504 --[[ImDrawFlags.StrokeLegacy]] then
if bit32.band(flags, 8192 --[[ImDrawFlags.AALines]]) ~= 0 then
PathRectXY(self, p_min.x + 0.50, p_min.y + 0.50, p_max.x - 0.50, p_max.y - 0.50, rounding, flags) -- [Luau] scalar math, no temporary ImVec2
else
PathRectXY(self, p_min.x + 0.50, p_min.y + 0.50, p_max.x - 0.49, p_max.y - 0.49, rounding, flags) -- Better looking lower-right corner and rounded non-AA shapes.
end
self:PathStroke(col, thickness, bit32.bor(512 --[[ImDrawFlags.Closed]], 1024 --[[ImDrawFlags.JoinMiter]], 262144 --[[ImDrawFlags.StrokeCenter]], bit32.band(flags, bit32.bnot(917504 --[[ImDrawFlags.StrokeMask_]]))))
return
end
local fringe_scale = self._FringeScale
local has_rounding = rounding >= 0.25 * fringe_scale -- IM_HAS_ROUNDING(rounding)
-- Calculate the outer boundary of the rectangle based on the stroke position.
local offset = 0.0 -- Inside
if stroke_pos == 262144 --[[ImDrawFlags.StrokeCenter]] then
offset = thickness * 0.5
elseif stroke_pos == 393216 --[[ImDrawFlags.StrokeCenterBiased]] then
offset = _CalculateCenterBiasedOffset(self, thickness)
elseif stroke_pos == 524288 --[[ImDrawFlags.StrokeOutside]] then
offset = thickness
end
local outer_min_x = p_min.x - offset
local outer_min_y = p_min.y - offset
local outer_max_x = p_max.x + offset
local outer_max_y = p_max.y + offset
local outer_rounding = rounding + (has_rounding and offset or 0.0)
local width = outer_max_x - outer_min_x
local height = outer_max_y - outer_min_y
local half_min_dim = ImMin(width, height) * 0.5
if half_min_dim <= thickness then
-- Rectangle has collapsed into filled rectangle.
self:AddRectFilled(ImVec2(outer_min_x, outer_min_y), ImVec2(outer_max_x, outer_max_y), col, outer_rounding, flags)
return
end
if bit32.band(flags, 496 --[[ImDrawFlags.RoundMask_]]) == 0 then
flags = bit32.bor(flags, 240 --[[ImDrawFlags.RoundAll]])
end
if has_rounding then
-- Constrain rounding to rect dimensions
outer_rounding = ImMin(outer_rounding, ImAbs(width) * (((bit32.band(flags, 48 --[[ImDrawFlags.RoundTop]]) == 48 --[[ImDrawFlags.RoundTop]]) or (bit32.band(flags, 192 --[[ImDrawFlags.RoundBottom]]) == 192 --[[ImDrawFlags.RoundBottom]])) and 0.5 or 1.0) - 1.0)
outer_rounding = ImMin(outer_rounding, ImAbs(height) * (((bit32.band(flags, 80 --[[ImDrawFlags.RoundLeft]]) == 80 --[[ImDrawFlags.RoundLeft]]) or (bit32.band(flags, 160 --[[ImDrawFlags.RoundRight]]) == 160 --[[ImDrawFlags.RoundRight]])) and 0.5 or 1.0) - 1.0)
end
local is_truncated = outer_min_x % 1 == 0 and outer_min_y % 1 == 0 and outer_max_x % 1 == 0 and outer_max_y % 1 == 0 and outer_rounding % 1 == 0 and thickness % 1 == 0 -- IM_IS_TRUNCATED4() x2
if self._FringeScaleIsInteger and is_truncated then
local s_rounding = ImTrunc64(outer_rounding * self._InvFringeScale)
local s_thickness = ImTrunc64(thickness * self._InvFringeScale)
if s_thickness <= 0 then
return
end
if s_rounding <= 0 or bit32.band(flags, 496 --[[ImDrawFlags.RoundMask_]]) == 256 --[[ImDrawFlags.RoundNone]] then
-- Pixel aligned non-rounded rect.
local opaque_uv = self._Data.TexUvWhitePixel
local u, v = opaque_uv.x, opaque_uv.y
local t = s_thickness * fringe_scale
PrimReserve(self, 8 * 3, 8)
local vb = self.VtxBuffer.Buf
local ib = self.IdxBuffer.Data
local vw = self._VtxWritePtr
local iw = self._IdxWritePtr
local idx = self._VtxCurrentIdx
WV(vb, vw + 0, outer_min_x, outer_min_y, u, v, col)
WV(vb, vw + 1, outer_min_x + t, outer_min_y + t, u, v, col)
WV(vb, vw + 2, outer_max_x, outer_min_y, u, v, col)
WV(vb, vw + 3, outer_max_x - t, outer_min_y + t, u, v, col)
WV(vb, vw + 4, outer_max_x, outer_max_y, u, v, col)
WV(vb, vw + 5, outer_max_x - t, outer_max_y - t, u, v, col)
WV(vb, vw + 6, outer_min_x, outer_max_y, u, v, col)
WV(vb, vw + 7, outer_min_x + t, outer_max_y - t, u, v, col)
ib[iw + 0] = idx + 0; ib[iw + 1] = idx + 3; ib[iw + 2] = idx + 1
ib[iw + 3] = idx + 0; ib[iw + 4] = idx + 2; ib[iw + 5] = idx + 3
ib[iw + 6] = idx + 2; ib[iw + 7] = idx + 5; ib[iw + 8] = idx + 3
ib[iw + 9] = idx + 2; ib[iw + 10] = idx + 4; ib[iw + 11] = idx + 5
ib[iw + 12] = idx + 4; ib[iw + 13] = idx + 7; ib[iw + 14] = idx + 5
ib[iw + 15] = idx + 4; ib[iw + 16] = idx + 6; ib[iw + 17] = idx + 7
ib[iw + 18] = idx + 6; ib[iw + 19] = idx + 1; ib[iw + 20] = idx + 7
ib[iw + 21] = idx + 6; ib[iw + 22] = idx + 0; ib[iw + 23] = idx + 1
self._VtxWritePtr = vw + 8
self._IdxWritePtr = iw + 24
self._VtxCurrentIdx = idx + 8
return
end
-- Textured corners are baked with AA, do not use them if no-AA is requested.
local allow_tex_corners_mask = bit32.bor(8192 --[[ImDrawFlags.AALines]], 2097152 --[[ImDrawFlags.UseTexForRoundCorners]], 8388608 --[[ImDrawFlags.AllowTexForRoundCorners_]])
local allow_tex_corners = bit32.band(flags, allow_tex_corners_mask) == allow_tex_corners_mask
if allow_tex_corners and s_thickness < 4 --[[IM_DRAWLIST_TEX_CORNERS_THICKNESS_MAX]] and s_rounding <= 16 --[[IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX]] then
-- Pixel aligned rect with round corners rendered using baked textures.
IM_ASSERT_PARANOID(s_thickness > 0 and s_rounding > 0)
local size = ImMax(s_rounding, s_thickness) -- This is matching the baking calculations.
local idx = (s_thickness * 16 --[[IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX]]) + s_rounding - 1
-- IM_ASSERT_PARANOID((_Data->Font->OwnerAtlas->Flags & ImFontAtlasFlags_NoBakedRoundCorners) == 0) -- [Luau] not evaluated (arguments are not compiled out)
IM_ASSERT_PARANOID(idx >= 0 and idx < 16 --[[IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX]] * 4 --[[IM_DRAWLIST_TEX_CORNERS_THICKNESS_MAX]])
_AddRectBakedXY(self, outer_min_x, outer_min_y, outer_max_x, outer_max_y, col, size * fringe_scale, thickness, self._Data.TexUvCorners[idx], flags)
return
end
end
if not has_rounding or bit32.band(flags, 496 --[[ImDrawFlags.RoundMask_]]) == 256 --[[ImDrawFlags.RoundNone]] then
local points = prim_points
points[1].x = outer_min_x; points[1].y = outer_min_y
points[2].x = outer_max_x; points[2].y = outer_min_y
points[3].x = outer_max_x; points[3].y = outer_max_y
points[4].x = outer_min_x; points[4].y = outer_max_y
self:_AddPolyline(points, 4, col, thickness, bit32.bor(512 --[[ImDrawFlags.Closed]], 1024 --[[ImDrawFlags.JoinMiter]], 131072 --[[ImDrawFlags.StrokeInside]]), half_min_dim)
self._Path.Size = 0
else
-- Switch to specialized rendering a bit early to avoid overdraw visible in transparent shapes.
if thickness + (0.75 * fringe_scale) > outer_rounding then
-- Special case rendering to avoid rendering artifacts at the corners.
-- If rendered using the regular polyline, the stroke will fold and leave artifacts on the corners if rendered with transparency.
self:_AddRectTinyRounding(ImVec2(outer_min_x, outer_min_y), ImVec2(outer_max_x, outer_max_y), col, outer_rounding, thickness, flags)
return
end
local rounding_tl = (bit32.band(flags, 16 --[[ImDrawFlags.RoundTopLeft]]) ~= 0) and outer_rounding or 0.0
local rounding_tr = (bit32.band(flags, 32 --[[ImDrawFlags.RoundTopRight]]) ~= 0) and outer_rounding or 0.0
local rounding_br = (bit32.band(flags, 128 --[[ImDrawFlags.RoundBottomRight]]) ~= 0) and outer_rounding or 0.0
local rounding_bl = (bit32.band(flags, 64 --[[ImDrawFlags.RoundBottomLeft]]) ~= 0) and outer_rounding or 0.0
-- [Luau] inline PathArcToFast()
_PathArcToFastExXY(self, outer_min_x + rounding_tl, outer_min_y + rounding_tl, rounding_tl, 6 * 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] / 12, 9 * 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] / 12, 0)
_PathArcToFastExXY(self, outer_max_x - rounding_tr, outer_min_y + rounding_tr, rounding_tr, 9 * 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] / 12, 12 * 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] / 12, 0)
_PathArcToFastExXY(self, outer_max_x - rounding_br, outer_max_y - rounding_br, rounding_br, 0 * 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] / 12, 3 * 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] / 12, 0)
_PathArcToFastExXY(self, outer_min_x + rounding_bl, outer_max_y - rounding_bl, rounding_bl, 3 * 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] / 12, 6 * 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] / 12, 0)
self:_AddPolyline(self._Path.Data, self._Path.Size, col, thickness, bit32.bor(512 --[[ImDrawFlags.Closed]], 1024 --[[ImDrawFlags.JoinMiter]], 131072 --[[ImDrawFlags.StrokeInside]], bit32.band(flags, bit32.bnot(917504 --[[ImDrawFlags.StrokeMask_]]))), half_min_dim)
self._Path.Size = 0
end
end
-- [Luau] _AddRectFilledBaked() taking p_min/p_max as scalars
@native
local function _AddRectFilledBakedXY(self, p_min_x, p_min_y, p_max_x, p_max_y, col, r, tex_uvs, flags)
-- 9-slice with corner mirroring
local uv_l, uv_t, uv_r, uv_b = tex_uvs.x, tex_uvs.y, tex_uvs.z, tex_uvs.w -- uv_tl = (l,t), uv_tr = (r,t), uv_br = (r,b), uv_bl = (l,b)
PrimReserve(self, 9 * 2 * 3, 4 * 4)
local r_tl = (bit32.band(flags, 16 --[[ImDrawFlags.RoundTopLeft]]) ~= 0) and r or 0.0
local r_tr = (bit32.band(flags, 32 --[[ImDrawFlags.RoundTopRight]]) ~= 0) and r or 0.0
local r_br = (bit32.band(flags, 128 --[[ImDrawFlags.RoundBottomRight]]) ~= 0) and r or 0.0
local r_bl = (bit32.band(flags, 64 --[[ImDrawFlags.RoundBottomLeft]]) ~= 0) and r or 0.0
local vb = self.VtxBuffer.Buf
local ib = self.IdxBuffer.Data
local vw = self._VtxWritePtr
local iw = self._IdxWritePtr
local idx = self._VtxCurrentIdx
WV(vb, vw + 0, p_min_x, p_min_y, uv_l, uv_t, col)
WV(vb, vw + 1, p_min_x + r_tl, p_min_y, uv_r, uv_t, col)
WV(vb, vw + 2, p_max_x - r_tr, p_min_y, uv_r, uv_t, col)
WV(vb, vw + 3, p_max_x, p_min_y, uv_l, uv_t, col)
WV(vb, vw + 4, p_min_x, p_min_y + r_tl, uv_l, uv_b, col)
WV(vb, vw + 5, p_min_x + r_tl, p_min_y + r_tl, uv_r, uv_b, col)
WV(vb, vw + 6, p_max_x - r_tr, p_min_y + r_tr, uv_r, uv_b, col)
WV(vb, vw + 7, p_max_x, p_min_y + r_tr, uv_l, uv_b, col)
WV(vb, vw + 8, p_min_x, p_max_y - r_bl, uv_l, uv_b, col)
WV(vb, vw + 9, p_min_x + r_bl, p_max_y - r_bl, uv_r, uv_b, col)
WV(vb, vw + 10, p_max_x - r_br, p_max_y - r_br, uv_r, uv_b, col)
WV(vb, vw + 11, p_max_x, p_max_y - r_br, uv_l, uv_b, col)
WV(vb, vw + 12, p_min_x, p_max_y, uv_l, uv_t, col)
WV(vb, vw + 13, p_min_x + r_bl, p_max_y, uv_r, uv_t, col)
WV(vb, vw + 14, p_max_x - r_br, p_max_y, uv_r, uv_t, col)
WV(vb, vw + 15, p_max_x, p_max_y, uv_l, uv_t, col)
for _ = 0, 2 do
for _ = 0, 2 do
ib[iw + 0] = idx + 4; ib[iw + 1] = idx + 0; ib[iw + 2] = idx + 1
ib[iw + 3] = idx + 4; ib[iw + 4] = idx + 1; ib[iw + 5] = idx + 5
iw = iw + 6
idx = idx + 1
end
idx = idx + 1
end
self._VtxWritePtr = vw + 16
self._IdxWritePtr = iw
self._VtxCurrentIdx = self._VtxCurrentIdx + 16
end
--- @param p_min ImVec2
--- @param p_max ImVec2
--- @param col ImU32
--- @param r float
--- @param tex_uvs ImVec4
--- @param flags ImDrawFlags
function MT.ImDrawList:_AddRectFilledBaked(p_min, p_max, col, r, tex_uvs, flags)
_AddRectFilledBakedXY(self, p_min.x, p_min.y, p_max.x, p_max.y, col, r, tex_uvs, flags)
end
--- @param p_min ImVec2
--- @param p_max ImVec2
--- @param col ImU32
--- @param rounding? float
--- @param flags? ImDrawFlags
@native
function MT.ImDrawList:AddRectFilled(p_min, p_max, col, rounding, flags)
if not rounding then rounding = 0.0 end
if not flags then flags = 0 end
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
local ax, ay, bx, by = p_min.x, p_min.y, p_max.x, p_max.y
local width = bx - ax
local height = by - ay
local min_dim = ImMin(width, height)
flags = bit32.bor(flags, self.Flags)
if bit32.band(flags, 917504 --[[ImDrawFlags.StrokeMask_]]) == 917504 --[[ImDrawFlags.StrokeLegacy]] and rounding < 0.5 then
PrimReserve(self, 6, 4) -- [Luau] local calls
PrimRectXY(self, ax, ay, bx, by, col)
return
end
-- The rect is smaller than pixel in one dimension.
local fringe_scale = self._FringeScale
if min_dim < fringe_scale then
if min_dim < (1.0 / 255.0) * fringe_scale then
return
end
if width < fringe_scale then
col = ImAlphaMultiply(col, width * self._InvFringeScale)
width = fringe_scale
end
if height < fringe_scale then
col = ImAlphaMultiply(col, height * self._InvFringeScale)
height = fringe_scale
end
local points = prim_points
points[1].x = ax; points[1].y = ay
points[2].x = ax + width; points[2].y = ay
points[3].x = ax + width; points[3].y = ay + height
points[4].x = ax; points[4].y = ay + height
self:AddConvexPolyFilled(points, 4, col, 1024 --[[ImDrawFlags.JoinMiter]])
return
end
-- When rounding non-integer and under threshold, has_rounding may be false but still affect is_truncated. Likely not worth catering for.
local has_rounding = bit32.band(flags, 496 --[[ImDrawFlags.RoundMask_]]) ~= 256 --[[ImDrawFlags.RoundNone]] and rounding >= 0.25 * fringe_scale -- IM_HAS_ROUNDING(rounding)
local is_truncated = ax % 1 == 0 and ay % 1 == 0 and bx % 1 == 0 and by % 1 == 0 and rounding % 1 == 0 -- IM_IS_TRUNCATED4() && IM_IS_TRUNCATED()
if self._FringeScaleIsInteger and is_truncated then
-- Fast path for the non-AA non-rounded rect.
if not has_rounding then
PrimReserve(self, 6, 4)
PrimRectXY(self, ax, ay, bx, by, col)
return
end
if bit32.band(flags, 496 --[[ImDrawFlags.RoundMask_]]) == 0 then
flags = bit32.bor(flags, 240 --[[ImDrawFlags.RoundAll]])
end
rounding = ImMin(rounding, ImAbs(width) * (((bit32.band(flags, 48 --[[ImDrawFlags.RoundTop]]) == 48 --[[ImDrawFlags.RoundTop]]) or (bit32.band(flags, 192 --[[ImDrawFlags.RoundBottom]]) == 192 --[[ImDrawFlags.RoundBottom]])) and 0.5 or 1.0) - 1.0)
rounding = ImMin(rounding, ImAbs(height) * (((bit32.band(flags, 80 --[[ImDrawFlags.RoundLeft]]) == 80 --[[ImDrawFlags.RoundLeft]]) or (bit32.band(flags, 160 --[[ImDrawFlags.RoundRight]]) == 160 --[[ImDrawFlags.RoundRight]])) and 0.5 or 1.0) - 1.0)
local s_rounding = ImTrunc64(rounding * self._InvFringeScale)
if s_rounding <= 0 then
PrimReserve(self, 6, 4)
PrimRectXY(self, ax, ay, bx, by, col)
return
end
-- Textured corners are baked with AA, do not use them if no-AA is requested.
local allow_tex_corners_mask = bit32.bor(4096 --[[ImDrawFlags.AAFill]], 2097152 --[[ImDrawFlags.UseTexForRoundCorners]], 8388608 --[[ImDrawFlags.AllowTexForRoundCorners_]])
local allow_tex_corners = bit32.band(flags, allow_tex_corners_mask) == allow_tex_corners_mask
if allow_tex_corners and s_rounding <= 16 --[[IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX]] then
-- IM_ASSERT_PARANOID((_Data->Font->OwnerAtlas->Flags & ImFontAtlasFlags_NoBakedRoundCorners) == 0) -- [Luau] not evaluated (arguments are not compiled out)
-- This is matching the baking calculations. The rounding clamping above makes sure that there's enough space for the extra pixel.
local corner_size = s_rounding + 1
-- Check that the baked corners do not overlap (this could be made more accurate if tested for x and y separately).
if corner_size * 2.0 * fringe_scale < min_dim then
local idx = (s_rounding - 1)
IM_ASSERT_PARANOID(idx >= 0 and idx < 16 --[[IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX]])
_AddRectFilledBakedXY(self, ax, ay, bx, by, col, corner_size * fringe_scale, self._Data.TexUvCorners[idx], flags)
return
end
end
end
if not has_rounding then
local points = prim_points
points[1].x = ax; points[1].y = ay
points[2].x = bx; points[2].y = ay
points[3].x = bx; points[3].y = by
points[4].x = ax; points[4].y = by
self:AddConvexPolyFilled(points, 4, col, 1024 --[[ImDrawFlags.JoinMiter]])
else
PathRectXY(self, ax, ay, bx, by, rounding, flags)
self:PathFillConvex(col, 1024 --[[ImDrawFlags.JoinMiter]])
end
end
-- p_min = upper-left, p_max = lower-right
-- Note: this does not support anti-aliasing when provided non-integer vertices.
--- @param p_min ImVec2
--- @param p_max ImVec2
--- @param col_upr_left ImU32
--- @param col_upr_right ImU32
--- @param col_bot_right ImU32
--- @param col_bot_left ImU32
function MT.ImDrawList:AddRectFilledMultiColor(p_min, p_max, col_upr_left, col_upr_right, col_bot_right, col_bot_left)
if bit32.band(bit32.bor(col_upr_left, col_upr_right, col_bot_right, col_bot_left), 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
local uv = self._Data.TexUvWhitePixel
PrimReserve(self, 6, 4)
-- [Luau] inline PrimWriteIdx()/PrimWriteVtx(): direct writes, no temporary ImVec2
local idx, idx_data, idx_write_ptr = self._VtxCurrentIdx, self.IdxBuffer.Data, self._IdxWritePtr
idx_data[idx_write_ptr] = 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
self._IdxWritePtr = idx_write_ptr + 6
local vtx_data, vtx_write_ptr = self.VtxBuffer.Buf, self._VtxWritePtr
WV(vtx_data, vtx_write_ptr + 0, p_min.x, p_min.y, uv.x, uv.y, col_upr_left)
WV(vtx_data, vtx_write_ptr + 1, p_max.x, p_min.y, uv.x, uv.y, col_upr_right)
WV(vtx_data, vtx_write_ptr + 2, p_max.x, p_max.y, uv.x, uv.y, col_bot_right)
WV(vtx_data, vtx_write_ptr + 3, p_min.x, p_max.y, uv.x, uv.y, col_bot_left)
self._VtxWritePtr = vtx_write_ptr + 4
self._VtxCurrentIdx = idx + 4
end
--- @param thickness? float
--- @param flags? ImDrawFlags
function MT.ImDrawList:AddQuad(p1, p2, p3, p4, col, thickness, flags)
if thickness == nil then thickness = 1.0 end
if flags == nil then flags = 0 end
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
--flags |= Flags; // Not needed.
local stroke_pos = _GetStrokePos(flags, 131072 --[[ImDrawFlags.StrokeInside]])
flags = bit32.bor(bit32.band(flags, bit32.bnot(917504 --[[ImDrawFlags.StrokeMask_]])), stroke_pos) -- As AddPolyline() default is different
local points = prim_points
ImVec2_Copy(points[1], p1); ImVec2_Copy(points[2], p2); ImVec2_Copy(points[3], p3); ImVec2_Copy(points[4], p4)
self:_AddPolyline(points, 4, col, thickness, bit32.bor(flags, 512 --[[ImDrawFlags.Closed]]), 3.4028234663853e+38 --[[FLT_MAX]])
end
function MT.ImDrawList:AddQuadFilled(p1, p2, p3, p4, col)
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
local points = prim_points
ImVec2_Copy(points[1], p1); ImVec2_Copy(points[2], p2); ImVec2_Copy(points[3], p3); ImVec2_Copy(points[4], p4)
self:AddConvexPolyFilled(points, 4, col)
end
--- @param thickness? float
--- @param flags? ImDrawFlags
function MT.ImDrawList:AddTriangle(p1, p2, p3, col, thickness, flags)
if thickness == nil then thickness = 1.0 end
if flags == nil then flags = 0 end
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
--flags |= Flags; // Not needed.
local stroke_pos = _GetStrokePos(flags, 131072 --[[ImDrawFlags.StrokeInside]])
flags = bit32.bor(bit32.band(flags, bit32.bnot(917504 --[[ImDrawFlags.StrokeMask_]])), stroke_pos) -- As AddPolyline() default is different
local points = prim_points
ImVec2_Copy(points[1], p1); ImVec2_Copy(points[2], p2); ImVec2_Copy(points[3], p3)
self:_AddPolyline(points, 3, col, thickness, bit32.bor(flags, 512 --[[ImDrawFlags.Closed]]), 3.4028234663853e+38 --[[FLT_MAX]])
end
function MT.ImDrawList:AddTriangleFilled(p1, p2, p3, col)
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
local points = prim_points
ImVec2_Copy(points[1], p1); ImVec2_Copy(points[2], p2); ImVec2_Copy(points[3], p3)
self:AddConvexPolyFilled(points, 3, col)
end
--- @param center ImVec2
--- @param radius float
--- @param col ImU32
--- @param num_segments? int
--- @param thickness? float
--- @param flags? ImDrawFlags
function MT.ImDrawList:AddCircle(center, radius, col, num_segments, thickness, flags)
if num_segments == nil then num_segments = 0 end
if thickness == nil then thickness = 1.0 end
if flags == nil then flags = 0 end
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
flags = bit32.bor(flags, self.Flags)
local stroke_pos = _GetStrokePos(flags, 131072 --[[ImDrawFlags.StrokeInside]])
if stroke_pos == 917504 --[[ImDrawFlags.StrokeLegacy]] then
radius = radius - 0.5
stroke_pos = 262144 --[[ImDrawFlags.StrokeCenter]]
end
radius = ImMax(0.01, radius)
local outer_radius = radius
if stroke_pos == 262144 --[[ImDrawFlags.StrokeCenter]] then
outer_radius = radius + thickness * 0.5
elseif stroke_pos == 393216 --[[ImDrawFlags.StrokeCenterBiased]] then
outer_radius = radius + _CalculateCenterBiasedOffset(self, thickness)
elseif stroke_pos == 131072 --[[ImDrawFlags.StrokeInside]] then
outer_radius = radius
elseif stroke_pos == 524288 --[[ImDrawFlags.StrokeOutside]] then
outer_radius = radius + thickness
end
if outer_radius <= thickness then
-- The circle has collapsed into a filled circle.
self:AddCircleFilled(center, outer_radius, col, num_segments)
return
end
if num_segments <= 0 and outer_radius <= self._Data.ArcFastRadiusCutoff then
-- Use arc with automatic segment count
local a_step = math.floor(48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] / self:_CalcCircleAutoSegmentCount(outer_radius)) -- Use outer_radius for segment count to be consistent with rounded rect.
_PathArcToFastExXY(self, center.x, center.y, radius, 0, 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]], a_step)
self._Path.Size = self._Path.Size - 1
else
-- Explicit segment count or above fast arc cutoff (still clamp to avoid drawing insanely tessellated shapes)
if num_segments <= 0 then
num_segments = self:_CalcCircleAutoSegmentCount(outer_radius)
end
num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX)
-- Because we are filling a closed shape we remove 1 from the count of segments/points
local a_max = (3.141592653589793 --[[IM_PI]] * 2.0) * (num_segments - 1.0) / num_segments
self:PathArcTo(center, radius, 0.0, a_max, num_segments - 1)
end
if self._Path.Size >= 3 then
-- Calculate distance to the edge of the polygon describing the circle.
-- This will be used by _AddPolyline() to clamp the inner stroke expansion, to avoid creating rendering artifacts.
local unit_apothem = ApproxApothem(self._Path.Size)
local edge_dist = radius * unit_apothem
self:_AddPolyline(self._Path.Data, self._Path.Size, col, thickness, bit32.bor(512 --[[ImDrawFlags.Closed]], 1024 --[[ImDrawFlags.JoinMiter]], stroke_pos), edge_dist)
end
self._Path.Size = 0
end
--- @param center ImVec2
--- @param radius float
--- @param col ImU32
--- @param num_segments? int
function MT.ImDrawList:AddCircleFilled(center, radius, col, num_segments)
if num_segments == nil then num_segments = 0 end
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
local diameter = radius * 2.0
if diameter < self._FringeScale then
if diameter < (1.0 / 255.0) * self._FringeScale then
return
end
col = ImAlphaMultiply(col, diameter * self._InvFringeScale)
radius = self._FringeScale * 0.5
end
if num_segments <= 0 and radius <= self._Data.ArcFastRadiusCutoff then
-- Use arc with automatic segment count
_PathArcToFastExXY(self, center.x, center.y, radius, 0, 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]], 0)
self._Path.Size = self._Path.Size - 1
else
-- Explicit segment count or above cutoff (still clamp to avoid drawing insanely tessellated shapes)
if num_segments <= 0 then
num_segments = self:_CalcCircleAutoSegmentCount(radius)
end
num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX)
-- Because we are filling a closed shape we remove 1 from the count of segments/points
local a_max = (3.141592653589793 --[[IM_PI]] * 2.0) * (num_segments - 1.0) / num_segments
self:PathArcTo(center, radius, 0.0, a_max, num_segments - 1)
end
self:PathFillConvex(col, 1024 --[[ImDrawFlags.JoinMiter]])
end
-- Guaranteed to honor 'num_segments'
--- @param thickness? float
--- @param flags? ImDrawFlags
function MT.ImDrawList:AddNgon(center, radius, col, num_segments, thickness, flags)
if thickness == nil then thickness = 1.0 end
if flags == nil then flags = 0 end
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 or num_segments <= 2 then
return
end
flags = bit32.bor(flags, self.Flags)
local stroke_pos = _GetStrokePos(flags, 131072 --[[ImDrawFlags.StrokeInside]])
if stroke_pos == 917504 --[[ImDrawFlags.StrokeLegacy]] then
radius = radius - 0.5
stroke_pos = 262144 --[[ImDrawFlags.StrokeCenter]]
end
radius = ImMax(0.01, radius)
-- Calculate distance to the edge of the polygon describing the circle.
-- This will be used by _AddPolyline() to clamp the inner stroke expansion, to avoid creating rendering artifacts.
local unit_apothem = ApproxApothem(num_segments)
local edge_dist = radius * unit_apothem
-- Check overestimate first before testing details.
if radius < thickness * 2.0 then
local outer_radius = radius
if stroke_pos == 262144 --[[ImDrawFlags.StrokeCenter]] then
outer_radius = radius + thickness * 0.5
elseif stroke_pos == 393216 --[[ImDrawFlags.StrokeCenterBiased]] then
outer_radius = radius + _CalculateCenterBiasedOffset(self, thickness)
elseif stroke_pos == 131072 --[[ImDrawFlags.StrokeInside]] then
outer_radius = radius
elseif stroke_pos == 524288 --[[ImDrawFlags.StrokeOutside]] then
outer_radius = radius + thickness
end
if outer_radius * unit_apothem <= thickness then
-- The polygon has collapsed into a filled polygon.
self:AddNgonFilled(center, outer_radius, col, num_segments)
return
end
end
-- Because we are filling a closed shape we remove 1 from the count of segments/points
local a_max = (3.141592653589793 --[[IM_PI]] * 2.0) * (num_segments - 1.0) / num_segments
self:PathArcTo(center, radius, 0.0, a_max, num_segments - 1)
self:_AddPolyline(self._Path.Data, self._Path.Size, col, thickness, bit32.bor(512 --[[ImDrawFlags.Closed]], 1024 --[[ImDrawFlags.JoinMiter]], stroke_pos), edge_dist)
self._Path.Size = 0
end
-- Guaranteed to honor 'num_segments'
function MT.ImDrawList:AddNgonFilled(center, radius, col, num_segments)
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 or num_segments <= 2 then
return
end
local diameter = radius * 2.0
if diameter < self._FringeScale then
if diameter < (1.0 / 255.0) * self._FringeScale then
return
end
col = ImAlphaMultiply(col, diameter * self._InvFringeScale)
radius = self._FringeScale * 0.5
end
-- Because we are filling a closed shape we remove 1 from the count of segments/points
local a_max = (3.141592653589793 --[[IM_PI]] * 2.0) * (num_segments - 1.0) / num_segments
self:PathArcTo(center, radius, 0.0, a_max, num_segments - 1)
self:PathFillConvex(col, 1024 --[[ImDrawFlags.JoinMiter]])
end
-- Ellipse
--- @param rot? float
--- @param num_segments? int
--- @param thickness? float
--- @param flags? ImDrawFlags
function MT.ImDrawList:AddEllipse(center, radius, col, rot, num_segments, thickness, flags)
if rot == nil then rot = 0.0 end
if num_segments == nil then num_segments = 0 end
if thickness == nil then thickness = 1.0 end
if flags == nil then flags = 0 end
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
local stroke_pos = _GetStrokePos(flags, 131072 --[[ImDrawFlags.StrokeInside]])
if stroke_pos == 917504 --[[ImDrawFlags.StrokeLegacy]] then
stroke_pos = 262144 --[[ImDrawFlags.StrokeCenter]]
end
local rad = ImVec2(radius.x, radius.y)
rad.x = ImMax(0.01, rad.x)
rad.y = ImMax(0.01, rad.y)
if num_segments <= 0 then
num_segments = self:_CalcCircleAutoSegmentCount(ImMax(rad.x, rad.y)) -- A bit pessimistic, maybe there's a better computation to do here.
end
self._Path:reserve(self._Path.Size + (num_segments + 1))
-- Because we are filling a closed shape we remove 1 from the count of segments/points
local a_max = 3.141592653589793 --[[IM_PI]] * 2.0 * (num_segments - 1.0) / num_segments
self:PathEllipticalArcTo(center, rad, rot, 0.0, a_max, num_segments - 1)
-- Prevent the inside part of the stroke to be expanded too much to prevent some rendering artifacts.
local max_inner_offset = ImMin(rad.x, rad.y)
self:_AddPolyline(self._Path.Data, self._Path.Size, col, thickness, bit32.bor(512 --[[ImDrawFlags.Closed]], 1024 --[[ImDrawFlags.JoinMiter]], stroke_pos), max_inner_offset)
self._Path.Size = 0
end
--- @param rot? float
--- @param num_segments? int
function MT.ImDrawList:AddEllipseFilled(center, radius, col, rot, num_segments)
if rot == nil then rot = 0.0 end
if num_segments == nil then num_segments = 0 end
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
local rad = ImVec2(radius.x, radius.y)
local diameter_x = rad.x * 2.0
local diameter_y = rad.y * 2.0
local min_dim = ImMin(diameter_x, diameter_y)
-- The ellipse is smaller than pixel in one dimension.
if min_dim < self._FringeScale then
if min_dim < (1.0 / 255.0) * self._FringeScale then
return
end
if diameter_x < self._FringeScale then
col = ImAlphaMultiply(col, diameter_x * self._InvFringeScale)
rad.x = self._FringeScale * 0.5
end
if diameter_y < self._FringeScale then
col = ImAlphaMultiply(col, diameter_y * self._InvFringeScale)
rad.y = self._FringeScale * 0.5
end
end
if num_segments <= 0 then
num_segments = self:_CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)) -- A bit pessimistic, maybe there's a better computation to do here.
end
-- Because we are filling a closed shape we remove 1 from the count of segments/points
local a_max = 3.141592653589793 --[[IM_PI]] * 2.0 * (num_segments - 1.0) / num_segments
self:PathEllipticalArcTo(center, radius, rot, 0.0, a_max, num_segments - 1) -- [Port] upstream passes 'radius' (not 'rad') here, kept as-is
self:PathFillConvex(col)
end
-- Cubic Bezier takes 4 controls points
--- @param num_segments? int
--- @param flags? ImDrawFlags
function MT.ImDrawList:AddBezierCubic(p1, p2, p3, p4, col, thickness, num_segments, flags)
if num_segments == nil then num_segments = 0 end
if flags == nil then flags = 0 end
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
self:PathLineTo(p1)
self:PathBezierCubicCurveTo(p2, p3, p4, num_segments)
self:PathStroke(col, thickness, flags)
end
-- Quadratic Bezier takes 3 controls points
--- @param num_segments? int
--- @param flags? ImDrawFlags
function MT.ImDrawList:AddBezierQuadratic(p1, p2, p3, col, thickness, num_segments, flags)
if num_segments == nil then num_segments = 0 end
if flags == nil then flags = 0 end
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
self:PathLineTo(p1)
self:PathBezierQuadraticCurveTo(p2, p3, num_segments)
self:PathStroke(col, thickness, flags)
end
--- @param font ImFont
--- @param font_size float
--- @param pos ImVec2
--- @param col ImU32
--- @param text string
--- @param text_begin? int # Defaults to 1
--- @param text_end int
--- @param wrap_width float
--- @param cpu_fine_clip_rect? ImVec4
local AddText_clip_rect = ImVec4()
@native
function MT.ImDrawList:AddText(font, font_size, pos, col, text, text_begin, text_end, wrap_width, cpu_fine_clip_rect)
-- Overload: AddText(pos, col, text, text_end?)
if type(font) == "table" and type(font[1]) == "number" then
font, font_size, pos, col, text, text_begin, text_end, wrap_width, cpu_fine_clip_rect = nil, 0.0, font, font_size, pos, 1, col, 0.0, nil
if text_end == nil then text_end = #text + 1 end
end
if text_begin == nil then text_begin = 1 end
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then return end
-- Accept null ranges
if text_begin == text_end then
return
end
-- No need to strlen() here: font:RenderText() will do it and may early out.
-- Pull default font/size from the shared ImDrawListSharedData instance
if font == nil then
font = self._Data.Font
end
if font_size == 0.0 then
font_size = self._Data.FontSize
end
local clip_rect = AddText_clip_rect -- Don't modify the clip rect! -- [Luau] scratch: ImFont:RenderText() only reads it
local src = self._CmdHeader.ClipRect
clip_rect.x = src.x; clip_rect.y = src.y; clip_rect.z = src.z; clip_rect.w = src.w
if (cpu_fine_clip_rect) then
clip_rect.x = math.max(clip_rect.x, cpu_fine_clip_rect.x)
clip_rect.y = math.max(clip_rect.y, cpu_fine_clip_rect.y)
clip_rect.z = math.min(clip_rect.z, cpu_fine_clip_rect.z)
clip_rect.w = math.min(clip_rect.w, cpu_fine_clip_rect.w)
end
font:RenderText(self, font_size, pos, col, clip_rect, text, text_begin, text_end, wrap_width, (cpu_fine_clip_rect ~= nil) and 1 --[[ImDrawTextFlags.CpuFineClip]] or 0 --[[ImDrawTextFlags.None]])
end
--- @param tex_ref ImTextureRef
--- @param p_min ImVec2
--- @param p_max ImVec2
--- @param uv_min? ImVec2
--- @param uv_max? ImVec2
--- @param col? ImU32
function MT.ImDrawList:AddImage(tex_ref, p_min, p_max, uv_min, uv_max, col)
if uv_min == nil then uv_min = ImVec2(0, 0) end
if uv_max == nil then uv_max = ImVec2(1, 1) end
if col == nil then col = 4294967295 --[[IM_COL32_WHITE]] end
if bit32.band(col, 4278190080 --[[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:PrimRectUV(p_min, p_max, uv_min, uv_max, col)
if push_texture_id then
self:PopTexture()
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 = 4294967295 --[[IM_COL32_WHITE]] end
if bit32.band(col, 4278190080 --[[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 p_min ImVec2
--- @param p_max ImVec2
--- @param uv_min ImVec2
--- @param uv_max ImVec2
--- @param col ImU32
--- @param rounding float
--- @param flags ImDrawFlags
function MT.ImDrawList:AddImageRounded(tex_ref, p_min, p_max, uv_min, uv_max, col, rounding, flags)
if bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
if flags == nil then flags = 0 end
assert(bit32.band(flags, 0x0F) == 0, "Misuse of legacy hardcoded ImDrawCornerFlags values!") -- If this assert triggers on legacy code: see comments in ImDrawList:PathRect().
if bit32.band(flags, 496 --[[ImDrawFlags.RoundMask_]]) == 0 then
flags = bit32.bor(flags, 240 --[[ImDrawFlags.RoundAll]])
end
if rounding < 0.5 or bit32.band(flags, 496 --[[ImDrawFlags.RoundMask_]]) == 256 --[[ImDrawFlags.RoundNone]] then
self:AddImage(tex_ref, p_min, p_max, uv_min, uv_max, col)
return
end
local push_texture_id = (tex_ref ~= self._CmdHeader.TexRef)
if push_texture_id then
self:PushTexture(tex_ref)
end
local vert_start_idx = self.VtxBuffer.Size + 1
self:PathRect(p_min, p_max, rounding, flags)
self:PathFillConvex(col, 1024 --[[ImDrawFlags.JoinMiter]])
local vert_end_idx = self.VtxBuffer.Size + 1
ImGui.ShadeVertsLinearUV(self, vert_start_idx, vert_end_idx, p_min, p_max, uv_min, uv_max, true)
if push_texture_id then
self:PopTexture()
end
end
--- @param radius float
function MT.ImDrawList:_CalcCircleAutoSegmentCount(radius)
-- Automatic segment count
local radius_idx = ImFloor(radius + 0.999) + 1 -- ceil to never reduce accuracy
if radius_idx >= 1 and radius_idx <= 64 then -- IM_COUNTOF(_Data->CircleSegmentCounts)
return self._Data.CircleSegmentCounts[radius_idx] -- Use cached value
else
return IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, self._Data.CircleTessellationMaxError)
end
end
-- Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui.PushClipRect() to affect logic (hit-testing and widget culling)
function MT.ImDrawList:PushClipRect(cr_min, cr_max, intersect_with_current_clip_rect)
local cr = ImVec4(cr_min.x, cr_min.y, cr_max.x, cr_max.y)
if intersect_with_current_clip_rect then
local current = self._CmdHeader.ClipRect
if cr.x < current.x then cr.x = current.x end -- = ClipWith(). Note that passing inverted range wouldn't be fixed here.
if cr.y < current.y then cr.y = current.y end
if cr.z > current.z then cr.z = current.z end
if cr.w > current.w then cr.w = current.w end
end
cr.z = math.max(cr.x, cr.z)
cr.w = math.max(cr.y, cr.w)
-- [Luau] inline self._ClipRectStack:push_back(cr)
local stack = self._ClipRectStack
local n = stack.Size + 1
if n > stack.Capacity then stack:push_back(cr) else stack.Data[n] = cr; stack.Size = n end
ImVec4_Copy(self._CmdHeader.ClipRect, cr)
self:_OnChangedClipRect()
end
function MT.ImDrawList:PushClipRectFullScreen()
local cr = self._Data.ClipRectFullscreen
self:PushClipRect(ImVec2(cr.x, cr.y), ImVec2(cr.z, cr.w))
end
function MT.ImDrawList:PopClipRect()
local stack = self._ClipRectStack
assert(stack.Size > 0)
stack.Size = stack.Size - 1 -- [Luau] inline pop_back()
if (stack.Size == 0) then -- LUA: No "Ternary Operator"
ImVec4_Copy(self._CmdHeader.ClipRect, self._Data.ClipRectFullscreen)
else
ImVec4_Copy(self._CmdHeader.ClipRect, stack.Data[stack.Size])
end
self:_OnChangedClipRect()
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 radius float
--- @param a_min_sample int
--- @param a_max_sample int
--- @param a_step int
-- [Luau] scalar center (cx, cy); assigned to the forward-declared local, _PathArcToFastEx() wraps it
@native
function _PathArcToFastExXY(self, cx, cy, radius, a_min_sample, a_max_sample, a_step)
if not (radius >= 0.25 * self._FringeScale) then -- !IM_HAS_ROUNDING(radius)
PathLineToXY(self, cx, cy)
return
end
-- Calculate arc auto segment step size
if a_step <= 0 then
-- Calculate step size using the common function. No need to clamp, the step is already in range.
local auto_seg_count = self:_CalcCircleAutoSegmentCount(radius)
a_step = CalcCornerArcStepAndCount(auto_seg_count)
else
-- Make sure we never do steps larger than one quarter of the circle
a_step = ImClamp(a_step, 1, math.floor(48 --[[IM_DRAWLIST_ARCFAST_TABLE_SIZE]] / 4))
end
local sample_range = ImAbs(a_max_sample - a_min_sample)
local a_next_step = a_step
local samples = sample_range + 1
local extra_max_sample = false
if a_step > 1 then
samples = math.floor(sample_range / a_step) + 1
local overstep = sample_range % a_step
if overstep > 0 then
extra_max_sample = true
samples = samples + 1
-- When we have overstep to avoid awkwardly looking one long line and one tiny one at the end,
-- distribute first step range evenly between them by reducing first step size.
if sample_range > 0 then
a_step = a_step - math.floor((a_step - overstep) / 2)
end
end
end
self._Path:resize(self._Path.Size + samples)
local out_ptr = self._Path.Size - samples + 1
local path_data, arc_fast_vtx = self._Path.Data, self._Data.ArcFastVtx -- [Luau] cache
local sample_index = a_min_sample
if sample_index < 0 or sample_index >= 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] then
sample_index = sample_index % 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]]
if sample_index < 0 then
sample_index = sample_index + 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]]
end
end
if a_max_sample >= a_min_sample then
local a = a_min_sample
while a <= a_max_sample do
-- a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more
if sample_index >= 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] then
sample_index = sample_index - 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]]
end
local s = arc_fast_vtx[sample_index + 1]
local d = path_data[out_ptr]
d.x = cx + s.x * radius; d.y = cy + s.y * radius
out_ptr = out_ptr + 1
a = a + a_step
sample_index = sample_index + a_step
a_step = a_next_step
end
else
local a = a_min_sample
while a >= a_max_sample do
-- a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more
if sample_index < 0 then
sample_index = sample_index + 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]]
end
local s = arc_fast_vtx[sample_index + 1]
local d = path_data[out_ptr]
d.x = cx + s.x * radius; d.y = cy + s.y * radius
out_ptr = out_ptr + 1
a = a - a_step
sample_index = sample_index - a_step
a_step = a_next_step
end
end
if extra_max_sample then
local normalized_max_sample = a_max_sample % 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]]
if normalized_max_sample < 0 then
normalized_max_sample = normalized_max_sample + 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]]
end
local s = arc_fast_vtx[normalized_max_sample + 1]
local d = path_data[out_ptr]
d.x = cx + s.x * radius; d.y = cy + s.y * radius
out_ptr = out_ptr + 1
end
IM_ASSERT_PARANOID(self._Path.Size == out_ptr - 1)
end
function MT.ImDrawList:_PathArcToFastEx(center, radius, a_min_sample, a_max_sample, a_step)
_PathArcToFastExXY(self, center.x, center.y, radius, a_min_sample, a_max_sample, a_step)
end
-- 0: East, 3: South, 6: West, 9: North, 12: East
@native
function MT.ImDrawList:PathArcToFast(center, radius, a_min_of_12, a_max_of_12)
if not (radius >= 0.25 * self._FringeScale) then -- !IM_HAS_ROUNDING(radius)
self._Path:push_back(center)
return
end
_PathArcToFastExXY(self, center.x, center.y, radius, a_min_of_12 * 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] / 12, a_max_of_12 * 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]] / 12, 0)
end
--- @param center ImVec2
--- @param radius float
--- @param a_min float
--- @param a_max float
--- @param num_segments int
function MT.ImDrawList:_PathArcToN(center, radius, a_min, a_max, num_segments)
if not (radius >= 0.25 * self._FringeScale) then -- !IM_HAS_ROUNDING(radius)
self._Path:push_back(center)
return
end
-- Note that we are adding a point at both a_min and a_max.
-- If you are trying to draw a full closed circle you don't want the overlapping points!
local path = self._Path
path:reserve(path.Size + (num_segments + 1))
-- [Luau] write into the reserved ImVec2 slots: no temporary ImVec2, no push_back() method lookups
local data, size = path.Data, path.Size
for i = 0, num_segments do
local a = a_min + (i / num_segments) * (a_max - a_min)
size = size + 1
local p = data[size]
p.x = center.x + ImCos(a) * radius; p.y = center.y + ImSin(a) * radius
end
path.Size = size
end
--- @param center ImVec2
--- @param radius float
--- @param a_min float
--- @param a_max float
--- @param num_segments? int
function MT.ImDrawList:PathArcTo(center, radius, a_min, a_max, num_segments)
if num_segments == nil then num_segments = 0 end
if not (radius >= 0.25 * self._FringeScale) then -- !IM_HAS_ROUNDING(radius)
self._Path:push_back(center)
return
end
if num_segments > 0 then
self:_PathArcToN(center, radius, a_min, a_max, num_segments)
return
end
-- Automatic segment count
if radius <= self._Data.ArcFastRadiusCutoff then
local a_is_reverse = a_max < a_min
-- Calculate number of segments per circle.
-- First we calculate the step count as usual, then match to the precomputed directions.
-- This will give us the spacing between the samples (step), and how many segments to use in total for a full circle.
local auto_seg_count = self:_CalcCircleAutoSegmentCount(radius)
local arc_step, arc_step_count = CalcCornerArcStepAndCount(auto_seg_count)
local arc_seg_count = arc_step_count * 4 -- Seg count for full circle.
-- We are going to use precomputed values for mid samples.
-- Determine first and last sample in lookup table that belong to the arc.
-- In first step we use the coarser segment count, and after rounding, convert back to the precomputed array index.
local a_min_sample_f = arc_seg_count * a_min / (3.141592653589793 --[[IM_PI]] * 2.0)
local a_max_sample_f = arc_seg_count * a_max / (3.141592653589793 --[[IM_PI]] * 2.0)
local a_min_sample = (a_is_reverse and ImFloor(a_min_sample_f) or ImCeil(a_min_sample_f)) * arc_step
local a_max_sample = (a_is_reverse and ImCeil(a_max_sample_f) or ImFloor(a_max_sample_f)) * arc_step
local a_mid_samples = a_is_reverse and ImMax(a_min_sample - a_max_sample, 0) or ImMax(a_max_sample - a_min_sample, 0)
local a_min_segment_angle = a_min_sample * 3.141592653589793 --[[IM_PI]] * 2.0 / 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]]
local a_max_segment_angle = a_max_sample * 3.141592653589793 --[[IM_PI]] * 2.0 / 48 --[[IM_DRAWLIST_ARCFAST_SAMPLE_MAX]]
local a_emit_start = ImAbs(a_min_segment_angle - a_min) >= 1e-5
local a_emit_end = ImAbs(a_max - a_max_segment_angle) >= 1e-5
self._Path:reserve(self._Path.Size + (a_mid_samples + 1 + (a_emit_start and 1 or 0) + (a_emit_end and 1 or 0)))
if a_emit_start then PathLineToXY(self, center.x + ImCos(a_min) * radius, center.y + ImSin(a_min) * radius) end -- [Luau] no temporary ImVec2
if a_mid_samples > 0 then _PathArcToFastExXY(self, center.x, center.y, radius, a_min_sample, a_max_sample, arc_step) end
if a_emit_end then PathLineToXY(self, center.x + ImCos(a_max) * radius, center.y + ImSin(a_max) * radius) end
else
local arc_length = ImAbs(a_max - a_min)
local circle_segment_count = self:_CalcCircleAutoSegmentCount(radius)
local arc_segment_count = ImMax(ImCeil(circle_segment_count * arc_length / (3.141592653589793 --[[IM_PI]] * 2.0)), 1)
self:_PathArcToN(center, radius, a_min, a_max, arc_segment_count)
end
end
-- Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
-- DO NOT CALL DIRECTLY THIS WILL CHANGE WILDLY IN 2026. Use ImDrawList:AddText().
--- @param draw_list ImDrawList
--- @param size float
--- @param pos ImVec2
--- @param col ImU32
--- @param clip_rect ImVec4
--- @param text ImString
--- @param text_begin int
--- @param text_end int
--- @param wrap_width? float
--- @param flags? ImDrawTextFlags
@native
function MT.ImFont:RenderText(draw_list, size, pos, col, clip_rect, text, text_begin, text_end, wrap_width, flags)
if wrap_width == nil then wrap_width = .0 end
if flags == nil then flags = 0 end
-- Align to be pixel perfect
local x = pos.x
local y = pos.y
if y > clip_rect.w then
return
end
flags = bit32.bor(flags, draw_list.Flags)
if bit32.band(flags, 1048576 --[[ImDrawFlags.TextNoPixelSnap]]) == 0 then
x = IM_TRUNC(x)
y = IM_TRUNC(y)
end
-- if not text_end then
-- end
local line_height = size
local baked = self:GetFontBaked(size)
local scale = size / baked.Size
local origin_x = x
local word_wrap_enabled = (wrap_width > 0.0)
-- Fast-forward to first visible line
local s = text_begin
if y + line_height < clip_rect.y then
while y + line_height < clip_rect.y and s < text_end do
local line_end = ImMemchr(text, '\n', s)
if word_wrap_enabled then
-- FIXME-OPT: This is not optimal as do first do a search for \n before calling CalcWordWrapPosition().
-- If the specs for CalcWordWrapPosition() were reworked to optionally return on \n we could combine both.
-- However it is still better than nothing performing the fast-forward!
s = ImFontCalcWordWrapPositionEx(self, size, text, s, (line_end ~= nil) and line_end or text_end, wrap_width, flags)
s = ImTextCalcWordWrapNextLineStart(text, s, text_end, flags)
else
s = (line_end ~= nil) and (line_end + 1) or text_end
end
y = y + line_height
end
end
-- For large text, scan for the last visible line in order to avoid over-reserving in the call to PrimReserve()
-- Note that very large horizontal line will still be affected by the issue (e.g. a one megabyte string buffer without a newline will likely crash atm)
if text_end - s > 1e4 and not word_wrap_enabled then
local s_end = s
local y_end = y
while (y_end < clip_rect.w and s_end < text_end) do
local _end = ImMemchr(text, '\n', s_end)
s_end = (_end ~= nil) and (_end + 1) or text_end
y_end = y_end + line_height
end
text_end = s_end
end
if s == text_end then
return
end
-- Reserve vertices for remaining worse case (over-reserving is useful and easily amortized)
local vtx_count_max = (text_end - s) * 4
local idx_count_max = (text_end - s) * 6
local idx_expected_size = draw_list.IdxBuffer.Size + idx_count_max
draw_list:PrimReserve(idx_count_max, vtx_count_max)
local vtx_write = draw_list._VtxWritePtr
local idx_write = draw_list._IdxWritePtr
local vtx_index = draw_list._VtxCurrentIdx
local cmd_count = draw_list.CmdBuffer.Size
local cpu_fine_clip = bit32.band(flags, 1 --[[ImDrawTextFlags.CpuFineClip]]) ~= 0
local idx_data = draw_list.IdxBuffer.Data; local vtx_data = draw_list.VtxBuffer.Buf
local color_untinted = bit32.bor(col, bit32.bnot(4278190080 --[[IM_COL32_A_MASK]]))
local word_wrap_eol
local stop = false
local text_is_str = type(text) == "string"
local lookup, lookup_n, glyphs = baked.IndexLookup.Data, baked.IndexLookup.Size, baked.Glyphs.Data
while s < text_end do
repeat
if word_wrap_enabled then
-- Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
if not word_wrap_eol then
word_wrap_eol = ImFontCalcWordWrapPositionEx(self, size, text, s, text_end, wrap_width - (x - origin_x), flags)
lookup, lookup_n, glyphs = baked.IndexLookup.Data, baked.IndexLookup.Size, baked.Glyphs.Data -- [Luau] may have loaded glyphs
end
if s >= word_wrap_eol then
x = origin_x
y = y + line_height
if y > clip_rect.w then
stop = true; break -- break out of main loop
end
word_wrap_eol = nil
s = ImTextCalcWordWrapNextLineStart(text, s, text_end, flags) -- Wrapping skips upcoming blanks
break
end
end
-- Decode and advance source
local c
if text_is_str then c = string.byte(text, s) else c = text[s] end
if c < 0x80 then
s = s + 1
else
local wanted, out_char = ImStd.ImTextCharFromUtf8(text, s, text_end)
c = out_char
s = s + wanted
end
if c < 32 then
if c == 10 then -- '\n'
x = origin_x
y = y + line_height
if y > clip_rect.w then
stop = true; break -- break out of main loop
end
break
end
if c == 13 then -- '\r'
break
end
end
local glyph
local gi = (c < lookup_n) and lookup[c + 1] or nil
if gi ~= nil and gi ~= IM_FONTGLYPH_INDEX_NOT_FOUND and gi ~= IM_FONTGLYPH_INDEX_UNUSED then
glyph = glyphs[gi]
else
glyph = baked:FindGlyph(c) -- may load the glyph and grow the tables
lookup, lookup_n, glyphs = baked.IndexLookup.Data, baked.IndexLookup.Size, baked.Glyphs.Data
end
local char_width = glyph.AdvanceX * scale
if glyph.Visible then
-- We don't do a second finer clipping test on the Y axis as we've already skipped anything before clip_rect.y and exit once we pass clip_rect.w
local x1 = x + glyph.X0 * scale
local x2 = x + glyph.X1 * scale
local y1 = y + glyph.Y0 * scale
local y2 = y + glyph.Y1 * scale
if x1 <= clip_rect.z and x2 >= clip_rect.x then
-- Render a character
local u1 = glyph.U0
local v1 = glyph.V0
local u2 = glyph.U1
local v2 = glyph.V1
-- CPU side clipping used to fit text in their frame when the frame is too small. Only does clipping for axis aligned quads.
if (cpu_fine_clip) then
if (x1 < clip_rect.x) then
u1 = u1 + (1.0 - (x2 - clip_rect.x) / (x2 - x1)) * (u2 - u1)
x1 = clip_rect.x
end
if (y1 < clip_rect.y) then
v1 = v1 + (1.0 - (y2 - clip_rect.y) / (y2 - y1)) * (v2 - v1)
y1 = clip_rect.y
end
if (x2 > clip_rect.z) then
u2 = u1 + ((clip_rect.z - x1) / (x2 - x1)) * (u2 - u1)
x2 = clip_rect.z
end
if (y2 > clip_rect.w) then
v2 = v1 + ((clip_rect.w - y1) / (y2 - y1)) * (v2 - v1)
y2 = clip_rect.w
end
if (y1 >= y2) then
x = x + char_width
break
end
end
-- Support for untinted glyphs
local glyph_col = glyph.Colored and color_untinted or col
-- We are NOT calling PrimRectUV() here because non-inlined causes too much overhead in a debug builds. Inlined here:
do
local o = (vtx_write - 1) * 36
writef64(vtx_data, o, x1); writef64(vtx_data, o + 8, y1); writef64(vtx_data, o + 16, u1); writef64(vtx_data, o + 24, v1); writeu32(vtx_data, o + 32, glyph_col)
writef64(vtx_data, o + 36, x2); writef64(vtx_data, o + 44, y1); writef64(vtx_data, o + 52, u2); writef64(vtx_data, o + 60, v1); writeu32(vtx_data, o + 68, glyph_col)
writef64(vtx_data, o + 72, x2); writef64(vtx_data, o + 80, y2); writef64(vtx_data, o + 88, u2); writef64(vtx_data, o + 96, v2); writeu32(vtx_data, o + 104, glyph_col)
writef64(vtx_data, o + 108, x1); writef64(vtx_data, o + 116, y2); writef64(vtx_data, o + 124, u1); writef64(vtx_data, o + 132, v2); writeu32(vtx_data, o + 140, glyph_col)
idx_data[idx_write + 0] = vtx_index; idx_data[idx_write + 1] = vtx_index + 1; idx_data[idx_write + 2] = vtx_index + 2;
idx_data[idx_write + 3] = vtx_index; idx_data[idx_write + 4] = vtx_index + 2; idx_data[idx_write + 5] = vtx_index + 3;
vtx_write = vtx_write + 4
vtx_index = vtx_index + 4
idx_write = idx_write + 6
end
end
end
x = x + char_width
until true
if stop then break end
end
-- Edge case: calling RenderText() with unloaded glyphs triggering texture change. It doesn't happen via ImGui.* calls because CalcTextSize() is always used.
if cmd_count ~= draw_list.CmdBuffer.Size then
assert(draw_list.CmdBuffer.Data[draw_list.CmdBuffer.Size].ElemCount == 0)
draw_list.CmdBuffer:pop_back()
draw_list:PrimUnreserve(idx_count_max, vtx_count_max)
draw_list:AddDrawCmd()
return self:RenderText(draw_list, size, pos, col, clip_rect, text, text_begin, text_end, wrap_width, flags) -- was `goto begin`
end
-- Give back unused vertices (clipped ones, blanks) ~ this is essentially a PrimUnreserve() action.
draw_list.VtxBuffer.Size = vtx_write - 1 -- Same as calling shrink()
draw_list.IdxBuffer.Size = idx_write - 1
draw_list.CmdBuffer.Data[draw_list.CmdBuffer.Size].ElemCount = draw_list.CmdBuffer.Data[draw_list.CmdBuffer.Size].ElemCount - (idx_expected_size - draw_list.IdxBuffer.Size)
draw_list._VtxWritePtr = vtx_write
draw_list._IdxWritePtr = idx_write
draw_list._VtxCurrentIdx = vtx_index
end
-------------------------------------------------------------------------------
-- [SECTION] ImGui Internal Render Helpers
-------------------------------------------------------------------------------
-- Vaguely redesigned to stop accessing ImGui global state:
-- - RenderArrow()
-- - RenderBullet()
-- - RenderCheckMark()
-- - RenderArrowPointingAt()
-- - RenderRectFilledInRangeH()
-- - RenderRectFilledWithHole()
-------------------------------------------------------------------------------
-- Function in need of a redesign (legacy mess)
-- - RenderColorRectWithAlphaCheckerboard()
-------------------------------------------------------------------------------
-- Render an arrow aimed to be aligned with text (p_min is a position in the same space text would be positioned). To e.g. denote expanded/collapsed state
--- @param draw_list ImDrawList
--- @param pos ImVec2
--- @param color ImU32
--- @param dir ImGuiDir
--- @param scale? float
local arrow_a, arrow_b, arrow_c = ImVec2(), ImVec2(), ImVec2() -- scratch: the path is consumed by AddTriangleFilled before returning
function ImGui.RenderArrow(draw_list, pos, color, dir, scale)
if scale == nil then scale = 1.0 end
local h = draw_list._Data.FontSize * 1.00
local r = h * 0.40 * scale
local cx, cy = pos.x + h * 0.50, pos.y + h * 0.50 * scale
local ax, ay, bx, by, qx, qy
if dir == 2 --[[ImGuiDir.Up]] or dir == 3 --[[ImGuiDir.Down]] then
if dir == 2 --[[ImGuiDir.Up]] then r = -r end
ax, ay = 0.000 * r, 0.750 * r
bx, by = -0.866 * r, -0.750 * r
qx, qy = 0.866 * r, -0.750 * r
elseif dir == 0 --[[ImGuiDir.Left]] or dir == 1 --[[ImGuiDir.Right]] then
if dir == 0 --[[ImGuiDir.Left]] then r = -r end
ax, ay = 0.750 * r, 0.000 * r
bx, by = -0.750 * r, 0.866 * r
qx, qy = -0.750 * r, -0.866 * r
else
assert(false)
end
arrow_a.x, arrow_a.y = cx + ax, cy + ay
arrow_b.x, arrow_b.y = cx + bx, cy + by
arrow_c.x, arrow_c.y = cx + qx, cy + qy
draw_list:AddTriangleFilled(arrow_a, arrow_b, arrow_c, color)
end
--- @param draw_list ImDrawList
--- @param pos ImVec2
--- @param col ImU32
function ImGui.RenderBullet(draw_list, pos, col)
-- FIXME-OPT: This should be baked in font now that it's easier.
local font_size = draw_list._Data.FontSize
draw_list:AddCircleFilled(pos, font_size * 0.20, col, (font_size < 22) and 8 or ((font_size < 40) and 12 or 0)) -- Hardcode optimal/nice tessellation threshold
end
--- @param draw_list ImDrawList
--- @param pos ImVec2
--- @param col ImU32
--- @param sz float
local checkmark_p = ImVec2() -- [Luau] scratch: PathLineTo() copies the point
function ImGui.RenderCheckMark(draw_list, pos, col, sz)
local thickness = math.max(sz / 5.0, 1.0)
sz = sz - thickness * 0.5
local pos_x, pos_y = pos.x + thickness * 0.25, pos.y + thickness * 0.25 -- [Luau] scalar pos
local third = sz / 3.0
local bx = pos_x + third
local by = pos_y + sz - third * 0.5
local p = checkmark_p
p.x = bx - third; p.y = by - third
draw_list:PathLineTo(p)
p.x = bx; p.y = by
draw_list:PathLineTo(p)
p.x = bx + third * 2.0; p.y = by - third * 2.0
draw_list:PathLineTo(p)
draw_list:PathStroke(col, thickness, 16384 --[[ImDrawFlags.AALineEnds]])
end
-- Render an arrow. 'pos' is position of the arrow tip. half_sz.x is length from base to tip. half_sz.y is length on each side.
--- @param draw_list ImDrawList
--- @param pos ImVec2
--- @param half_sz ImVec2
--- @param direction ImGuiDir
--- @param col ImU32
function ImGui.RenderArrowPointingAt(draw_list, pos, half_sz, direction, col)
if direction == 0 --[[ImGuiDir.Left]] then
draw_list:AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), pos, col)
elseif direction == 1 --[[ImGuiDir.Right]] then
draw_list:AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), pos, col)
elseif direction == 2 --[[ImGuiDir.Up]] then
draw_list:AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), pos, col)
elseif direction == 3 --[[ImGuiDir.Down]] then
draw_list:AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), pos, col)
elseif direction == (-1) --[[ImGuiDir.None]] or direction == 4 --[[ImGuiDir.COUNT]] then -- Fix warnings
end
end
local function ImAcos01(x)
if x <= 0.0 then return 3.141592653589793 --[[IM_PI]] * 0.5 end
if x >= 1.0 then return 0.0 end
return ImAcos(x)
--return (-0.69813170079773212 * x * x - 0.87266462599716477) * x + 1.5707963267948966 -- Cheap approximation, may be enough for what we do.
end
-- FIXME: Cleanup and move code to ImDrawList.
-- - Before 2025-12-04: RenderRectFilledRangeH() with 'float x_start_norm, float x_end_norm` <- normalized
-- - After 2025-12-04: RenderRectFilledInRangeH() with 'float x1, float x2' <- absolute coords!!
--- @param draw_list ImDrawList
--- @param rect ImRect
--- @param col ImU32
--- @param fill_x0 float
--- @param fill_x1 float
--- @param rounding float
function ImGui.RenderRectFilledInRangeH(draw_list, rect, col, fill_x0, fill_x1, rounding)
if fill_x0 > fill_x1 then
return
end
local p0 = ImVec2(fill_x0, rect.Min.y)
local p1 = ImVec2(fill_x1, rect.Max.y)
if rounding == 0.0 then
draw_list:AddRectFilled(p0, p1, col, 0.0)
return
end
rounding = ImClamp(ImMin((rect.Max.x - rect.Min.x) * 0.5, (rect.Max.y - rect.Min.y) * 0.5) - 1.0, 0.0, rounding)
local inv_rounding = 1.0 / rounding
local arc0_b = ImAcos01(1.0 - (p0.x - rect.Min.x) * inv_rounding)
local arc0_e = ImAcos01(1.0 - (p1.x - rect.Min.x) * inv_rounding)
local half_pi = 3.141592653589793 --[[IM_PI]] * 0.5 -- We will == compare to this because we know this is the exact value ImAcos01 can return.
local x0 = ImMax(p0.x, rect.Min.x + rounding)
if arc0_b == arc0_e then
draw_list:PathLineTo(ImVec2(x0, p1.y))
draw_list:PathLineTo(ImVec2(x0, p0.y))
elseif arc0_b == 0.0 and arc0_e == half_pi then
draw_list:PathArcToFast(ImVec2(x0, p1.y - rounding), rounding, 3, 6) -- BL
draw_list:PathArcToFast(ImVec2(x0, p0.y + rounding), rounding, 6, 9) -- TR
else
draw_list:PathArcTo(ImVec2(x0, p1.y - rounding), rounding, math.pi - arc0_e, math.pi - arc0_b) -- BL
draw_list:PathArcTo(ImVec2(x0, p0.y + rounding), rounding, math.pi + arc0_b, math.pi + arc0_e) -- TR
end
if p1.x > rect.Min.x + rounding then
local arc1_b = ImAcos01(1.0 - (rect.Max.x - p1.x) * inv_rounding)
local arc1_e = ImAcos01(1.0 - (rect.Max.x - p0.x) * inv_rounding)
local x1 = ImMin(p1.x, rect.Max.x - rounding)
if arc1_b == arc1_e then
draw_list:PathLineTo(ImVec2(x1, p0.y))
draw_list:PathLineTo(ImVec2(x1, p1.y))
elseif arc1_b == 0.0 and arc1_e == half_pi then
draw_list:PathArcToFast(ImVec2(x1, p0.y + rounding), rounding, 9, 12) -- TR
draw_list:PathArcToFast(ImVec2(x1, p1.y - rounding), rounding, 0, 3) -- BR
else
draw_list:PathArcTo(ImVec2(x1, p0.y + rounding), rounding, -arc1_e, -arc1_b) -- TR
draw_list:PathArcTo(ImVec2(x1, p1.y - rounding), rounding, arc1_b, arc1_e) -- BR
end
end
draw_list:PathFillConvex(col, 1024 --[[ImDrawFlags.JoinMiter]])
end
--- @param r_in ImRect
--- @param r_outer ImRect
--- @param threshold float
--- @return ImDrawFlags
function ImGui.CalcRoundingFlagsForRectInRect(r_in, r_outer, threshold)
local round_l = r_in.Min.x <= r_outer.Min.x + threshold
local round_r = r_in.Max.x >= r_outer.Max.x - threshold
local round_t = r_in.Min.y <= r_outer.Min.y + threshold
local round_b = r_in.Max.y >= r_outer.Max.y - threshold
local flags = 256 --[[ImDrawFlags.RoundNone]]
if round_t and round_l then
flags = bit32.bor(flags, 16 --[[ImDrawFlags.RoundTopLeft]])
end
if round_t and round_r then
flags = bit32.bor(flags, 32 --[[ImDrawFlags.RoundTopRight]])
end
if round_b and round_l then
flags = bit32.bor(flags, 64 --[[ImDrawFlags.RoundBottomLeft]])
end
if round_b and round_r then
flags = bit32.bor(flags, 128 --[[ImDrawFlags.RoundBottomRight]])
end
return flags
end
-- Helper for ColorPicker4()
-- NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that.
-- Spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding altogether.
--- @param draw_list ImDrawList
--- @param p_min ImVec2
--- @param p_max ImVec2
--- @param col ImU32
--- @param alpha float
--- @param grid_step float
--- @param grid_off ImVec2
--- @param rounding? float
--- @param flags? ImDrawFlags
function ImGui.RenderColorRectWithAlphaCheckerboard(draw_list, p_min, p_max, col, alpha, grid_step, grid_off, rounding, flags)
if rounding == nil then rounding = 0.0 end
if flags == nil then flags = 0 end
if bit32.band(flags, 496 --[[ImDrawFlags.RoundMask_]]) == 0 then
flags = 240 --[[ImDrawFlags.RoundAll]]
end
if bit32.rshift(bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]), 24 --[[IM_COL32_A_SHIFT]]) < 0xFF then
assert(alpha >= 0.0 and alpha <= 1.0)
local a_mask = bit32.lshift(ImTrunc(alpha * 255.0), 24 --[[IM_COL32_A_SHIFT]])
local col_bg1 = bit32.bor(bit32.band(ImStd.ImAlphaBlendColors(IM_COL32(128, 128, 128, 255), col), bit32.bnot(4278190080 --[[IM_COL32_A_MASK]])), a_mask)
local col_bg2 = bit32.bor(bit32.band(ImStd.ImAlphaBlendColors(IM_COL32(204, 204, 204, 255), col), bit32.bnot(4278190080 --[[IM_COL32_A_MASK]])), a_mask)
local dual_layer = (alpha == 1.0) -- Dual layer is faster but incorrect if blending.
if dual_layer then
draw_list:AddRectFilled(p_min, p_max, col_bg1, rounding, flags)
end
local yi = 0
local y = p_min.y + grid_off.y
while y < p_max.y do
local y1 = ImClamp(ImTrunc(y), p_min.y, p_max.y)
local y2 = ImMin(ImTrunc(y + grid_step), p_max.y)
if y2 > y1 then
local x_start_off = dual_layer and (((yi + 1) % 2) * grid_step) or 0.0
local x_step = dual_layer and (grid_step * 2.0) or grid_step
local xi = 0
local x = p_min.x + grid_off.x + x_start_off
while x < p_max.x do
local x1 = ImClamp(ImTrunc(x), p_min.x, p_max.x)
local x2 = ImMin(ImTrunc(x + grid_step), p_max.x)
if x2 > x1 then
local cell_flags = 256 --[[ImDrawFlags.RoundNone]] -- FIXME: Could use CalcRoundingFlagsForRectInRect()
if y1 <= p_min.y then
if x1 <= p_min.x then cell_flags = bit32.bor(cell_flags, 16 --[[ImDrawFlags.RoundTopLeft]]) end
if x2 >= p_max.x then cell_flags = bit32.bor(cell_flags, 32 --[[ImDrawFlags.RoundTopRight]]) end
end
if y2 >= p_max.y then
if x1 <= p_min.x then cell_flags = bit32.bor(cell_flags, 64 --[[ImDrawFlags.RoundBottomLeft]]) end
if x2 >= p_max.x then cell_flags = bit32.bor(cell_flags, 128 --[[ImDrawFlags.RoundBottomRight]]) end
end
-- Combine flags
if flags == 256 --[[ImDrawFlags.RoundNone]] or cell_flags == 256 --[[ImDrawFlags.RoundNone]] then
cell_flags = 256 --[[ImDrawFlags.RoundNone]]
else
cell_flags = bit32.band(cell_flags, flags)
end
local col_bg
if dual_layer then col_bg = col_bg2 elseif (yi + xi) % 2 == 1 then col_bg = col_bg2 else col_bg = col_bg1 end
draw_list:AddRectFilled(ImVec2(x1, y1), ImVec2(x2, y2), col_bg, rounding, cell_flags)
end
x = x + x_step
xi = xi + 1
end
end
y = y + grid_step
yi = yi + 1
end
else
draw_list:AddRectFilled(p_min, p_max, col, rounding, flags)
end
end
function ImGui.RenderRectFilledWithHole(draw_list, outer, inner, col, rounding)
local fill_L = inner.Min.x > outer.Min.x
local fill_R = inner.Max.x < outer.Max.x
local fill_U = inner.Min.y > outer.Min.y
local fill_D = inner.Max.y < outer.Max.y
local D = ImDrawFlags
local bor = bit32.bor
local function f(c, v) return c and 0 or v end
if fill_L then draw_list:AddRectFilled(ImVec2(outer.Min.x, inner.Min.y), ImVec2(inner.Min.x, inner.Max.y), col, rounding, bor(D.RoundNone, f(fill_U, D.RoundTopLeft), f(fill_D, D.RoundBottomLeft))) end
if fill_R then draw_list:AddRectFilled(ImVec2(inner.Max.x, inner.Min.y), ImVec2(outer.Max.x, inner.Max.y), col, rounding, bor(D.RoundNone, f(fill_U, D.RoundTopRight), f(fill_D, D.RoundBottomRight))) end
if fill_U then draw_list:AddRectFilled(ImVec2(inner.Min.x, outer.Min.y), ImVec2(inner.Max.x, inner.Min.y), col, rounding, bor(D.RoundNone, f(fill_L, D.RoundTopLeft), f(fill_R, D.RoundTopRight))) end
if fill_D then draw_list:AddRectFilled(ImVec2(inner.Min.x, inner.Max.y), ImVec2(inner.Max.x, outer.Max.y), col, rounding, bor(D.RoundNone, f(fill_L, D.RoundBottomLeft), f(fill_R, D.RoundBottomRight))) end
if fill_L and fill_U then draw_list:AddRectFilled(ImVec2(outer.Min.x, outer.Min.y), ImVec2(inner.Min.x, inner.Min.y), col, rounding, D.RoundTopLeft) end
if fill_R and fill_U then draw_list:AddRectFilled(ImVec2(inner.Max.x, outer.Min.y), ImVec2(outer.Max.x, inner.Min.y), col, rounding, D.RoundTopRight) end
if fill_L and fill_D then draw_list:AddRectFilled(ImVec2(outer.Min.x, inner.Max.y), ImVec2(inner.Min.x, outer.Max.y), col, rounding, D.RoundBottomLeft) end
if fill_R and fill_D then draw_list:AddRectFilled(ImVec2(inner.Max.x, inner.Max.y), ImVec2(outer.Max.x, outer.Max.y), col, rounding, D.RoundBottomRight) end
end
-------------------------------------------------------------------------------
-- Extra path functions (elliptical arc, bezier curves) and concave fill
-------------------------------------------------------------------------------
function MT.ImDrawList:PathEllipticalArcTo(center, radius, rot, a_min, a_max, num_segments)
if num_segments == nil or num_segments <= 0 then
num_segments = self:_CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)) -- A bit pessimistic, maybe there's a better computation to do here.
end
local cos_rot, sin_rot = ImCos(rot), ImSin(rot)
for i = 0, num_segments do
local a = a_min + (i / num_segments) * (a_max - a_min)
local px, py = ImCos(a) * radius.x, ImSin(a) * radius.y
self._Path:push_back(ImVec2(px * cos_rot - py * sin_rot + center.x, px * sin_rot + py * cos_rot + center.y))
end
end
function ImBezierCubicCalc(p1, p2, p3, p4, t)
local u = 1.0 - t
local w1, w2, w3, w4 = u * u * u, 3 * u * u * t, 3 * u * t * t, t * t * t
return ImVec2(w1 * p1.x + w2 * p2.x + w3 * p3.x + w4 * p4.x, w1 * p1.y + w2 * p2.y + w3 * p3.y + w4 * p4.y)
end
function ImBezierQuadraticCalc(p1, p2, p3, t)
local u = 1.0 - t
local w1, w2, w3 = u * u, 2 * u * t, t * t
return ImVec2(w1 * p1.x + w2 * p2.x + w3 * p3.x, w1 * p1.y + w2 * p2.y + w3 * p3.y)
end
-- Closely mimics ImBezierCubicClosestPointCasteljau() in imgui.cpp
local function PathBezierCubicCurveToCasteljau(path, x1, y1, x2, y2, x3, y3, x4, y4, tess_tol, level)
local dx, dy = x4 - x1, y4 - y1
local d2 = math.abs((x2 - x4) * dy - (y2 - y4) * dx)
local d3 = math.abs((x3 - x4) * dy - (y3 - y4) * dx)
if (d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy) then
path:push_back(ImVec2(x4, y4))
elseif level < 10 then
local x12, y12 = (x1 + x2) * 0.5, (y1 + y2) * 0.5
local x23, y23 = (x2 + x3) * 0.5, (y2 + y3) * 0.5
local x34, y34 = (x3 + x4) * 0.5, (y3 + y4) * 0.5
local x123, y123 = (x12 + x23) * 0.5, (y12 + y23) * 0.5
local x234, y234 = (x23 + x34) * 0.5, (y23 + y34) * 0.5
local x1234, y1234 = (x123 + x234) * 0.5, (y123 + y234) * 0.5
PathBezierCubicCurveToCasteljau(path, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1)
PathBezierCubicCurveToCasteljau(path, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1)
end
end
local function PathBezierQuadraticCurveToCasteljau(path, x1, y1, x2, y2, x3, y3, tess_tol, level)
local dx, dy = x3 - x1, y3 - y1
local det = (x2 - x3) * dy - (y2 - y3) * dx
if det * det * 4.0 < tess_tol * (dx * dx + dy * dy) then
path:push_back(ImVec2(x3, y3))
elseif level < 10 then
local x12, y12 = (x1 + x2) * 0.5, (y1 + y2) * 0.5
local x23, y23 = (x2 + x3) * 0.5, (y2 + y3) * 0.5
local x123, y123 = (x12 + x23) * 0.5, (y12 + y23) * 0.5
PathBezierQuadraticCurveToCasteljau(path, x1, y1, x12, y12, x123, y123, tess_tol, level + 1)
PathBezierQuadraticCurveToCasteljau(path, x123, y123, x23, y23, x3, y3, tess_tol, level + 1)
end
end
function MT.ImDrawList:PathBezierCubicCurveTo(p2, p3, p4, num_segments)
local p1 = self._Path:back()
if num_segments == nil or num_segments == 0 then
assert(self._Data.CurveTessellationMaxError > 0.0)
local max_error_sqr = (self._Data.CurveTessellationMaxError * self._FringeScale) * (self._Data.CurveTessellationMaxError * self._FringeScale)
PathBezierCubicCurveToCasteljau(self._Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, max_error_sqr, 0) -- Auto-tessellated
else
local t_step = 1.0 / num_segments
for i_step = 1, num_segments do self._Path:push_back(ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step)) end
end
end
function MT.ImDrawList:PathBezierQuadraticCurveTo(p2, p3, num_segments)
local p1 = self._Path:back()
if num_segments == nil or num_segments == 0 then
assert(self._Data.CurveTessellationMaxError > 0.0)
local max_error_sqr = (self._Data.CurveTessellationMaxError * self._FringeScale) * (self._Data.CurveTessellationMaxError * self._FringeScale)
PathBezierQuadraticCurveToCasteljau(self._Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, max_error_sqr, 0) -- Auto-tessellated
else
local t_step = 1.0 / num_segments
for i_step = 1, num_segments do self._Path:push_back(ImBezierQuadraticCalc(p1, p2, p3, t_step * i_step)) end
end
end
--------------------------------------------------------------------------------
-- [SECTION] ImTriangulator, ImDrawList concave polygon fill
--------------------------------------------------------------------------------
-- Triangulate concave polygons. Based on "Triangulation by Ear Clipping" paper, O(N^2) complexity.
-- Reference: https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
-- Provided as a convenience for user but not used by main library.
--------------------------------------------------------------------------------
-- - ImTriangulator [Internal]
-- - AddConcavePolyFilled()
--------------------------------------------------------------------------------
local ImTriangulatorNodeType =
{
Convex = 0,
Ear = 1,
Reflex = 2,
}
-- [Luau] No pointers: a node is an int handle (1-based slot) into the parallel arrays of its ImTriangulator
-- (tri.Type[n], tri.Index[n], tri.Pos[n], tri.Next[n], tri.Prev[n]). node->Index (0-based, as upstream) == n - 1.
local function ImTriangulatorNode_Unlink(tri, n) tri.Prev[tri.Next[n]] = tri.Prev[n]; tri.Next[tri.Prev[n]] = tri.Next[n] end
-- [Luau] Deviation: upstream classifies a node as reflex when '!ImTriangleIsClockwise()', i.e. also when its corner is
-- degenerate (collinear or duplicate points). A duplicate point then sits as a reflex on top of every candidate ear's vertex
-- and blocks it, ending in the degenerate fallback with overlapping triangles. Only strictly counter-clockwise corners are
-- reflex here: degenerate corners are clipped as zero-area ears.
local function ImTriangulator_IsReflex(a, b, c) return ((b.x - a.x) * (c.y - b.y)) - ((c.x - b.x) * (b.y - a.y)) < 0.0 end
local function ImTriangulatorNodeSpan()
return { Data = {}, Size = 0 } -- Data: node handles, 1-based
end
MT.ImTriangulatorNodeSpan = {}
MT.ImTriangulatorNodeSpan.__index = MT.ImTriangulatorNodeSpan
function MT.ImTriangulatorNodeSpan:push_back(node) self.Size = self.Size + 1; self.Data[self.Size] = node end
function MT.ImTriangulatorNodeSpan:find_erase_unsorted(idx) for i = self.Size, 1, -1 do if self.Data[i] - 1 == idx then self.Data[i] = self.Data[self.Size]; self.Size = self.Size - 1; return end end end
-- [Luau] The node/ears/reflexes storage (upstream: carved out of _Data->TempBuffer via scratch_buffer) is owned by the
-- triangulator itself and only ever grows. AddConcavePolyFilled() reuses a single module-level instance (no per-call allocations).
local function ImTriangulator()
return setmetatable({
_TrianglesLeft = 0,
_Nodes = 0, -- node handle (head of the circular list)
_Ears = setmetatable(ImTriangulatorNodeSpan(), MT.ImTriangulatorNodeSpan),
_Reflexes = setmetatable(ImTriangulatorNodeSpan(), MT.ImTriangulatorNodeSpan),
Type = {}, Index = {}, Pos = {}, Next = {}, Prev = {}, -- [Luau] ImTriangulatorNode fields, one array each
}, MT.ImTriangulator)
end
MT.ImTriangulator = {}
MT.ImTriangulator.__index = MT.ImTriangulator
function MT.ImTriangulator.EstimateTriangleCount(points_count) return (points_count < 3) and 0 or points_count - 2 end
-- [Luau] In elements (nodes + ears + reflexes) rather than bytes. Unused by the port: arrays grow on demand.
function MT.ImTriangulator.EstimateScratchBufferSize(points_count) return points_count + points_count * 2 end
-- Distribute storage for nodes, ears and reflexes.
-- FIXME-OPT: if everything is convex, we could report it to caller and let it switch to an convex renderer
-- (this would require first building reflexes to bail to convex if empty, without even building nodes)
--- @param points ImVec2[] # 1-based
function MT.ImTriangulator:Init(points, points_count)
assert(points_count >= 3)
self._TrianglesLeft = MT.ImTriangulator.EstimateTriangleCount(points_count)
self._Nodes = 1
self._Ears.Size = 0
self._Reflexes.Size = 0
self:BuildNodes(points, points_count)
-- [Luau] Deviation: upstream only flips the node list when no ear is found, so a counter-clockwise *concave* polygon
-- (whose reflex corners look like ears) yields triangles outside of it. Normalize to clockwise order up-front (O(N)).
local area2 = 0.0
local p0 = points[points_count]
for i = 1, points_count do
local p1 = points[i]
area2 = area2 + (p0.x * p1.y - p1.x * p0.y)
p0 = p1
end
if area2 < 0.0 then
self:FlipNodeList()
end
self:BuildReflexes()
self:BuildEars()
end
function MT.ImTriangulator:BuildNodes(points, points_count)
local Type, Index, Pos, Next, Prev = self.Type, self.Index, self.Pos, self.Next, self.Prev
for i = 1, points_count do
Type[i] = ImTriangulatorNodeType.Convex
Index[i] = i - 1
Pos[i] = points[i] -- [Luau] reference, not a copy (points are not modified while triangulating)
Next[i] = i + 1
Prev[i] = i - 1
end
Prev[1] = points_count
Next[points_count] = 1
end
function MT.ImTriangulator:BuildReflexes()
local Pos, Next, Prev = self.Pos, self.Next, self.Prev
local n1 = self._Nodes
for _ = self._TrianglesLeft, 0, -1 do
if ImTriangulator_IsReflex(Pos[Prev[n1]], Pos[n1], Pos[Next[n1]]) then
self.Type[n1] = ImTriangulatorNodeType.Reflex
self._Reflexes:push_back(n1)
end
n1 = Next[n1]
end
end
function MT.ImTriangulator:BuildEars()
local Index, Pos, Next, Prev = self.Index, self.Pos, self.Next, self.Prev
local n1 = self._Nodes
for _ = self._TrianglesLeft, 0, -1 do
if self.Type[n1] == ImTriangulatorNodeType.Convex then
local n0, n2 = Prev[n1], Next[n1]
if self:IsEar(Index[n0], Index[n1], Index[n2], Pos[n0], Pos[n1], Pos[n2]) then
self.Type[n1] = ImTriangulatorNodeType.Ear
self._Ears:push_back(n1)
end
end
n1 = Next[n1]
end
end
-- [Luau] out_triangle[3] is returned as 3 values (relative 0-based indexes)
function MT.ImTriangulator:GetNextTriangle()
if self._Ears.Size == 0 then
self:FlipNodeList()
local node = self._Nodes
for _ = self._TrianglesLeft, 0, -1 do
self.Type[node] = ImTriangulatorNodeType.Convex
node = self.Next[node]
end
self._Reflexes.Size = 0
self:BuildReflexes()
self:BuildEars()
-- If we still don't have ears, it means geometry is degenerated.
if self._Ears.Size == 0 then
-- Return first triangle available, mimicking the behavior of convex fill.
assert(self._TrianglesLeft > 0) -- Geometry is degenerated
self._Ears.Data[1] = self._Nodes
self._Ears.Size = 1
end
end
local ear = self._Ears.Data[self._Ears.Size]
self._Ears.Size = self._Ears.Size - 1
local Index, Next, Prev = self.Index, self.Next, self.Prev
local t0, t1, t2 = Index[Prev[ear]], Index[ear], Index[Next[ear]]
ImTriangulatorNode_Unlink(self, ear)
if ear == self._Nodes then
self._Nodes = Next[ear]
end
self:ReclassifyNode(Prev[ear])
self:ReclassifyNode(Next[ear])
self._TrianglesLeft = self._TrianglesLeft - 1
return t0, t1, t2
end
function MT.ImTriangulator:FlipNodeList()
local Next, Prev = self.Next, self.Prev
local nodes = self._Nodes
local prev = nodes
local temp = nodes
local current = Next[nodes]
Next[prev] = prev
Prev[prev] = prev
while current ~= nodes do
temp = Next[current]
Next[current] = prev
Prev[prev] = current
Next[nodes] = current
Prev[current] = nodes
prev = current
current = temp
end
self._Nodes = prev
end
-- A triangle is an ear is no other vertex is inside it. We can test reflexes vertices only (see reference algorithm)
function MT.ImTriangulator:IsEar(i0, i1, i2, v0, v1, v2)
local Index, Pos = self.Index, self.Pos
local data = self._Reflexes.Data
for p = 1, self._Reflexes.Size do
local reflex = data[p]
local idx = Index[reflex]
if idx ~= i0 and idx ~= i1 and idx ~= i2 then
if ImStd.ImTriangleContainsPoint(v0, v1, v2, Pos[reflex]) then
return false
end
end
end
return true
end
function MT.ImTriangulator:ReclassifyNode(n1)
-- Classify node
local type
local n0 = self.Prev[n1]
local n2 = self.Next[n1]
local Index, Pos = self.Index, self.Pos
if ImTriangulator_IsReflex(Pos[n0], Pos[n1], Pos[n2]) then
type = ImTriangulatorNodeType.Reflex
elseif self:IsEar(Index[n0], Index[n1], Index[n2], Pos[n0], Pos[n1], Pos[n2]) then
type = ImTriangulatorNodeType.Ear
else
type = ImTriangulatorNodeType.Convex
end
-- Update lists when a type changes
local n1_type = self.Type[n1]
if type == n1_type then
return
end
if n1_type == ImTriangulatorNodeType.Reflex then
self._Reflexes:find_erase_unsorted(Index[n1])
elseif n1_type == ImTriangulatorNodeType.Ear then
self._Ears:find_erase_unsorted(Index[n1])
end
if type == ImTriangulatorNodeType.Reflex then
self._Reflexes:push_back(n1)
elseif type == ImTriangulatorNodeType.Ear then
self._Ears:push_back(n1)
end
self.Type[n1] = type
end
local concave_triangulator = ImTriangulator() -- [Luau] reused scratch (see ImTriangulator())
-- Use ear-clipping algorithm to triangulate a simple polygon (no self-interaction, no holes).
-- (Reminder: we don't perform any coarse clipping/culling in ImDrawList layer!
-- It is up to caller to ensure not making costly calls that will be outside of visible area.
-- As concave fill is noticeably more expensive than other primitives, be mindful of this...
-- Caller can build AABB of points, and avoid filling if 'draw_list->_CmdHeader.ClipRect.Overlays(points_bb) == false')
--- @param points ImVec2[] # 1-based
--- @param points_count int
--- @param col ImU32
@native
function MT.ImDrawList:AddConcavePolyFilled(points, points_count, col)
if points_count < 3 or bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
local uv = self._Data.TexUvWhitePixel
local uv_x, uv_y = uv.x, uv.y
local triangulator = concave_triangulator
if bit32.band(self.Flags, 4096 --[[ImDrawFlags.AAFill]]) ~= 0 then
-- Anti-aliased Fill
local AA_SIZE = self._FringeScale
local col_trans = bit32.band(col, bit32.bnot(4278190080 --[[IM_COL32_A_MASK]]))
local idx_count = (points_count - 2) * 3 + points_count * 6
local vtx_count = (points_count * 2)
PrimReserve(self, idx_count, vtx_count)
local vb = self.VtxBuffer.Buf
local ib = self.IdxBuffer.Data
local vw = self._VtxWritePtr
local iw = self._IdxWritePtr
-- Add indexes for fill
local vtx_inner_idx = self._VtxCurrentIdx
local vtx_outer_idx = self._VtxCurrentIdx + 1
triangulator:Init(points, points_count)
while triangulator._TrianglesLeft > 0 do
local t0, t1, t2 = triangulator:GetNextTriangle()
ib[iw + 0] = vtx_inner_idx + t0 * 2; ib[iw + 1] = vtx_inner_idx + t1 * 2; ib[iw + 2] = vtx_inner_idx + t2 * 2
iw = iw + 3
end
-- Compute normals
-- [Luau] scratch arrays (see AddPolyline) instead of _Data->TempBuffer. Indexes below are 1-based.
local temp_normals_x, temp_normals_y = poly_normals_x, poly_normals_y
local i0 = points_count
for i1 = 1, points_count do
local p0 = points[i0]
local p1 = points[i1]
local dx, dy = IM_NORMALIZE2F_OVER_ZERO(p1.x - p0.x, p1.y - p0.y)
temp_normals_x[i0] = dy
temp_normals_y[i0] = -dx
i0 = i1
end
i0 = points_count
for i1 = 1, points_count do
-- Average normals
local dm_x, dm_y = IM_FIXNORMAL2F((temp_normals_x[i0] + temp_normals_x[i1]) * 0.5, (temp_normals_y[i0] + temp_normals_y[i1]) * 0.5)
dm_x = dm_x * AA_SIZE * 0.5
dm_y = dm_y * AA_SIZE * 0.5
-- Add vertices
local p1 = points[i1]
WV(vb, vw, p1.x - dm_x, p1.y - dm_y, uv_x, uv_y, col) -- Inner
WV(vb, vw + 1, p1.x + dm_x, p1.y + dm_y, uv_x, uv_y, col_trans) -- Outer
vw = vw + 2
-- Add indexes for fringes
local e0, e1 = (i0 - 1) * 2, (i1 - 1) * 2
ib[iw + 0] = vtx_inner_idx + e1; ib[iw + 1] = vtx_inner_idx + e0; ib[iw + 2] = vtx_outer_idx + e0
ib[iw + 3] = vtx_outer_idx + e0; ib[iw + 4] = vtx_outer_idx + e1; ib[iw + 5] = vtx_inner_idx + e1
iw = iw + 6
i0 = i1
end
self._VtxWritePtr = vw
self._IdxWritePtr = iw
self._VtxCurrentIdx = self._VtxCurrentIdx + vtx_count
else
-- Non Anti-aliased Fill
local idx_count = (points_count - 2) * 3
local vtx_count = points_count
PrimReserve(self, idx_count, vtx_count)
local vb = self.VtxBuffer.Buf
local ib = self.IdxBuffer.Data
local vw = self._VtxWritePtr
local iw = self._IdxWritePtr
for i = 1, vtx_count do
local p = points[i]
WV(vb, vw, p.x, p.y, uv_x, uv_y, col)
vw = vw + 1
end
local base_idx = self._VtxCurrentIdx
triangulator:Init(points, points_count)
while triangulator._TrianglesLeft > 0 do
local t0, t1, t2 = triangulator:GetNextTriangle()
ib[iw + 0] = base_idx + t0; ib[iw + 1] = base_idx + t1; ib[iw + 2] = base_idx + t2
iw = iw + 3
end
self._VtxWritePtr = vw
self._IdxWritePtr = iw
self._VtxCurrentIdx = base_idx + vtx_count
end
end
function MT.ImDrawList:PathFillConcave(col)
self:AddConcavePolyFilled(self._Path.Data, self._Path.Size, col)
self._Path.Size = 0
end
-------------------------------------------------------------------------------
-- [SECTION] Decompression code
-------------------------------------------------------------------------------
-- Compressed with stb_compress() then converted to a C array and encoded as base85.
-- Use the program in misc/fonts/binary_to_compressed_c.cpp to create the array from a TTF file.
-- The purpose of encoding as base85 instead of "0x00,0x01,..." style is only save on _source code_ size.
-- Decompression from stb.h (public domain) by Sean Barrett https://github.com/nothings/stb/blob/master/stb.h
-- [Luau] Input and output are buffers, pointers are 0-based byte offsets into them.
-------------------------------------------------------------------------------
do
local readu8 = buffer.readu8
local writeu8 = buffer.writeu8
--- @param input buffer
function stb_decompress_length(input)
return readu8(input, 8) * 16777216 + readu8(input, 9) * 65536 + readu8(input, 10) * 256 + readu8(input, 11)
end
local stb__barrier_out_e, stb__barrier_out_b
local stb__barrier_in_b
local stb__dout
local stb__out, stb__in -- [Luau] buffers the offsets above point into
local function stb__match(data, length)
-- INVERSE of memmove... write each byte before copying the next...
assert(stb__dout + length <= stb__barrier_out_e)
if stb__dout + length > stb__barrier_out_e then stb__dout = stb__dout + length; return end
if data < stb__barrier_out_b then stb__dout = stb__barrier_out_e + 1; return end
if stb__dout - data >= length then -- [Luau] no overlap: block copy
buffer.copy(stb__out, stb__dout, stb__out, data, length)
stb__dout = stb__dout + length
return
end
local out = stb__out
local dout = stb__dout
for n = 0, length - 1 do writeu8(out, dout + n, readu8(out, data + n)) end
stb__dout = dout + length
end
local function stb__lit(data, length)
assert(stb__dout + length <= stb__barrier_out_e)
if stb__dout + length > stb__barrier_out_e then stb__dout = stb__dout + length; return end
if data < stb__barrier_in_b then stb__dout = stb__barrier_out_e + 1; return end
buffer.copy(stb__out, stb__dout, stb__in, data, length)
stb__dout = stb__dout + length
end
local function stb__in2(i, x) return readu8(stb__in, i + x) * 256 + readu8(stb__in, i + x + 1) end
local function stb__in3(i, x) return readu8(stb__in, i + x) * 65536 + stb__in2(i, x + 1) end
local function stb__in4(i, x) return readu8(stb__in, i + x) * 16777216 + stb__in3(i, x + 1) end
local function stb_decompress_token(i)
local i0 = readu8(stb__in, i)
if i0 >= 0x20 then -- use fewer if's for cases that expand small
if i0 >= 0x80 then stb__match(stb__dout - readu8(stb__in, i + 1) - 1, i0 - 0x80 + 1); i = i + 2
elseif i0 >= 0x40 then stb__match(stb__dout - (stb__in2(i, 0) - 0x4000 + 1), readu8(stb__in, i + 2) + 1); i = i + 3
else --[[ *i >= 0x20 ]] stb__lit(i + 1, i0 - 0x20 + 1); i = i + 1 + (i0 - 0x20 + 1)
end
else -- more ifs for cases that expand large, since overhead is amortized
if i0 >= 0x18 then stb__match(stb__dout - (stb__in3(i, 0) - 0x180000 + 1), readu8(stb__in, i + 3) + 1); i = i + 4
elseif i0 >= 0x10 then stb__match(stb__dout - (stb__in3(i, 0) - 0x100000 + 1), stb__in2(i, 3) + 1); i = i + 5
elseif i0 >= 0x08 then stb__lit(i + 2, stb__in2(i, 0) - 0x0800 + 1); i = i + 2 + (stb__in2(i, 0) - 0x0800 + 1)
elseif i0 == 0x07 then stb__lit(i + 3, stb__in2(i, 1) + 1); i = i + 3 + (stb__in2(i, 1) + 1)
elseif i0 == 0x06 then stb__match(stb__dout - (stb__in3(i, 1) + 1), readu8(stb__in, i + 4) + 1); i = i + 5
elseif i0 == 0x04 then stb__match(stb__dout - (stb__in3(i, 1) + 1), stb__in2(i, 4) + 1); i = i + 6
end
end
return i
end
--- @param buf buffer
--- @param buflen int
local function stb_adler32(adler32, buf, buflen)
local ADLER_MOD = 65521
local s1 = adler32 % 65536
local s2 = math.floor(adler32 / 65536)
local blocklen = buflen % 5552
local p = 0
while buflen > 0 do
for _ = 1, blocklen do
s1 = s1 + readu8(buf, p); s2 = s2 + s1
p = p + 1
end
s1 = s1 % ADLER_MOD; s2 = s2 % ADLER_MOD
buflen = buflen - blocklen
blocklen = 5552
end
return s2 * 65536 + s1
end
--- @param output buffer
--- @param i buffer
function stb_decompress(output, i, length)
stb__in = i
if stb__in4(0, 0) ~= 0x57bC0000 then return 0 end
if stb__in4(0, 4) ~= 0 then return 0 end -- error! stream is > 4GB
local olen = stb_decompress_length(i)
stb__barrier_in_b = 0
stb__barrier_out_e = olen
stb__barrier_out_b = 0
stb__out = output
local p = 16
stb__dout = 0
while true do
local old_p = p
p = stb_decompress_token(p)
if p == old_p then
if readu8(i, p) == 0x05 and readu8(i, p + 1) == 0xfa then
assert(stb__dout == olen)
if stb__dout ~= olen then return 0 end
if stb_adler32(1, output, olen) ~= stb__in4(p, 2) then
return 0
end
return olen
else
assert(false) -- NOTREACHED
return 0
end
end
assert(stb__dout <= olen)
if stb__dout > olen then
return 0
end
end
end
end
return true -- [Roblox] ModuleScripts must return exactly one value
end
__FILES['imgui_h.lua'] = function()
-- dear imgui, v1.93.0 WIP
-- (headers)
-- Help:
-- - Call and read ImGui.ShowDemoWindow() in imgui_demo.lua. All applications in examples/ are doing that.
-- - Read top of imgui.lua for more details, links and comments.
-- Resources:
-- - FAQ ........................ https://dearimgui.com/faq
-- - Homepage ................... https://github.com/ocornut/imgui
-- - Releases & Changelog ....... https://github.com/ocornut/imgui/releases
-- - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there)
-- - Issues & support ........... https://github.com/ocornut/imgui/issues
-- Index of this file:
-- [SECTION] Forward declarations and basic types
-- [SECTION] METATABLE MANAGEMENT
-- [SECTION] Helpers: Debug log, memory allocations macros, ImVector<>
-- [SECTION] Drawing API (ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawList, ImDrawData)
-- [SECTION] Texture API (ImTextureFormat, ImTextureStatus, ImTextureRect, ImTextureData)
-- [SECTION] Texture identifiers (ImTextureID, ImTextureRef)
-- [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFontBaked, ImFont)
-- [SECTION] ImGuiIO
-- [SECTION] Viewports
-- [SECTION] ImGuiPlatformIO + other Platform Dependent Interfaces (ImGuiPlatformMonitor, ImGuiPlatformImeData)
-- [SECTION] Flags & Enumerations
-- [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload)
-- [SECTION] Tables API flags and structures (ImGuiTableFlags, ImGuiTableColumnFlags, ImGuiTableRowFlags, ImGuiTableBgTarget, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs)
-- [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor)
--- @meta
local type = type -- [Luau] builtin (FASTCALL); ImGui._GetTypeFunc() kept for compatibility
local rawget = rawget; local rawset = rawset
-- [LuaBitOp](https://bitop.luajit.org/semantics.html)
local bitAnd = bit32.band; local bitOr = bit32.bor
local bitLShift = bit32.lshift
-------------------------------------------------------------------------------
-- [SECTION] Forward declarations and basic types
-------------------------------------------------------------------------------
-- Scalar data types
--- @class char : integer
--- @class unsigned_char : integer
--- @alias ImS8 char
--- @alias ImU8 unsigned_char
--- @class ImU16 : integer
--- @class ImS16 : integer
--- @class ImU32 : integer
--- @class ImS32 : integer
--- @param val number
--- @return ImU8
function ImU8(val) return bitAnd(val, 0xFF) end
--- @param val number
--- @return ImS8
function ImS8(val) return bitAnd(val, 0xFF) - (bitAnd(val, 0x80) ~= 0 and 0x100 or 0) end
--- @param val number
--- @return ImU16
function ImU16(val) return bitAnd(val, 0xFFFF) end
--- @param val number
--- @return ImS16
function ImS16(val) return bitAnd(val, 0xFFFF) - (bitAnd(val, 0x8000) ~= 0 and 0x10000 or 0) end
--- @param val number
--- @return ImU32
function ImU32(val) return bitAnd(val, 0xFFFFFFFF) end
--- @param val number
--- @return ImS32
function ImS32(val) return bitAnd(val, 0xFFFFFFFF) end
--- @alias float number
--- @class double : number
--- @alias int integer
--- @alias unsigned_int integer
--- @alias short integer
--- @alias unsigned_short integer
--- @alias size_t unsigned_int
-- Character types
-- (we generally use UTF-8 encoded string in the API. This is storage specifically for a decoded character used for keyboard input and display)
--- @alias ImWchar16 unsigned_short
--- @alias ImWchar ImWchar16
--- @alias bool boolean
-- A unique ID used by widgets (typically the result of hashing a stack of string)
--- @alias ImGuiID unsigned_int
-- ImTextureID = backend specific, low-level identifier for a texture uploaded in GPU/graphics system.
-- [Compile-time configurable type]
-- - When a Rendered Backend creates a texture, it store its native identifier into a ImTextureID value.
-- (e.g. Used by DX11 backend to a `ID3D11ShaderResourceView*`; Used by OpenGL backends to store `GLuint`;
-- Used by SDLGPU backend to store a `SDL_GPUTextureSamplerBinding*`, etc.).
-- - User may submit their own textures to e.g. ImGui.Image() function by passing this value.
-- - During the rendering loop, the Renderer Backend retrieve the ImTextureID, which stored inside a
-- ImTextureRef, which is stored inside a ImDrawCmd.
--- @alias ImTextureID integer
-- ImGuiKey | ImGuiMod_XXX: for IsKeyChordPressed(), Shortcut() etc. an ImGuiKey optionally OR-ed with one or more ImGuiMod_XXX values.
--- @alias ImGuiKeyChord int
-- ImDrawIdx: vertex index. [Compile-time configurable type]
--- @alias ImDrawIdx unsigned_int
-- Library Version
-- (Integer encoded as XYYZZ for use in conditionals, e.g. 'if IMGUI_VERSION_NUM >= 12345')
IMGUI_VERSION = "1.93.0 WIP"
IMGUI_VERSION_NUM = 19298
-- Helper: Unicode defines
IM_UNICODE_CODEPOINT_INVALID = 0xFFFD -- Invalid Unicode code point (standard value).
IM_UNICODE_CODEPOINT_MAX = 0xFFFF -- Maximum Unicode code point supported by this build.
-- IM_ALLOC(), IM_FREE(), IM_DELETE()
IM_ALLOC = ImGui.MemAlloc
IM_FREE = ImGui.MemFree
--- @param owner table
--- @param field string|int
IM_DELETE = function(owner, field) if owner[field] ~= nil then owner[field] = nil; ImGui.MemFree(owner, field); end end
-------------------------------------------------------------------------------
-- [SECTION] METATABLE MANAGEMENT
-------------------------------------------------------------------------------
--- File-scope metatable storage
local MT = {}
function ImGui.GetMetatables() return MT end
--- @param _EXPR any
--- @param _MSG string?
function IM_ASSERT(_EXPR, _MSG) assert((_EXPR), _MSG) end
IM_ASSERT_PARANOID = IM_ASSERT
-- A primary data type
--- Note that `S64` and `U64` are not supported
--- @enum ImGuiDataType
ImGuiDataType =
{
S8 = 0, -- signed char / char (with sensible compilers)
U8 = 1, -- unsigned char
S16 = 2, -- short
U16 = 3, -- unsigned short
S32 = 4, -- int
U32 = 5, -- unsigned int
Float = 6, -- float
Double = 7, -- double
Bool = 8, -- bool (provided for user convenience, not supported by scalar widgets)
String = 9, -- string (provided for user convenience, not supported by scalar widgets)
COUNT = 10
}
-- Extend ImGuiDataType_
ImGuiDataType.Pointer = 11 -- [internal]
ImGuiDataType.ID = 12 -- [internal]
ImTextureID_Invalid = -1
-- We intentionally support a limited amount of texture formats to limit burden on CPU-side code and extension.
-- Most standard backends only support RGBA32 but we provide a single channel option for low-resource/embedded systems.
--- @enum ImTextureFormat
ImTextureFormat =
{
RGBA32 = 0, -- 4 components per pixel, each is unsigned 8-bit. Total size = TexWidth * TexHeight * 4
Alpha8 = 1, -- 1 component per pixel, each is unsigned 8-bit. Total size = TexWidth * TexHeight
}
-- Status of a texture to communicate with Renderer Backend.
--- @enum ImTextureStatus
ImTextureStatus =
{
OK = 0,
Destroyed = 1, -- Backend destroyed the texture.
WantCreate = 2, -- Requesting backend to create the texture. Set status OK when done.
WantUpdates = 3, -- Requesting backend to update specific blocks of pixels (write to texture portions which have never been used before). Set status OK when done.
WantDestroy = 4, -- Requesting backend to destroy the texture. Set status to Destroyed when done.
}
-- Flags for ImFontAtlas build
--- @enum ImFontAtlasFlags
ImFontAtlasFlags =
{
None = 0,
NoPowerOfTwoHeight = bitLShift(1, 0), -- Don't round the height to next power of two
NoMouseCursors = bitLShift(1, 1), -- Don't build software mouse cursors into the atlas (save a little texture memory)
NoBakedLines = bitLShift(1, 2), -- Don't build anti-aliased line textures into the atlas (save a little texture memory). SINCE 1.93.0 THIS PREVENTS ANTI-ALIASED LINES FROM WORKING AND WILL DISABLE THEM.
NoBakedRoundCorners= bitLShift(1, 3), -- Don't build round corners into the atlas.
}
-- A cardinal direction
--- @enum ImGuiDir
ImGuiDir =
{
None = -1,
Left = 0,
Right = 1,
Up = 2,
Down = 3,
COUNT = 4
}
-- Identify a mouse button.
-- Those values are guaranteed to be stable and we frequently use 0/1 directly. Named enums provided for convenience.
--- @enum ImGuiMouseButton
ImGuiMouseButton =
{
Left = 0,
Right = 1,
Middle = 2,
COUNT = 5
}
-- Enumeration for GetMouseCursor()
-- User code may request backend to display given cursor by calling SetMouseCursor(), which is why we have some cursors that are marked unused here
--- @enum ImGuiMouseCursor
ImGuiMouseCursor =
{
None = -1,
Arrow = 0,
TextInput = 1, -- When hovering over InputText, etc.
ResizeAll = 2, -- (Unused by Dear ImGui functions)
ResizeNS = 3, -- When hovering over a horizontal border
ResizeEW = 4, -- When hovering over a vertical border or a column
ResizeNESW = 5, -- When hovering over the bottom-left corner of a window
ResizeNWSE = 6, -- When hovering over the bottom-right corner of a window
Hand = 7, -- (Unused by Dear ImGui functions. Use for e.g. hyperlinks)
Wait = 8, -- When waiting for something to process/load.
Progress = 9, -- When waiting for something to process/load, but application is still interactive.
NotAllowed = 10, -- When hovering something with disallowed interaction. Usually a crossed circle.
COUNT = 11
}
-- Coordinates of a rectangle within a texture.
-- When a texture is in ImTextureStatus.WantUpdates state, we provide a list of individual rectangles to copy to the graphics system.
-- You may use ImTextureData::Updates[] for the list, or ImTextureData::UpdateBox for a single bounding box.
--- @class ImTextureRect
--- @field x unsigned_short
--- @field y unsigned_short
--- @field w unsigned_short
--- @field h unsigned_short
--- @param x? unsigned_short
--- @param y? unsigned_short
--- @param w? unsigned_short
--- @param h? unsigned_short
--- @return ImTextureRect
function ImTextureRect(x, y, w, h) return { x = x, y = y, w = w, h = h } end
-- ImVec2: 2D vector used to store positions, sizes etc. [Compile-time configurable type]
-- [Luau] Stored as named fields {x=, y=} so the common `.x`/`.y` access is a plain table read (no metamethod call).
-- Numeric keys 1, 2 still work through the metatable, but are slower: prefer .x/.y in hot code.
--- @class ImVec2
--- @operator add(ImVec2): ImVec2
--- @operator sub(ImVec2): ImVec2
--- @operator mul(number): ImVec2
--- @operator mul(ImVec2): ImVec2
--- @operator div(number): ImVec2
--- @operator div(ImVec2): ImVec2
--- @field x number
--- @field y number
local IM_VEC2 = {}
local VEC2_KEYS = { "x", "y" }
IM_VEC2.__index = function(t, k)
local n = VEC2_KEYS[k]
if n then return rawget(t, n) end
end
IM_VEC2.__newindex = function(t, k, v)
local n = VEC2_KEYS[k]
assert(n ~= nil)
rawset(t, n, v)
end
--- @param x? number
--- @param y? number
--- @return ImVec2
--- @nodiscard
@native
function ImVec2(x, y) return setmetatable({ x = x or 0, y = y or 0 }, IM_VEC2) end
@native
function IM_VEC2.__add(lhs, rhs) return setmetatable({ x = lhs.x + rhs.x, y = lhs.y + rhs.y }, IM_VEC2) end
@native
function IM_VEC2.__sub(lhs, rhs) return setmetatable({ x = lhs.x - rhs.x, y = lhs.y - rhs.y }, IM_VEC2) end
@native
function IM_VEC2.__mul(lhs, rhs)
if type(rhs) == "number" then return setmetatable({ x = lhs.x * rhs, y = lhs.y * rhs }, IM_VEC2) end
return setmetatable({ x = lhs.x * rhs.x, y = lhs.y * rhs.y }, IM_VEC2)
end
@native
function IM_VEC2.__div(lhs, rhs)
if type(rhs) == "number" then return setmetatable({ x = lhs.x / rhs, y = lhs.y / rhs }, IM_VEC2) end
return setmetatable({ x = lhs.x / rhs.x, y = lhs.y / rhs.y }, IM_VEC2)
end
function IM_VEC2.__eq(lhs, rhs) return lhs.x == rhs.x and lhs.y == rhs.y end
function IM_VEC2:__tostring() return string.format("ImVec2(%g, %g)", self.x, self.y) end
--- @param dest ImVec2
--- @param src ImVec2
@native
function ImVec2_Copy(dest, src) dest.x = src.x; dest.y = src.y end
function ImVec2_CopyV(dest, src_x, src_y) dest.x = src_x; dest.y = src_y end
function ImVec2_AddVA(v, add_x, add_y) return v.x + add_x, v.y + add_y end
function ImVec2_SubVA(v, sub_x, sub_y) return v.x - sub_x, v.y - sub_y end
function ImVec2_MulVX(a, scalar) return a.x * scalar, a.y * scalar end
--- An inlined version of `ImVec2_Copy` currently for use in certain ImVector<ImVec2> `push_back`
local function ImVec2_TCopy(t, k, v) local dest = t[k]; dest.x = v.x; dest.y = v.y; end
-- ImVec4: 4D vector used to store clipping rectangles, colors etc. [Compile-time configurable type]
-- [Luau] Same layout as ImVec2: named fields x, y, z, w; numeric keys 1..4 go through the metatable.
--- @class ImVec4
--- @operator add(ImVec4): ImVec4
--- @operator sub(ImVec4): ImVec4
--- @operator mul(number): ImVec4
--- @field x number
--- @field y number
--- @field z number
--- @field w number
local IM_VEC4 = {}
local VEC4_KEYS = { "x", "y", "z", "w" }
IM_VEC4.__index = function(t, k)
local n = VEC4_KEYS[k]
if n then return rawget(t, n) end
end
IM_VEC4.__newindex = function(t, k, v)
local n = VEC4_KEYS[k]
assert(n ~= nil)
rawset(t, n, v)
end
--- @return ImVec4
--- @nodiscard
@native
function ImVec4(x, y, z, w) return setmetatable({ x = x or 0, y = y or 0, z = z or 0, w = w or 0 }, IM_VEC4) end
function IM_VEC4.__add(lhs, rhs) return ImVec4(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w) end
function IM_VEC4.__sub(lhs, rhs) return ImVec4(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w) end
function IM_VEC4.__mul(lhs, rhs) return ImVec4(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs, lhs.w * rhs) end
function IM_VEC4.__eq(lhs, rhs) return lhs.x == rhs.x and lhs.y == rhs.y and lhs.z == rhs.z and lhs.w == rhs.w end
function IM_VEC4:__tostring() return string.format("ImVec4(%g, %g, %g, %g)", self.x, self.y, self.z, self.w) end
--- @param dest ImVec4
--- @param src ImVec4
@native
function ImVec4_Copy(dest, src) dest.x = src.x; dest.y = src.y; dest.z = src.z; dest.w = src.w end
-------------------------------------------------------------------------------
-- [SECTION] Helpers: Debug log, memory allocations macros, ImVector<>
-------------------------------------------------------------------------------
-- ImVector<>
--- A compact ImVector clone
--- @class ImVector<T>
--- @field Data T[] # 1-based table
--- @field Size int # >= 0
--- @field _Constructor function
--- @field _CopyFunc function
local IM_VECTOR = {}
-- Support 1-based number key indexing while keep method accessing speed
--- @param t ImVector
--- @param k string|int
--- @return any
IM_VECTOR.__index = function(t, k)
local m = IM_VECTOR[k] -- methods first (hot path)
if m ~= nil then return m end
if k == "Data" then return nil end -- Data has already been discarded
return t.Data[IM_ASSERT(k >= 1 and k <= t.Size) or k] -- if the mt access turns out nil, the k must be int index into Data
end
--- @param t ImVector
--- @param k int
--- @param v any
IM_VECTOR.__newindex = function(t, k, v)
if k == "Data" then rawset(t, "Data", v); return; end -- set new Data. old Data is already discarded
assert(k >= 1 and k <= t.Size)
t.Data[k] = v
end
local _default_constructor = function() return nil end
local _default_copyfunc = function(t, k, v) t[k] = v end
local function _grow_capacity(v, sz) local new_capacity = (v.Capacity ~= 0) and math.floor(v.Capacity + v.Capacity / 2) or 8; return (new_capacity > sz) and new_capacity or sz; end
--- @param T? function
--- @param COPY_FUNC? function
--- @return ImVector
--- @nodiscard
function ImVector(T, COPY_FUNC) return setmetatable({Data = nil, Size = 0, Capacity = 0, _Constructor = T or _default_constructor, _CopyFunc = COPY_FUNC or _default_copyfunc}, IM_VECTOR) end
function IM_VECTOR:push_back(value) if self.Size == self.Capacity then self:reserve(_grow_capacity(self, self.Size + 1)) end; self._CopyFunc(self.Data, self.Size + 1, value); self.Size = self.Size + 1; return value end
function IM_VECTOR:pop_back() assert(self.Size > 0); self.Size = self.Size - 1; end
function IM_VECTOR:push_front(value) if self.Size == 0 then self:push_back(value) else self:insert(1, value) end end
function IM_VECTOR:clear() if self.Data then self.Size = 0; self.Capacity = 0; IM_FREE(self, "Data"); self.Data = nil end end
function IM_VECTOR:clear_delete() for i = 1, self.Size do self.Data[i] = nil end self.Size = 0 end
function IM_VECTOR:empty() return self.Size == 0 end
function IM_VECTOR:back() assert(self.Size > 0) return self.Data[self.Size] end
function IM_VECTOR:erase(i) assert(i >= 1 and i <= self.Size) local removed = (table.remove(self.Data, i)) ~= nil self.Size = self.Size - 1 return removed end
local function _iter(v, i) i = i + 1 if i <= v.Size then return i, v.Data[i] end end
function IM_VECTOR:iter() return _iter, self, 0 end
function IM_VECTOR:find_index(value) for i = 1, self.Size do if self.Data[i] == value then return i end end return nil end
function IM_VECTOR:erase_unsorted(index) assert(index >= 1 and index <= self.Size) local last_idx = self.Size if index ~= last_idx then self.Data[index] = self.Data[last_idx] end self.Data[last_idx] = nil self.Size = self.Size - 1 return true end
function IM_VECTOR:find_erase(value) local idx = self:find_index(value) if idx then return self:erase(idx) end return false end
function IM_VECTOR:find_erase_unsorted(value) local idx = self:find_index(value) if idx then return self:erase_unsorted(idx) end return false end
function IM_VECTOR:reserve(new_capacity)
if new_capacity <= self.Capacity then return end
local new_data = IM_ALLOC(self._Constructor, new_capacity)
if self.Data then
ImStd.memmove(new_data, 1, self.Data, 1, self.Size)
IM_FREE(self, "Data")
end
self.Data = new_data
self.Capacity = new_capacity
end
function IM_VECTOR:reserve_discard(new_capacity)
if new_capacity <= self.Capacity then return end
if self.Data then IM_FREE(self, "Data") end
self.Data = IM_ALLOC(self._Constructor, new_capacity)
self.Capacity = new_capacity
end
function IM_VECTOR:shrink(new_size) assert(new_size <= self.Size) self.Size = new_size end
function IM_VECTOR:resize(new_size, v)
if new_size > self.Capacity then self:reserve(_grow_capacity(self, new_size)) end
if v ~= nil and new_size > self.Size then
local data = self.Data
for n = self.Size + 1, new_size do data[n] = v end
end
self.Size = new_size
end
function IM_VECTOR:swap(other) self.Size, other.Size = other.Size, self.Size; self.Capacity, other.Capacity = other.Capacity, self.Capacity; self.Data, other.Data = other.Data, self.Data end
function IM_VECTOR:contains(v) for i = 1, self.Size do if self.Data[i] == v then return true end end return false end
-- NOTE: This currently does not use type-aware copy!
function IM_VECTOR:insert(pos, value) assert(pos >= 1 and pos <= self.Size + 1); if self.Size == self.Capacity then self:reserve(_grow_capacity(self, self.Size + 1)) end; for i = self.Size, pos, -1 do self.Data[i + 1] = self.Data[i] end self.Data[pos] = value self.Size = self.Size + 1 return value end
-- NOTE: This currently does not copy type related info!
--- @nodiscard
function IM_VECTOR:copy()
local v = ImVector(); v:resize(self.Size)
local dest = v.Data; local src = self.Data
for i = 1, v.Size do dest[i] = src[i] end
return v
end
-- Not keeping value-key records inside `ImVector`, instead just find it
--- @return int # 0-based index
function IM_VECTOR:index_from_ptr(p)
local data = self.Data; for i = 1, self.Size do if p == data[i] then return i - 1 end end
--- @diagnostic disable-next-line
assert(false, "index_from_ptr failed!")
end
-------------------------------------------------------------------------------
-- [SECTION] Drawing API (ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawList, ImDrawData)
-------------------------------------------------------------------------------
-- Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList.
-- Typically, 1 command = 1 GPU draw call (unless command is a callback)
-- - VtxOffset: When 'io.BackendFlags & ImGuiBackendFlags.RendererHasVtxOffset' is enabled,
-- this fields allow us to render meshes larger than 64K vertices while keeping 16-bit indices.
-- Backends made for <1.71. will typically ignore the VtxOffset fields.
--- @class ImDrawCmd
--- @field ClipRect ImVec4
--- @field TexRef ImTextureRef
--- @field VtxOffset unsigned_int
--- @field IdxOffset unsigned_int
--- @field ElemCount unsigned_int
--- @field UserCallback ImDrawCallback
--- @field UserCallbackData any
--- @field UserCallbackDataSize int
--- @field UserCallbackDataOffset int
MT.ImDrawCmd = {}
MT.ImDrawCmd.__index = MT.ImDrawCmd
--- @return ImDrawCmd
--- @nodiscard
function ImDrawCmd()
return setmetatable({
ClipRect = ImVec4(), -- 4*4 -- Clipping rectangle (x1, y1, x2, y2). Subtract ImDrawData->DisplayPos to get clipping rectangle in "viewport" coordinates
TexRef = nil, -- 16 -- Reference to a font/texture atlas (where backend called ImTextureData::SetTexID()) or to a user-provided texture ID (via e.g. ImGui.Image() calls). Both will lead to a ImTextureID value.
VtxOffset = 0, -- 4 -- Start offset in vertex buffer. ImGuiBackendFlags.RendererHasVtxOffset: always 0, otherwise may be >0 to support meshes larger than 64K vertices with 16-bit indices.
IdxOffset = 0, -- 4 -- Start offset in index buffer.
ElemCount = 0, -- 4 -- Number of indices (multiple of 3) to be rendered as triangles. Vertices are stored in the callee ImDrawList's vtx_buffer[] array, indices in idx_buffer[].
UserCallback = nil, -- 4-8 -- If != nil, call the function instead of rendering the vertices. clip_rect and texture_id will be set normally.
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.
UserCallbackDataOffset = 0 -- 4 -- [Internal] Offset of callback user data when using storage, otherwise -1.
}, MT.ImDrawCmd)
end
--- @return ImTextureID
function MT.ImDrawCmd:GetTexID()
local tex_id = (self.TexRef._TexData) and self.TexRef._TexData.TexID or self.TexRef._TexID
if self.TexRef._TexData ~= nil then
assert(tex_id ~= (-1) --[[ImTextureID_Invalid]], "ImDrawCmd is referring to ImTextureData that wasn't uploaded to graphics system. Backend must call ImTextureData::SetTexID() after handling ImTextureStatus_WantCreate request!")
end
return tex_id
end
-- Vertex layout
--- @class ImDrawVert
--- @field [1] ImVec2 # pos
--- @field [2] ImVec2 # uv
--- @field [3] ImU32 # col
--- @return ImDrawVert
--- @nodiscard
function ImDrawVert() return { ImVec2(), ImVec2(), nil } end
-- [Luau] ImDrawList.VtxBuffer storage: one buffer, VTX_STRIDE bytes per vertex. Vertex i (1-based) is at
-- byte (i - 1) * VTX_STRIDE: f64 pos.x, f64 pos.y, f64 uv.x, f64 uv.y, u32 col. (f64 keeps results identical to tables.)
-- Same Size/Capacity/resize/reserve/shrink/clear interface as ImVector; elements are read with ImDrawVtx_* below.
VTX_STRIDE = 36
local IM_VTXBUF = {}
IM_VTXBUF.__index = IM_VTXBUF
function ImDrawVtxBuffer() return setmetatable({ Size = 0, Capacity = 0, Buf = buffer.create(0) }, IM_VTXBUF) end
function IM_VTXBUF:reserve(n)
if n <= self.Capacity then return end
local nb = buffer.create(n * 36 --[[VTX_STRIDE]])
buffer.copy(nb, 0, self.Buf, 0, self.Size * 36 --[[VTX_STRIDE]])
self.Buf = nb
self.Capacity = n
end
function IM_VTXBUF:resize(n)
if n > self.Capacity then self:reserve(math.max(n, math.floor(self.Capacity * 1.5), 64)) end
self.Size = n
end
function IM_VTXBUF:shrink(n) self.Size = n end
function IM_VTXBUF:clear() self.Size = 0; self.Capacity = 0; self.Buf = buffer.create(0) end
function ImDrawVtx_Pos(b, i) local o = (i - 1) * 36 --[[VTX_STRIDE]]; return buffer.readf64(b, o), buffer.readf64(b, o + 8) end
function ImDrawVtx_Col(b, i) return buffer.readu32(b, (i - 1) * 36 --[[VTX_STRIDE]] + 32) end
-- [Internal] For use by ImDrawList
--- @class ImDrawCmdHeader
--- @field ClipRect ImVec4
--- @field TexRef ImTextureRef
--- @field VtxOffset unsigned_int
--- @return ImDrawCmdHeader
--- @nodiscard
function ImDrawCmdHeader() return { ClipRect = ImVec4(), TexRef = nil, VtxOffset = 0 } end
-- [Internal] For use by ImDrawListSplitter
--- @class ImDrawChannel
--- @field _CmdBuffer ImVector<ImDrawCmd>
--- @field _IdxBuffer ImVector<ImDrawIdx>
--- @return ImDrawChannel
--- @nodiscard
function ImDrawChannel() return { _CmdBuffer = ImVector(), _IdxBuffer = ImVector() } end
-- Split/Merge functions are used to split the draw list into different layers which can be drawn into out of order.
-- This is used by the Columns/Tables API, so items of each column can be batched together in a same draw call.
--- @class ImDrawListSplitter
--- @field _Current int
--- @field _Count int
--- @field _Channels ImVector<ImDrawChannel>
--- @return ImDrawListSplitter
--- @nodiscard
MT.ImDrawListSplitter = {} -- methods in imgui_draw.lua
MT.ImDrawListSplitter.__index = MT.ImDrawListSplitter
function ImDrawListSplitter() return setmetatable({ _Current = 0, _Count = 0, _Channels = ImVector() }, MT.ImDrawListSplitter) end
-- Draw command list
-- This is the low-level list of polygons that ImGui.* functions are filling. At the end of the frame,
-- all command lists are passed to your ImGuiIO::RenderDrawListFn function for rendering.
-- Each dear imgui window contains its own ImDrawList. You can use ImGui.GetWindowDrawList() to
-- access the current window draw list and draw custom primitives.
-- You can interleave normal ImGui.* calls and adding primitives to the current draw list.
-- In single viewport mode, top-left is == GetMainViewport()->Pos (generally 0,0), bottom-right is == GetMainViewport()->Pos+Size (generally io.DisplaySize).
-- You are totally free to apply whatever transformation matrix you want to the data (depending on the use of the transformation you may want to apply it to ClipRect as well!)
-- Important: Primitives are always added to the list and not culled (culling is done at higher-level by ImGui.* functions), if you use this API a lot consider coarse culling your drawn objects.
--- @class ImDrawList
--- @field CmdBuffer ImVector<ImDrawCmd>
--- @field IdxBuffer ImVector<ImDrawIdx>
--- @field VtxBuffer ImVector<ImDrawVert>
--- @field Flags ImDrawFlags # Current flags for drawing primitives. Alter with PushDrawFlag()/PopDrawFlag()
--- @field _VtxCurrentIdx unsigned_int # 1-based, generally == (VtxBuffer.Size + 1)
--- @field _Data ImDrawListSharedData # Pointes to shared draw data
--- @field _VtxWritePtr unsigned_int # 1-based, points to the current writing vertex in VtxBuffer (see ImDrawVtxBuffer)
--- @field _IdxWritePtr unsigned_int # 1-based, points to the current writing index in IdxBuffer.Data
--- @field _Path ImVector<ImVec2> # current path building
--- @field _CmdHeader ImDrawCmdHeader # template of active commands. Fields should match those of CmdBuffer:back()
--- @field _Splitter ImDrawListSplitter
--- @field _ClipRectStack ImVector<ImVec4>
--- @field _TextureStack ImVector<ImTextureRef>
--- @field _CallbacksDataBuf any
--- @field _DrawFlagsStack ImVector<ImDrawFlags>
--- @field _FringeScale float
--- @field _InvFringeScale float
--- @field _FringeScaleIsInteger bool
--- @field _OwnerName string
MT.ImDrawList = {}
MT.ImDrawList.__index = MT.ImDrawList
--- @param pos ImVec2
--- @param uv ImVec2
--- @param col ImU32
function MT.ImDrawList:PrimWriteVtx(pos, uv, col)
local b, o = self.VtxBuffer.Buf, (self._VtxWritePtr - 1) * 36 --[[VTX_STRIDE]]
buffer.writef64(b, o, pos.x); buffer.writef64(b, o + 8, pos.y)
buffer.writef64(b, o + 16, uv.x); buffer.writef64(b, o + 24, uv.y)
buffer.writeu32(b, o + 32, col)
self._VtxWritePtr = self._VtxWritePtr + 1
self._VtxCurrentIdx = self._VtxCurrentIdx + 1
end
--- @param idx ImDrawIdx
function MT.ImDrawList:PrimWriteIdx(idx)
self.IdxBuffer.Data[self._IdxWritePtr] = idx
self._IdxWritePtr = self._IdxWritePtr + 1
end
--- @param pos ImVec2
--- @param uv ImVec2
--- @param col ImU32
function MT.ImDrawList:PrimVtx(pos, uv, col)
self:PrimWriteIdx(self._VtxCurrentIdx)
self:PrimWriteVtx(pos, uv, col)
end
--- @param data? ImDrawListSharedData
--- @return ImDrawList
--- @nodiscard
function ImDrawList(data)
--- @type ImDrawList
local this = setmetatable({
-- This is what you have to render
CmdBuffer = ImVector(), -- Draw commands. Typically 1 command = 1 GPU draw call, unless the command is a callback.
IdxBuffer = ImVector(), -- Index buffer. Each command consume ImDrawCmd::ElemCount of those
VtxBuffer = ImDrawVtxBuffer(), -- Vertex buffer.
Flags = 0, -- Current flags for drawing primitives. Alter with PushDrawFlag()/PopDrawFlags() to adjust defaults to a given scope.
-- [Internal, used while building lists]
_VtxCurrentIdx = 1, -- [Internal] generally == VtxBuffer.Size unless we are past 64K vertices, in which case this gets reset to 0.
_Data = nil, -- Pointer to shared draw data (you can use ImGui.GetDrawListSharedData() to get the one from current ImGui context)
_VtxWritePtr = 1, -- [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)
_IdxWritePtr = 1, -- [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)
_Path = ImVector(ImVec2, ImVec2_TCopy), -- [Internal] current path building
_CmdHeader = ImDrawCmdHeader(), -- [Internal] template of active commands. Fields should match those of CmdBuffer.back().
_Splitter = ImDrawListSplitter(), -- [Internal] for channels api (note: prefer using your own persistent instance of ImDrawListSplitter!)
_ClipRectStack = ImVector(), -- [Internal]
_TextureStack = ImVector(), -- [Internal]
_CallbacksDataBuf = nil, -- [Internal]
_DrawFlagsStack = ImVector(), -- [Internal] ImSmallStack_<ImDrawFlags,3>: capacity checked by PushDrawFlag()
_FringeScale = 1.0, -- [Internal] anti-alias fringe is scaled by this value, this helps to keep things sharp while zooming at vertex buffer content -- [Luau] 1.0 instead of memset 0 so a list used before _SetPixelDensity() still works
_InvFringeScale = 1.0, -- [internal] 1.0 / _FringeScale // FIXME: Consider renaming to _PixelDensity.
_FringeScaleIsInteger = true, -- [Internal] true if 1/_FringeScale is a whole number, used to select fast path for rendering
_OwnerName = nil -- Pointer to owner window's name for debugging
}, MT.ImDrawList)
this:_SetDrawListSharedData(data)
return this
end
-- All draw data to render a Dear ImGui frame
-- (NB: the style and the naming convention here is a little inconsistent, we currently preserve them for backward compatibility purpose,
-- as this is one of the oldest structure exposed by the library! Basically, ImDrawList == CmdList)
--- @class ImDrawData
--- @field Valid bool
--- @field FrameCount int
--- @field TotalIdxCount int
--- @field TotalVtxCount int
--- @field CmdLists ImVector<ImDrawList>
--- @field DisplayPos ImVec2
--- @field DisplaySize ImVec2
--- @field FramebufferScale ImVec2
--- @field OwnerViewport ImGuiViewport
--- @field Textures ImVector<ImTextureData>
MT.ImDrawData = {}
MT.ImDrawData.__index = MT.ImDrawData
--- @return ImDrawData
function ImDrawData()
--- @type ImDrawData
local this = setmetatable({}, MT.ImDrawData)
this.CmdLists = ImVector() -- Array of ImDrawList* to render. The ImDrawLists are owned by ImGuiContext and only pointed to from here.
this:Clear()
return this
end
-------------------------------------------------------------------------------
-- [SECTION] Texture API (ImTextureFormat, ImTextureStatus, ImTextureRect, ImTextureData)
-------------------------------------------------------------------------------
-- Specs and pixel storage for a texture used by Dear ImGui.
-- This is only useful for (1) core library and (2) backends. End-user/applications do not need to care about this.
-- Renderer Backends will create a GPU-side version of this.
-- Why does we store two identifiers: TexID and BackendUserData?
-- - ImTextureID TexID = lower-level identifier stored in ImDrawCmd. ImDrawCmd can refer to textures not created by the backend, and for which there's no ImTextureData.
-- - void* BackendUserData = higher-level opaque storage for backend own book-keeping. Some backends may have enough with TexID and not need both.
-- In columns below: who reads/writes each fields? 'r'=read, 'w'=write, 'core'=main library, 'backend'=renderer backend
--- @class ImTextureData
--- @field UniqueID int
--- @field Status ImTextureStatus
--- @field BackendUserData any
--- @field QueueUserData any
--- @field TexID ImTextureID
--- @field Format ImTextureFormat
--- @field Width int
--- @field Height int
--- @field BytesPerPixel int
--- @field Pixels unsigned_char[]
--- @field UsedRect ImTextureRect
--- @field UpdateRect ImTextureRect
--- @field Updates ImVector<ImTextureRect>
--- @field UnusedFrames int
--- @field RefCount unsigned_short
--- @field UseColors bool
--- @field WantDestroyNextFrame bool
MT.ImTextureData = {}
MT.ImTextureData.__index = MT.ImTextureData
--- @return ImTextureData
--- @nodiscard
function ImTextureData()
--- @type ImTextureData
local this = setmetatable({}, MT.ImTextureData)
-------------------------------------------- core / backend ---------------------------------------
this.UniqueID = 0 -- w - -- [DEBUG] Sequential index to facilitate identifying a texture when debugging/printing. Unique per atlas.
this.Status = 1 --[[ImTextureStatus.Destroyed]] -- rw rw -- ImTextureStatus.OK/_WantCreate/_WantUpdates/_WantDestroy. Always use SetStatus() to modify!
this.BackendUserData = nil -- - rw -- Convenience storage for backend. Some backends may have enough with TexID.
this.QueueUserData = nil -- r - -- Convenience storage for a staged/multi-threaded rendering texture queue (e.g. imgui_threaded_rendering.h. See #8597). When ~= nil, core assumes the texture is referenced by the queue.
this.TexID = (-1) --[[ImTextureID_Invalid]] -- r w -- Backend-specific texture identifier. Always use SetTexID() to modify! The identifier will stored in ImDrawCmd::GetTexID() and passed to backend's RenderDrawData function.
this.Format = 0 -- w r -- ImTextureFormat.RGBA32 (default) or ImTextureFormat.Alpha8
this.Width = 0 -- w r -- Texture width
this.Height = 0 -- w r -- Texture height
this.BytesPerPixel = 0 -- w r -- 4 or 1
this.Pixels = nil -- w r -- Pointer to whole texture buffer holding 'Width*Height' pixels and 'Width*Height*BytesPerPixels' bytes.
this.UsedRect = ImTextureRect() -- w r -- Bounding box encompassing all past and queued Updates[].
this.UpdateRect = ImTextureRect() -- w r -- Bounding box encompassing all queued Updates[].
this.Updates = ImVector() -- w r -- Array of individual updates.
this.UnusedFrames = 0 -- w r -- In order to facilitate handling Status==WantDestroy in some backend: this is a count successive frames where the texture was not used. Always >0 when Status==WantDestroy.
this.RefCount = 0 -- w r -- Number of contexts using this texture. Used during backend shutdown.
this.UseColors = false -- w r -- Tell whether our texture data is known to use colors (rather than just white + alpha).
this.WantDestroyNextFrame = false -- rw - -- [Internal] Queued to set ImTextureStatus.WantDestroy next frame. May still be used in the current frame.
return this
end
--- @param x int
--- @param y int
function MT.ImTextureData:GetPixels() assert(self.Pixels ~= nil); return self.Pixels end
function MT.ImTextureData:GetPixelsAt(x, y)
return self.Pixels, (x + y * self.Width) * self.BytesPerPixel + 1
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:GetTexRef() local tex_ref = ImTextureRef(); tex_ref._TexData = self; tex_ref._TexID = (-1) --[[ImTextureID_Invalid]]; return tex_ref end
function MT.ImTextureData:GetTexID() return self.TexID end
--- @param tex_id ImTextureID
function MT.ImTextureData:SetTexID(tex_id) self.TexID = tex_id end
--- @param status ImTextureStatus
function MT.ImTextureData:SetStatus(status) self.Status = status if (status == 1 --[[ImTextureStatus.Destroyed]] and not self.WantDestroyNextFrame and self.Pixels ~= nil) then self.Status = 2 --[[ImTextureStatus.WantCreate]] end end
-------------------------------------------------------------------------------
-- [SECTION] Texture identifiers (ImTextureID, ImTextureRef)
-------------------------------------------------------------------------------
-- ImTextureRef = higher-level identifier for a texture. Store a ImTextureID _or_ a ImTextureData*.
-- The identifier is valid even before the texture has been uploaded to the GPU/graphics system.
-- This is what gets passed to functions such as `ImGui.Image()`, `ImDrawList::AddImage()`.
-- This is what gets stored in draw commands (`ImDrawCmd`) to identify a texture during rendering.
--- @class ImTextureRef
--- @field _TexData? ImTextureData
--- @field _TexID ImTextureID
MT.ImTextureRef = {}
MT.ImTextureRef.__index = MT.ImTextureRef
--- @return ImTextureRef
--- @nodiscard
function ImTextureRef(tex_id)
return setmetatable({
-- 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.
_TexID = tex_id or (-1) --[[ImTextureID_Invalid]] -- _OR_ Low-level backend texture identifier, if already uploaded or created by user/app. Generally provided to e.g. ImGui.Image() calls.
}, MT.ImTextureRef)
end
-- This is provided for consistency (but we don't actually use this)
--- @return ImTextureID
function MT.ImTextureRef:GetTexID()
assert(not (self._TexData ~= nil and self._TexID ~= (-1) --[[ImTextureID_Invalid]]))
if self._TexData then return self._TexData.TexID else return self._TexID end
end
-------------------------------------------------------------------------------
-- [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFontBaked, ImFont)
-------------------------------------------------------------------------------
-- Font runtime data for a given size
-- Important: pointers to ImFontBaked are only valid for the current frame.
--- @class ImFontBaked
--- @field IndexAdvanceX ImVector<float> # Glyphs->AdvanceX in a directly indexable way. Note that codepoint starts from 0, so IndexAdvanceX.Data[0 + 1] holds the advanceX of glyph at codepoint 0
--- @field FallbackAdvanceX float
--- @field Size float
--- @field RasterizerDensity float
--- @field IndexLookup ImVector<ImU16> # Index glyphs by Unicode codepoint. use IndexLookup.Data[codepoint + 1] for codepoint. Stores 1-based index!
--- @field Glyphs ImVector<ImFontGlyph>
--- @field FallbackGlyphIndex int # Initial value = -1, then becomes 1-based index if fallback char is set
--- @field Ascent float
--- @field Descent float
--- @field MetricsTotalSurface unsigned_int
--- @field WantDestroy bool
--- @field LoadNoFallback bool
--- @field LoadNoRenderOnLayout bool
--- @field LastUsedFrame int
--- @field BakedId ImGuiID
--- @field OwnerFont ImFont
--- @field FontLoaderDatas any
MT.ImFontBaked = {}
MT.ImFontBaked.__index = MT.ImFontBaked
--- @return ImFontBaked
--- @nodiscard
function ImFontBaked()
--- @type ImFontBaked
local this = setmetatable({}, MT.ImFontBaked)
-- [Internal] Members: Hot ~20/24 bytes (for CalcTextSize)
this.IndexAdvanceX = ImVector() -- 12-16 -- out -- Sparse. Glyphs->AdvanceX in a directly indexable way (cache-friendly for CalcTextSize functions which only this info, and are often bottleneck in large UI).
this.FallbackAdvanceX = 0 -- 4 -- out -- FindGlyph(FallbackChar)->AdvanceX
this.Size = 0 -- 4 -- in -- Height of characters/line, set during loading (doesn't change after loading)
this.RasterizerDensity = 0 -- 4 -- in -- Density this is baked at
-- [Internal] Members: Hot ~28/36 bytes (for RenderText loop)
this.IndexLookup = ImVector() -- 12-16 -- out -- Sparse. Index glyphs by Unicode code-point.
this.Glyphs = ImVector() -- 12-16 -- out -- All glyphs.
this.FallbackGlyphIndex = -1 -- 4 -- out -- Index of FontFallbackChar
-- [Internal] Members: Cold
this.Ascent = 0 -- 4+4 -- out -- Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] (unscaled)
this.Descent = 0
this.MetricsTotalSurface = 0
this.WantDestroy = false
this.LoadNoFallback = false
this.LoadNoRenderOnLayout = false
this.LastUsedFrame = 0 -- 4 -- -- Record of that time this was bounds
this.BakedId = 0 -- 4 -- -- Unique ID for this baked storage
this.OwnerFont = nil -- 4-8 -- in -- Parent font
this.FontLoaderDatas = nil -- 4-8 -- -- Font loader opaque storage (per baked font * sources): single contiguous buffer allocated by imgui, passed to loader.
return this
end
-- Font runtime data and rendering
-- - ImFontAtlas automatically loads a default embedded font for you if you didn't load one manually.
-- - Since 1.92.0 a font may be rendered as any size! Therefore a font doesn't have one specific size.
-- - Use 'font->GetFontBaked(size)' to retrieve the ImFontBaked* corresponding to a given size.
-- - If you used g.Font + g.FontSize (which is frequent from the ImGui layer), you can use g.FontBaked as a shortcut, as g.FontBaked == g.Font->GetFontBaked(g.FontSize).
--- @class ImFont
--- @field LastBaked ImFontBaked
--- @field OwnerAtlas ImFontAtlas
--- @field Flags ImFontFlags
--- @field CurrentRasterizerDensity float
--- @field FontId ImGuiID
--- @field LegacySize float
--- @field Sources ImVector<ImFontConfig>
--- @field EllipsisChar ImWchar
--- @field FallbackChar ImWchar
--- @field Used8kPagesMap ImU8[] # 1-based table
--- @field EllipsisAutoBake bool
--- @field RemapPairs table<ImGuiID, any> # LUA: No ImGuiStorage
--- @field Scale float
MT.ImFont = {}
MT.ImFont.__index = MT.ImFont
function MT.ImFont:IsLoaded() return self.OwnerAtlas ~= nil end
function MT.ImFont:GetDebugName() if self.Sources.Size > 0 then return self.Sources[1].Name else return "<unknown>" end end
--- @return ImFont
--- @nodiscard
function ImFont()
--- @type ImFont
local this = setmetatable({}, MT.ImFont)
-- [Internal] Members: Hot ~12-20 bytes
this.LastBaked = nil -- 4-8 -- Cache last bound baked. NEVER USE DIRECTLY. Use GetFontBaked().
this.OwnerAtlas = nil -- 4-8 -- What we have been loaded into.
this.Flags = 0 -- 4 -- Font flags.
this.CurrentRasterizerDensity = 0 -- Current rasterizer density. This is a varying state of the font.
-- [Internal] Members: Cold ~24-52 bytes
-- Conceptually Sources[] is the list of font sources merged to create this font.
this.FontId = 0 -- Unique identifier for the font
this.LegacySize = 0 -- 4 -- in -- Font size passed to AddFont(). Use for old code calling PushFont() expecting to use that size. (use ImGui.GetFontBaked() to get font baked at current bound size).
this.Sources = ImVector() -- 16 -- in -- List of sources. Pointers within OwnerAtlas->Sources[]
this.EllipsisChar = 0 -- 2-4 -- out -- Character used for ellipsis rendering ('...'). If you ever want to temporarily swap this for an alternative/dummy char, make sure to clear EllipsisAutoBake.
this.FallbackChar = 0 -- 2-4 -- out -- Character used if a glyph isn't found (U+FFFD, '?')
this.Used8kPagesMap = {}
this.EllipsisAutoBake = false -- 1 -- -- Mark when the "..." glyph (== EllipsisChar) needs to be generated by combining multiple '.'.
this.RemapPairs = {} -- 16 -- -- Remapping pairs when using AddRemapCodepointXXX().
this.Scale = 0
return this
end
-- A font input/source (we may rename this to ImFontSource in the future)
--- @class ImFontConfig
--- @field Name string
--- @field FontData table
--- @field FontDataSize int
--- @field FontDataOwnedByAtlas bool
--- @field MergeMode bool
--- @field PixelSnapH bool
--- @field OversampleH ImS8
--- @field OversampleV ImS8
--- @field EllipsisChar ImWchar
--- @field SizePixels float
--- @field GlyphRanges ImWchar[]
--- @field GlyphExcludeRanges ImWchar[]
--- @field GlyphOffset ImVec2
--- @field GlyphMinAdvanceX float
--- @field GlyphMaxAdvanceX float
--- @field GlyphExtraAdvanceX float
--- @field FontNo ImU32
--- @field FontLoaderFlags unsigned_int
--- @field RasterizerMultiply float
--- @field RasterizerDensity float
--- @field ExtraSizeScale float
--- @field Flags ImFontFlags
--- @field DstFont ImFont
--- @field FontLoader ImFontLoader
--- @field FontLoaderData any
local IM_FONT_CONFIG = {}
IM_FONT_CONFIG.__index = IM_FONT_CONFIG
--- @return ImFontConfig
--- @nodiscard
function ImFontConfig()
--- @type ImFontConfig
local this = setmetatable({}, IM_FONT_CONFIG)
-- Data Source
this.Name = nil -- <auto> -- Name (strictly to ease debugging, hence limited size buffer)
this.FontData = nil -- TTF/OTF data
this.FontDataSize = 0 -- TTF/OTF data size
this.FontDataOwnedByAtlas = true -- true -- TTF/OTF data ownership taken by the owner ImFontAtlas (will delete memory itself). SINCE 1.92, THE DATA NEEDS TO PERSIST FOR WHOLE DURATION OF ATLAS.
-- Options
this.MergeMode = false -- false -- Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights.
this.PixelSnapH = false -- false -- Align every glyph AdvanceX to pixel boundaries. Prevents fractional font size from working correctly! Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, OversampleH/V will default to 1.
this.OversampleH = 0 -- 0 (2) -- Rasterize at higher quality for sub-pixel positioning. 0 == auto == 1 or 2 depending on size. Note the difference between 2 and 3 is minimal. You can reduce this to 1 for large glyphs save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details.
this.OversampleV = 0 -- 0 (1) -- Rasterize at higher quality for sub-pixel positioning. 0 == auto == 1. This is not really useful as we don't use sub-pixel positions on the Y axis.
this.EllipsisChar = 0 -- 0 -- Explicitly specify Unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used.
this.SizePixels = 0 -- Output size in pixels for rasterizer (more or less maps to the resulting font height).
this.GlyphRanges = nil -- nil -- *LEGACY* THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE. Pointer to a user-provided list of Unicode range (2 value per range, values are inclusive, zero-terminated list).
this.GlyphExcludeRanges = nil -- nil -- Pointer to a small user-provided list of Unicode ranges (2 value per range, values are inclusive, zero-terminated list). This is very close to GlyphRanges[] but designed to exclude ranges from a font source, when merging fonts with overlapping glyphs. Use "Input Glyphs Overlap Detection Tool" to find about your overlapping ranges.
this.GlyphOffset = ImVec2() -- 0, 0 -- Offset (in pixels) all glyphs from this font input. Absolute value for default size, other sizes will scale this value.
this.GlyphMinAdvanceX = 0 -- 0 -- Minimum AdvanceX for glyphs, set Min to align font icons, set both Min/Max to enforce mono-space font. Absolute value for default size, other sizes will scale this value.
this.GlyphMaxAdvanceX = 3.4028234663853e+38 --[[FLT_MAX]] -- FLT_MAX -- Maximum AdvanceX for glyphs
this.GlyphExtraAdvanceX = 0 -- 0 -- Extra spacing (in pixels) between glyphs. Please contact us if you are using this. -- FIXME-NEWATLAS: Intentionally unscaled
this.FontNo = 0 -- 0 -- Index of font within TTF/OTF file
this.FontLoaderFlags = 0 -- 0 -- Settings for custom font builder. THIS IS BUILDER IMPLEMENTATION DEPENDENT. Leave as zero if unsure.
this.RasterizerMultiply = 1.0 -- 1.0f -- Linearly brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable. This is a silly thing we may remove in the future.
this.RasterizerDensity = 1.0 -- 1.0f -- [LEGACY: this only makes sense when ImGuiBackendFlags.RendererHasTextures is not supported] DPI scale multiplier for rasterization. Not altering other font metrics: makes it easy to swap between e.g. a 100% and a 400% fonts for a zooming display, or handle Retina screen. IMPORTANT: If you change this it is expected that you increase/decrease font scale roughly to the inverse of this, otherwise quality may look lowered.
this.ExtraSizeScale = 1.0 -- 1.0f -- Extra rasterizer scale over SizePixels.
-- [Internal]
this.Flags = 0 -- Font flags (don't use just yet, will be exposed in upcoming 1.92.X updates)
this.DstFont = nil -- Target font (as we merging fonts, multiple ImFontConfig may target the same font)
this.FontLoader = nil -- Custom font backend for this source (default source is the one stored in ImFontAtlas)
this.FontLoaderData = nil -- Font loader opaque storage (per font config)
return this
end
-- Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding:
-- - One or more fonts.
-- - Custom graphics data needed to render the shapes needed by Dear ImGui.
-- - Mouse cursor shapes for software cursor rendering (unless setting 'Flags |= ImFontAtlasFlags.NoMouseCursors' in the font atlas).
-- - If you don't call any AddFont*** functions, the default font embedded in the code will be loaded for you.
-- It is the rendering backend responsibility to upload texture into your graphics API:
-- - ImGui_ImplXXXX_RenderDrawData() functions generally iterate platform_io->Textures[] to create/update/destroy each ImTextureData instance.
-- - Backend then set ImTextureData's TexID and BackendUserData.
-- - Texture id are passed back to you during rendering to identify the texture. Read FAQ entry about ImTextureID/ImTextureRef for more details.
--- @class ImFontAtlas
--- @field Flags ImFontAtlasFlags
--- @field TexDesiredFormat ImTextureFormat
--- @field TexGlyphPadding int
--- @field TexMinWidth int
--- @field TexMinHeight int
--- @field TexMaxWidth int
--- @field TexMaxHeight int
--- @field TexRef ImTextureRef
--- @field TexData ImTextureData
--- @field TexList ImVector<ImTextureData>
--- @field Locked bool
--- @field RendererHasTextures bool
--- @field TexPixelsUseColors bool
--- @field TexUvScale ImVec2
--- @field TexUvWhitePixel ImVec2
--- @field Fonts ImVector<ImFont>
--- @field Sources ImVector<ImFontConfig>
--- @field TexUvLines ImVec4[] # 0-based table
--- @field TexNextUniqueID int
--- @field FontNextUniqueID int
--- @field DrawListSharedDatas ImVector<ImDrawListSharedData>
--- @field Builder ImFontAtlasBuilder
--- @field FontLoader ImFontLoader
--- @field FontLoaderName string
--- @field FontLoaderData any
--- @field FontLoaderFlags unsigned_int
--- @field RefCount int
--- @field OwnerContext ImGuiContext
MT.ImFontAtlas = {}
MT.ImFontAtlas.__index = MT.ImFontAtlas
--- @return ImFontAtlas
--- @nodiscard
function ImFontAtlas()
--- @type ImFontAtlas
local this = setmetatable({}, MT.ImFontAtlas)
-- Input
this.Flags = 0 -- Build flags (see ImFontAtlasFlags_)
this.TexDesiredFormat = 0 --[[ImTextureFormat.RGBA32]] -- Desired texture format (default to ImTextureFormat.RGBA32 but may be changed to ImTextureFormat.Alpha8).
this.TexGlyphPadding = 1 -- FIXME: Should be called "TexPackPadding". Padding between glyphs within texture in pixels. Defaults to 1. If your rendering method never relies on bilinear filtering you may set this to 0.
this.TexMinWidth = 512 -- Minimum desired texture width. Must be a power of two. Default to 512.
this.TexMinHeight = 128 -- Minimum desired texture height. Must be a power of two. Default to 128.
this.TexMaxWidth = 8192 -- Maximum desired texture width. Must be a power of two. Default to 8192.
this.TexMaxHeight = 8192 -- Maximum desired texture height. Must be a power of two. Default to 8192.
this.TexRef = ImTextureRef()
this.TexData = nil -- Latest texture.
-- [Internal]
this.TexList = ImVector() -- Texture list (most often TexList.Size == 1). TexData is always == TexList.back(). DO NOT USE DIRECTLY, USE GetDrawData().Textures[]/GetPlatformIO().Textures[] instead!
this.Locked = false -- Marked as locked during ImGui.NewFrame()..EndFrame() scope if TexUpdates are not supported. Any attempt to modify the atlas will assert.
this.RendererHasTextures = false -- Copy of (BackendFlags & ImGuiBackendFlags.RendererHasTextures) from supporting context.
this.TexPixelsUseColors = nil -- Tell whether our texture data is known to use colors (rather than just alpha channel), in order to help backend select a format or conversion process.
this.TexUvScale = nil -- = (1.0f/TexData->TexWidth, 1.0f/TexData->TexHeight). May change as new texture gets created.
this.TexUvWhitePixel = nil -- Texture coordinates to a white pixel. May change as new texture gets created.
this.Fonts = ImVector() -- Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui.NewFrame(), use ImGui.PushFont()/PopFont() to change the current font.
this.Sources = ImVector() -- Source/configuration data
this.TexUvLines = {}
this.TexNextUniqueID = 1 -- Next value to be stored in TexData->UniqueID
this.FontNextUniqueID = 1 -- Next value to be stored in ImFont->FontID
this.DrawListSharedDatas = ImVector() -- List of users for this atlas. Typically one per Dear ImGui context.
this.Builder = nil -- Opaque interface to our data that doesn't need to be public and may be discarded when rebuilding.
this.FontLoader = nil -- Font loader opaque interface (default to use FreeType when IMGUI_ENABLE_FREETYPE is defined, otherwise default to use stb_truetype). Use SetFontLoader() to change this at runtime.
this.FontLoaderName = nil -- Font loader name (for display e.g. in About box) == FontLoader->Name
this.FontLoaderData = nil -- Font backend opaque storage
this.FontLoaderFlags = nil -- Shared flags (for all fonts) for font loader. THIS IS BUILD IMPLEMENTATION DEPENDENT (e.g. Per-font override is also available in ImFontConfig).
this.RefCount = 0 -- Number of contexts using this atlas
this.OwnerContext = nil -- Context which own the atlas will be in charge of updating and destroying it.
return this
end
-- Output of ImFontAtlas::GetCustomRect() when using custom rectangles.
-- Those values may not be cached/stored as they are only valid for the current value of atlas->TexRef
-- (this is in theory derived from ImTextureRect but we use separate structures for reasons)
--- @class ImFontAtlasRect
--- @field x unsigned_short
--- @field y unsigned_short
--- @field w unsigned_short
--- @field h unsigned_short
--- @field uv0 ImVec2
--- @field uv1 ImVec2
-- An opaque identifier to a rectangle in the atlas. -1 when invalid.
-- The rectangle may move and UV may be invalidated, use GetCustomRect() to retrieve it.
--- @alias ImFontAtlasRectId int
ImFontAtlasRectId_Invalid = -1
--- @return ImFontAtlasRect
--- @nodiscard
function ImFontAtlasRect()
return {
x = nil, y = nil, -- Position (in current texture)
w = nil, h = nil, -- Size
uv0 = ImVec2(), -- UV coordinates (in current texture)
uv1 = ImVec2()
}
end
-- Hold rendering data for one glyph.
--- @class ImFontGlyph
--- @field Colored boolean
--- @field Visible boolean
--- @field SourceIdx unsigned_int
--- @field Codepoint unsigned_int
--- @field AdvanceX float
--- @field X0 float
--- @field Y0 float
--- @field X1 float
--- @field Y1 float
--- @field U0 float
--- @field V0 float
--- @field U1 float
--- @field V1 float
--- @field PackId int
--- @return ImFontGlyph
--- @nodiscard
function ImFontGlyph()
return {
Colored = false,
Visible = false,
SourceIdx = 0,
Codepoint = 0,
AdvanceX = 0, -- Horizontal distance to advance cursor/layout position.
X0 = 0, Y0 = 0, X1 = 0, Y1 = 0, -- Glyph corners. Offsets from current cursor/layout position.
U0 = 0, V0 = 0, U1 = 0, V1 = 0, -- Texture coordinates for the current value of ImFontAtlas->TexRef. Cached equivalent of calling GetCustomRect() with PackId.
PackId = -1 -- [Internal] ImFontAtlasRectId value (FIXME: Cold data, could be moved elsewhere?)
}
end
-- Helper to build glyph ranges from text/string data. Feed your application strings/characters to it then call BuildRanges().
-- This is essentially a tightly packed of vector of 64k booleans = 8KB storage.
--- @class ImFontGlyphRangesBuilder
--- @field UsedChars ImVector<ImU32> # Store 1-bit per Unicode code point (0=unused, 1=used)
MT.ImFontGlyphRangesBuilder = {}
MT.ImFontGlyphRangesBuilder.__index = MT.ImFontGlyphRangesBuilder
--- @return ImFontGlyphRangesBuilder
--- @nodiscard
function ImFontGlyphRangesBuilder()
local this = setmetatable({ UsedChars = ImVector() }, MT.ImFontGlyphRangesBuilder)
this:Clear()
return this
end
function MT.ImFontGlyphRangesBuilder:Clear()
local size_in_bytes = (65535 --[[IM_UNICODE_CODEPOINT_MAX]] + 1) / 8
self.UsedChars:resize(size_in_bytes / 4)
local data = self.UsedChars.Data
for i = 1, self.UsedChars.Size do data[i] = 0 end
end
-- Get bit n in the array
function MT.ImFontGlyphRangesBuilder:GetBit(n) local off = bit32.rshift(n, 5); local mask = bit32.lshift(1, bit32.band(n, 31)); return bit32.band(self.UsedChars.Data[off + 1], mask) ~= 0 end
-- Set bit n in the array
function MT.ImFontGlyphRangesBuilder:SetBit(n) local off = bit32.rshift(n, 5); local mask = bit32.lshift(1, bit32.band(n, 31)); self.UsedChars.Data[off + 1] = bit32.bor(self.UsedChars.Data[off + 1], mask) end
-- Add character
function MT.ImFontGlyphRangesBuilder:AddChar(c) self:SetBit(c) end
-------------------------------------------------------------------------------
-- [SECTION] ImGuiIO
-------------------------------------------------------------------------------
-- Communicate most settings and inputs/outputs to Dear ImGui using this structure.
-- Access via ImGui.GetIO(). Read 'Programmer guide' section in .cpp file for general usage.
-- It is generally expected that:
-- - initialization: backends and user code writes to ImGuiIO.
-- - main loop: backends writes to ImGuiIO, user code and imgui code reads from ImGuiIO.
-------------------------------------------------------------------------------
-- Also see ImGui.GetPlatformIO() and ImGuiPlatformIO struct for OS/platform related functions: clipboard, IME etc.
-------------------------------------------------------------------------------
-- [Internal] Storage used by IsKeyDown(), IsKeyPressed() etc functions.
-- If prior to 1.87 you used io.KeysDownDuration[] (which was marked as internal), you should use GetKeyData(key)->DownDuration and *NOT* io.KeysData[key]->DownDuration.
--- @class ImGuiKeyData
--- @field Down bool
--- @field DownDuration float
--- @field DownDurationPrev float
--- @field AnalogValue float
--- @return ImGuiKeyData
--- @nodiscard
function ImGuiKeyData() return { Down = false, DownDuration = 0.0, DownDurationPrev = 0.0, AnalogValue = 0.0 } end
-- Configuration flags stored in io.ConfigFlags. Set by user/application.
-- Note that nowadays most of our configuration options are in other ImGuiIO fields, e.g. io.ConfigWindowsMoveFromTitleBarOnly.
--- @enum ImGuiConfigFlags
ImGuiConfigFlags = {
None = 0,
NavEnableKeyboard = bitLShift(1, 0),
NavEnableGamepad = bitLShift(1, 1),
NoMouse = bitLShift(1, 4),
NoMouseCursorChange = bitLShift(1, 5),
NoKeyboard = bitLShift(1, 6),
DockingEnable = bitLShift(1, 7),
ViewportsEnable = bitLShift(1, 10),
IsSRGB = bitLShift(1, 20),
IsTouchScreen = bitLShift(1, 21)
}
--- @class ImGuiIO
MT.ImGuiIO = {}
MT.ImGuiIO.__index = MT.ImGuiIO
--- @return ImGuiIO
function ImGuiIO()
local this = {
Ctx = nil, -- Parent UI context (needs to be set explicitly by parent).
KeyCtrl = false, -- Keyboard modifier down: Ctrl (non-macOS), Cmd (macOS)
KeyShift = false, -- Keyboard modifier down: Shift
KeyAlt = false, -- Keyboard modifier down: Alt
KeySuper = false, -- Keyboard modifier down: Windows/Super (non-macOS), Ctrl (macOS)
-- Other state maintained from data above + IO function calls
KeyMods = nil, -- Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags). Read-only, updated by NewFrame()
BackendPlatformName = nil, -- = NULL
BackendRendererName = nil, -- = NULL
BackendFlags = 0 --[[ImGuiBackendFlags.None]], -- = 0 -- See ImGuiBackendFlags_ enum. Set by backend (imgui_impl_xxx files or custom backend) to communicate features supported by the backend.
ConfigFlags = 0 --[[ImGuiConfigFlags.None]], -- = 0 -- See ImGuiConfigFlags_ enum. Set by user/application. Keyboard/Gamepad navigation options, etc.
DisplaySize = ImVec2(-1.0, -1.0), -- <unset> -- Main display size, in pixels (== GetMainViewport()->Size). May change every frame.
DeltaTime = 1.0 / 60.0, -- = 1.0f/60.0f -- Time elapsed since last frame, in seconds. May change every frame.
DisplayFramebufferScale = ImVec2(1.0, 1.0), -- = (1, 1) -- Main display density. For retina display where window coordinates are different from framebuffer coordinates. This will affect font density + will end up in ImDrawData::FramebufferScale.
-- Font system
FontAllowUserScaling = false, -- = false -- Allow user scaling text of individual window with Ctrl+Wheel.
-- Main Input State
-- (this block used to be written by backend, since 1.87 it is best to NOT write to those directly, call the AddXXX functions above instead)
-- (reading from those variables is fair game, as they are extremely unlikely to be moving anywhere)
MousePos = ImVec2(), -- Mouse position, in pixels. Set to ImVec2(-FLT_MAX, -FLT_MAX) if mouse is unavailable (on another screen, etc.)
MousePosPrev = ImVec2(), -- Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid)
WantSetMousePos = false, -- MousePos has been altered, backend should reposition mouse on next frame. Rarely used! Set only when io.ConfigNavMoveSetMousePos is enabled.
MouseDelta = ImVec2(), -- Mouse delta. Note that this is zero if either current or previous position are invalid (-FLT_MAX,-FLT_MAX), so a disappearing/reappearing mouse won't have a huge delta.
MouseDown = {[0] = false, [1] = false, [2] = false}, -- Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton.COUNT == 5). Dear ImGui mostly uses left and right buttons. Other buttons allow us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API.
MouseWheel = 0, -- Mouse wheel Vertical: 1 unit scrolls about 5 lines text. >0 scrolls Up, <0 scrolls Down. Hold Shift to turn vertical scroll into horizontal scroll.
MouseWheelH = 0, -- Mouse wheel Horizontal. >0 scrolls Left, <0 scrolls Right. Most users don't have a mouse with a horizontal wheel, may not be filled by all backends.
MouseCtrlLeftAsRightClick = false, -- (OSX) Set to true when the current click was a Ctrl+Click that spawned a simulated right click
MouseSource = 0 --[[ImGuiMouseSource.Mouse]], -- Mouse actual input peripheral (Mouse/TouchScreen/Pen).
PenPressure = 0.0, -- Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui.
MouseWheelRequestAxisSwap = false, -- On a non-Mac system, holding Shift requests WheelY to perform the equivalent of a WheelX event. On a Mac system this is already enforced by the system.
ConfigColorEditFlags = 311427072 --[[ImGuiColorEditFlags.DefaultOptions_]], -- = <defaults> -- Current settings for ColorEdit/ColorPicker widgets. Must have one bit of ImGuiColorEditFlags.DisplayMask_, one bit of ImGuiColorEditFlags.DataTypeMask_, one bit of ImGuiColorEditFlags.PickerMask_, one bit of ImGuiColorEditFlags.InputMask_. Defaults to ImGuiColorEditFlags.DefaultOptions_. May be further edited by users, unless you also set ImGuiColorEditFlags.NoOptions.
-- Widget options
ConfigMacOSXBehaviors = false, -- Swap Cmd<>Ctrl keys + OS X style text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl.
ConfigNavCursorVisibleAuto = true, -- = true -- Using directional navigation key makes the cursor visible. Mouse click hides the cursor.
ConfigNavCursorVisibleAlways = false, -- = false -- Navigation cursor is always visible.
ConfigInputTrickleEventQueue = true, -- = true -- Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates.
ConfigWindowsResizeFromEdges = true, -- = true -- Enable resizing of windows from their edges and from the lower-left corner. This requires ImGuiBackendFlags.HasMouseCursors for better mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags.ResizeFromAnySide flag)
ConfigDebugIniSettings = false, -- = false -- Save .ini data with extra comments (particularly helpful for Docking, but makes saving slower)
ConfigWindowsCopyContentsWithCtrlC = false, -- = false -- [EXPERIMENTAL] Ctrl+C copy the contents of focused window into the clipboard. Experimental because: (1) has known issues with nested Begin/End pairs (2) text output quality varies (3) text output is in submission order rather than spatial order.
ConfigWindowsMoveFromTitleBarOnly = false, -- = false -- Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar.
ConfigScrollbarScrollByPage = true, -- = true -- Enable scrolling page by page when clicking outside the scrollbar grab. When disabled, always scroll to clicked location. When enabled, Shift+Click scrolls to clicked location.
ConfigInputTextCursorBlink = true, -- = true -- Enable blinking cursor (optional as some users consider it to be distracting).
ConfigInputTextEnterKeepActive = false, -- = false -- [BETA] Pressing Enter will reactivate item and select all text (single-line only). Ctrl+Enter or Shift+Enter will deactivate normally.
ConfigDragClickToInputText = false, -- = false -- [BETA] Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving). Not desirable on devices without a keyboard.
-- Keyboard/Gamepad Navigation options
ConfigNavSwapGamepadButtons = false, -- = false -- Swap Activate<>Cancel (A<>B) buttons, matching typical "Nintendo/Japanese style" gamepad layout.
ConfigNavMoveSetMousePos = false, -- = false -- Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult. Will update io.MousePos and set io.WantSetMousePos=true.
ConfigNavCaptureKeyboard = true, -- = true -- Sets io.WantCaptureKeyboard when io.NavActive is set.
ConfigNavEscapeClearFocusItem = true, -- = true -- Pressing Escape can clear focused item + navigation id/highlight. Set to false if you want to always keep highlight on.
ConfigNavEscapeClearFocusWindow = false, -- = false -- Pressing Escape can clear focused window as well (super set of io.ConfigNavEscapeClearFocusItem).
-- DPI/Scaling options
ConfigDpiScaleFonts = false, -- = false -- [EXPERIMENTAL] Automatically overwrite style.FontScaleDpi when Monitor DPI changes. This will scale fonts but _NOT_ scale sizes/padding for now.
ConfigDpiScaleViewports = false, -- = false -- [EXPERIMENTAL] Scale Dear ImGui and Platform Windows when Monitor DPI changes.
-- Ini Settings options
ConfigIniSettingsSaveLastUsedDate = true, -- = true -- Enable loading/saving last used day (YYYYMMDD) in some .ini struct, making things easier to audit and allowing custom tools to cleanup old data.
ConfigIniSettingsAutoDiscardMonths = 0, -- = 0 -- [BETA] Set number of months after which unused .ini entries are discarded on load. Require platform_io.Platform_SessionDate to be set. For systems supporting the feature, .ini entries without a LastUsed field will always be discarded! Please report if you are using this.
-- Options to configure Error Handling and how we handle recoverable errors [EXPERIMENTAL]
-- - Error recovery is provided as a way to facilitate:
-- - Recovery after a programming error (native code or scripting language - the latter tends to facilitate iterating on code while running).
-- - Recovery after running an exception handler or any error processing which may skip code after an error has been detected.
-- - Error recovery is not perfect nor guaranteed! It is a feature to ease development.
-- You not are not supposed to rely on it in the course of a normal application run.
-- - Functions that support error recovery are using IM_ASSERT_USER_ERROR() instead of IM_ASSERT().
-- - By design, we do NOT allow error recovery to be 100% silent. One of the three options needs to be checked!
-- - Always ensure that on programmers seats you have at minimum Asserts or Tooltips enabled when making direct imgui API calls!
-- Otherwise it would severely hinder your ability to catch and correct mistakes!
-- Read https://github.com/ocornut/imgui/wiki/Error-Handling for details.
-- - Programmer seats: keep asserts (default), or disable asserts and keep error tooltips (new and nice!)
-- - Non-programmer seats: maybe disable asserts, but make sure errors are resurfaced (tooltips, visible log entries, use callback etc.)
-- - Recovery after error/exception: record stack sizes with ErrorRecoveryStoreState(), disable assert, set log callback (to e.g. trigger high-level breakpoint), recover with ErrorRecoveryTryToRecoverState(), restore settings.
ConfigErrorRecovery = true, -- = true -- Enable error recovery support. Some errors won't be detected and lead to direct crashes if recovery is disabled.
ConfigErrorRecoveryEnableAssert = true, -- = true -- Enable asserts on recoverable error. By default call IM_ASSERT() when returning from a failing IM_ASSERT_USER_ERROR()
ConfigErrorRecoveryEnableDebugLog = true, -- = true -- Enable debug log output on recoverable errors.
ConfigErrorRecoveryEnableTooltip = true, -- = true -- Enable tooltip on recoverable errors. The tooltip include a way to enable asserts if they were disabled.
-- Tools to detect code submitting items with conflicting/duplicate IDs
-- - Code should use PushID()/PopID() in loops, or append "##xx" to same-label identifiers.
-- - Empty label e.g. Button("") == same ID as parent widget/node. Use Button("##xx") instead!
-- - See FAQ https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#q-about-the-id-stack-system
ConfigDebugHighlightIdConflicts = false, -- Highlight and show an error message popup when multiple items have conflicting identifiers.
ConfigDebugHighlightIdConflictsShowItemPicker = true, -- =true -- Show "Item Picker" button in aforementioned popup.
ConfigDebugIsDebuggerPresent = false, -- = false -- Enable various tools calling IM_DEBUG_BREAK().
-- Tools to test correct Begin/End and BeginChild/EndChild behaviors.
ConfigDebugBeginReturnValueOnce = false, -- = false -- First-time calls to Begin()/BeginChild() will return false. NEEDS TO BE SET AT APPLICATION BOOT TIME if you don't want to miss windows.
ConfigDebugBeginReturnValueLoop = false, -- = false -- Some calls to Begin()/BeginChild() will return false. Will cycle through window depths then repeat. Suggested use: add "io.ConfigDebugBeginReturnValue = io.KeyShift" in your main loop then occasionally press SHIFT. Windows should be flickering while running.
-- Option to deactivate io.AddFocusEvent(false) handling.
ConfigDebugIgnoreFocusLoss = false, -- = false -- Ignore io.AddFocusEvent(false), consequently not calling io.ClearInputKeys()/io.ClearInputMouse() in input processing.
-- Option to compare < 1.93.0 rendering details vs 1.93.0 rendering details. (#9504)
-- - Equivalent to calling PushDrawFlag(ImDrawList_StrokeLegacy,true) on every draw list instances.
-- - Suggested use, e.g. io.ConfigDebugDrawListDefaultsToStrokeLegacy = io.KeyShift; // Map to SHIFT modifier
ConfigDebugDrawListDefaultsToStrokeLegacy = false, -- false -- Default all ImDrawList to ImDrawList_StrokeLegacy mode, mimicking pre-1.93.0 rendering.
IniFilename = "imgui.ini", -- Roblox: no disk access, only used as a flag (nil disables .ini handling)
IniSavingRate = 5.0, -- = 5.0f -- Minimum time between saving positions/sizes to .ini file, in seconds.
LogFilename = "imgui_log.txt", -- = "imgui_log.txt"-- Path to .log file (default parameter to ImGui.LogToFile when no file is specified).
WantSaveIniSettings = false, -- When manual .ini load/save is active (io.IniFilename == nil), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving!
-- Docking options (when ImGuiConfigFlags.DockingEnable is set)
ConfigDockingNoSplit = false, -- = false -- Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars.
ConfigDockingNoDockingOver = false, -- = false -- Simplified docking mode: disable window merging into a same tab-bar, so docking is limited to splitting windows.
ConfigDockingWithShift = false, -- = false -- Enable docking with holding Shift key (reduce visual noise, allows dropping in wider space)
ConfigDockingAlwaysTabBar = false, -- = false -- [BETA] [FIXME: This currently creates regression with auto-sizing and general overhead] Make every single floating window display within a docking node.
ConfigDockingTransparentPayload = false, -- = false -- [BETA] Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport cannot be synced. Best used with ConfigViewportsNoAutoMerge.
-- Viewport options (when ImGuiConfigFlags.ViewportsEnable is set)
-- (sorry for the amount of "NoXXXX" flags, which may be harder to reason about! may rework someday)
ConfigViewportsNoAutoMerge = false, -- = false; -- Set to make all floating imgui windows always create their own viewport. Otherwise, they are merged into the main host viewports when overlapping it. May also set ImGuiViewportFlags.NoAutoMerge on individual viewport.
ConfigViewportsNoTaskBarIcon = false, -- = false -- Disable default OS task bar icon flag for secondary viewports. When a viewport doesn't want a task bar icon, ImGuiViewportFlags.NoTaskBarIcon will be set on it.
ConfigViewportsNoDecoration = true, -- = true -- Disable default OS window decoration flag for secondary viewports. When a viewport doesn't want window decorations, ImGuiViewportFlags.NoDecoration will be set on it. Enabling decoration can create subsequent issues at OS levels (e.g. minimum window size).
ConfigViewportsNoDefaultParent = true, -- = true -- Disable setting OS window parent to main viewport by default. The platform backend is expected to honor `viewport->ParentViewportID` to setup a parent/child relationship between the OS windows (supported if ImGuiBackendFlags.HasParentViewport is set). When parented: child windows always appear in front of their parent. Set to false if you want viewports to automatically be parent of main viewport, otherwise all viewports will be top-level OS windows. Parent/child relationship may be set on a per-window basis using ImGuiWindowClass.
ConfigViewportsPlatformFocusSetsImGuiFocus = true, -- = true -- When a platform window is focused (e.g. using Alt+Tab, clicking Platform Title Bar), apply corresponding focus on imgui windows (may clear focus/active id from imgui windows location in other platform windows). In principle this is better enabled but we provide an opt-out, because some Linux window managers tend to eagerly focus windows (e.g. on mouse hover, or even a simple window pos/size change).
ConfigMemoryCompactTimer = 60.0, -- = 60.0f -- Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable.
-- Miscellaneous options
-- (you can visualize and interact with all options in 'Demo->Configuration')
MouseDrawCursor = false, -- = false -- Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations.
MouseClicked = {[0] = false, [1] = false, [2] = false}, -- Mouse button went from !Down to Down (same as MouseClickedCount[x] != 0)
MouseReleased = {[0] = false, [1] = false, [2] = false}, -- Mouse button went from Down to !Down
MouseClickedCount = {[0] = 0, [1] = 0, [2] = 0}, -- == 0 (not clicked), == 1 (same as MouseClicked[]), == 2 (double-clicked), == 3 (triple-clicked) etc. when going from !Down to Down
MouseClickedLastCount = {[0] = 0, [1] = 0, [2] = 0}, -- Count successive number of clicks. Stays valid after mouse release. Reset after another click is done.
MouseDownDuration = {[0] = -1, [1] = -1, [2] = -1}, -- Duration the mouse button has been down (0.0f == just clicked)
MouseDownDurationPrev = {[0] = -1, [1] = -1, [2] = -1}, -- Previous time the mouse button has been down
MouseDragMaxDistanceAbs = {[0] = ImVec2(), [1] = ImVec2(), [2] = ImVec2()}, -- Maximum distance, absolute, on each axis, of how much mouse has traveled from the clicking point
MouseDragMaxDistanceSqr = {[0] = 0, [1] = 0, [2] = 0}, -- Squared maximum distance of how much mouse has traveled from the clicking point (used for moving thresholds)
MouseDownOwned = {[0] = nil, [1] = nil, [2] = nil}, -- Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds.
MouseDownOwnedUnlessPopupClose = {[0] = nil, [1] = nil, [2] = nil}, -- Track if button was clicked inside a dear imgui window.
MouseClickedTime = {[0] = 0, [1] = 0, [2] = 0}, -- Time of last click (used to figure out double-click)
MouseReleasedTime = {[0] = 0, [1] = 0, [2] = 0}, -- Time of last released (rarely used! but useful to handle delayed single-click when trying to disambiguate them from double-click).
MouseClickedPos = {[0] = ImVec2(), [1] = ImVec2(), [2] = ImVec2()}, -- Position at time of clicking
MouseDoubleClicked = {[0] = false, [1] = false, [2] = false}, -- Has mouse button been double-clicked? (same as MouseClickedCount[x] == 2)
-- Inputs Behaviors
-- (other variables, ones which are expected to be tweaked within UI code, are exposed in ImGuiStyle)
MouseDoubleClickTime = 0.30, -- = 0.30f -- Time for consecutive clicks to account as a double-click, in seconds.
MouseSingleClickDelay = 0.50, -- Time for a delayed click when using GetItemClickedCountWithSingleClickDelay() or IsMouseReleasedWithDelay(), in seconds. Must be > io.MouseDoubleClickTime.
MouseDoubleClickMaxDist = 6.0, -- = 6.0f -- Distance threshold to stay in to validate a double-click or multiple clicks, in pixels.
MouseDragThreshold = 6.0, -- = 6.0f -- Distance threshold before considering we are dragging.
KeyRepeatDelay = 0.275, -- = 0.275f -- When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.).
KeyRepeatRate = 0.050, -- = 0.050f -- When holding a key/button, rate at which it repeats, in seconds.
KeysData = {}, -- size = ImGuiKey.NamedKey_COUNT
WantCaptureMouse = nil, -- Set when Dear ImGui will use mouse inputs, in this case do not dispatch them to your main game/application (either way, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.).
WantCaptureKeyboard = nil, -- Set when Dear ImGui will use keyboard inputs, in this case do not dispatch them to your main game/application (either way, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.).
WantTextInput = nil, -- Mobile/console: when set, you may display an on-screen keyboard. This is set by Dear ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active).
Framerate = 0, -- Estimate of application framerate (rolling average over 60 frames, based on io.DeltaTime), in frame per second. Solely for convenience. Slow applications may not want to use a moving average or may want to reset underlying buffers occasionally.
MetricsRenderVertices = 0, MetricsRenderIndices = 0, MetricsRenderWindows = 0, MetricsActiveWindows = 0,
NavActive = false, -- Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey.NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags.NoNavInputs flag.
NavVisible = false, -- Keyboard/Gamepad navigation highlight is visible and allowed (will handle ImGuiKey.NavXXX events).
MetricsRenderWindows = 0, -- Number of visible windows
-- Font system
Fonts = nil, -- <auto> -- Font atlas: load, rasterize and pack one or more fonts into a single texture.
FontDefault = nil, -- = nil -- Font to use on NewFrame(). Use nil to uses Fonts->Fonts[0].
BackendPlatformUserData = nil, -- = nil -- User data for platform backend
BackendRendererUserData = nil, -- = nil -- User data for renderer backend
InputQueueCharacters = ImVector(), -- Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper.
AppAcceptingEvents = true, -- Only modify via SetAppAcceptingEvents()
InputQueueSurrogate = 0 -- For AddInputCharacterUTF16()
}
for i = 0, 155 --[[ImGuiKey.NamedKey_COUNT]] - 1 do
this.KeysData[i] = ImGuiKeyData()
end
return setmetatable(this, MT.ImGuiIO)
end
-- (Optional) Support for IME (Input Method Editor) via the platform_io.Platform_SetImeDataFn() function. Handler is called during EndFrame().
--- @class ImGuiPlatformImeData
--- @field WantVisible bool
--- @field WantTextInput bool
--- @field InputPos ImVec2
--- @field InputLineHeight float
--- @field ViewportId ImGuiID
--- @return ImGuiPlatformImeData
--- @nodiscard
function ImGuiPlatformImeData()
return {
WantVisible = false, -- A widget wants the IME to be visible.
WantTextInput = false, -- A widget wants text input, not necessarily IME to be visible. This is automatically set to the upcoming value of io.WantTextInput.
InputPos = ImVec2(), -- Position of input cursor (for IME).
InputLineHeight = 0.0, -- Line height (for IME).
ViewportId = 0 -- ID of platform window/viewport.
}
end
--- @param dest ImGuiPlatformImeData
--- @param src ImGuiPlatformImeData
function ImGuiPlatformImeData_Copy(dest, src)
dest.WantVisible = src.WantVisible
dest.WantTextInput = src.WantTextInput
ImVec2_Copy(dest.InputPos, src.InputPos)
dest.InputLineHeight = src.InputLineHeight
dest.ViewportId = src.ViewportId
end
--- @param data1 ImGuiPlatformImeData
--- @param data2 ImGuiPlatformImeData
function ImGuiPlatformImeData_Compare(data1, data2)
if data1.WantVisible ~= data2.WantVisible or
data1.WantTextInput ~= data2.WantTextInput or
data1.InputPos ~= data2.InputPos or
data1.InputLineHeight ~= data2.InputLineHeight or
data1.ViewportId ~= data2.ViewportId then
return false
end
return true
end
-------------------------------------------------------------------------------
-- [SECTION] Viewports
-------------------------------------------------------------------------------
-- Flags stored in ImGuiViewport::Flags, giving indications to the platform backends.
--- @enum ImGuiViewportFlags
ImGuiViewportFlags = {
None = 0,
IsPlatformWindow = bitLShift(1, 0), -- Represent a Platform Window
IsPlatformMonitor = bitLShift(1, 1), -- Represent a Platform Monitor (unused yet)
OwnedByApp = bitLShift(1, 2), -- Platform Window: Is created/managed by the user application? (rather than our backend)
NoDecoration = bitLShift(1, 3), -- Platform Window: Disable platform decorations: title bar, borders, etc. (generally set all windows, but if ImGuiConfigFlags.ViewportsDecoration is set we only set this on popups/tooltips)
NoTaskBarIcon = bitLShift(1, 4), -- Platform Window: Disable platform task bar icon (generally set on popups/tooltips, or all windows if ImGuiConfigFlags.ViewportsNoTaskBarIcon is set)
NoFocusOnAppearing = bitLShift(1, 5), -- Platform Window: Don't take focus when created.
NoFocusOnClick = bitLShift(1, 6), -- Platform Window: Don't take focus when clicked on.
NoInputs = bitLShift(1, 7), -- Platform Window: Make mouse pass through so we can drag this window while peaking behind it.
NoRendererClear = bitLShift(1, 8), -- Platform Window: Renderer doesn't need to clear the framebuffer ahead (because we will fill it entirely).
NoAutoMerge = bitLShift(1, 9), -- Platform Window: Avoid merging this window into another host window. This can only be set via ImGuiWindowClass viewport flags override (because we need to now ahead if we are going to create a viewport in the first place!).
TopMost = bitLShift(1, 10), -- Platform Window: Display on top (for tooltips only).
CanHostOtherWindows = bitLShift(1, 11), -- Viewport can host multiple imgui windows (secondary viewports are associated to a single window). -- FIXME: In practice there's still probably code making the assumption that this is always and only on the MainViewport. Will fix once we add support for "no main viewport".
-- Output status flags (from Platform)
IsMinimized = bitLShift(1, 12), -- Platform Window: Window is minimized, can skip render. When minimized we tend to avoid using the viewport pos/size for clipping window or testing if they are contained in the viewport.
IsFocused = bitLShift(1, 13) -- Platform Window: Window is focused (last call to Platform_GetWindowFocus() returned true)
}
-- - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows.
-- - With multi-viewport enabled, we extend this concept to have multiple active viewports.
-- - In the future we will extend this concept further to also represent Platform Monitor and support a "no main platform window" operation mode.
-- - About Main Area vs Work Area:
-- - Main Area = entire viewport.
-- - Work Area = entire viewport minus sections used by main menu bars (for platform windows), or by task bar (for platform monitor).
-- - Windows are generally trying to stay within the Work Area of their host viewport.
--- @class ImGuiViewport
--- @field ID ImGuiID
--- @field Flags ImGuiViewportFlags
--- @field Pos ImVec2
--- @field Size ImVec2
--- @field FramebufferScale ImVec2
--- @field WorkPos ImVec2
--- @field WorkSize ImVec2
--- @field DpiScale float
--- @field ParentViewportId ImGuiID
--- @field ParentViewport ImGuiViewport
--- @field DrawData ImDrawData
--- @field RendererUserData any
--- @field PlatformUserData any
--- @field PlatformHandle any
--- @field PlatformHandleRaw any
--- @field PlatformWindowCreated bool
--- @field PlatformRequestMove bool
--- @field PlatformRequestResize bool
--- @field PlatformRequestClose bool
MT.ImGuiViewport = {}
MT.ImGuiViewport.__index = MT.ImGuiViewport
function MT.ImGuiViewport:GetCenter()
return ImVec2(self.Pos.x + self.Size.x * 0.5, self.Pos.y + self.Size.y * 0.5)
end
function MT.ImGuiViewport:GetWorkCenter()
return ImVec2(self.WorkPos.x + self.WorkSize.x * 0.5, self.WorkPos.y + self.WorkSize.y * 0.5)
end
--- @return ImGuiViewport
--- @nodiscard
function ImGuiViewport()
return setmetatable({
ID = 0, -- Unique identifier for the viewport
Flags = 0, -- See ImGuiViewportFlags_
Pos = ImVec2(), -- Main Area: Position of the viewport (Dear ImGui coordinates are the same as OS desktop/native coordinates)
Size = ImVec2(), -- Main Area: Size of the viewport.
FramebufferScale = ImVec2(), -- Density of the viewport for Retina display (always 1,1 on Windows, may be 2,2 etc on macOS/iOS). This will affect font rasterizer density.
WorkPos = ImVec2(), -- Work Area: Position of the viewport minus task bars, menus bars, status bars (>= Pos)
WorkSize = ImVec2(), -- Work Area: Size of the viewport minus task bars, menu bars, status bars (<= Size)
DpiScale = 0, -- 1.0f = 96 DPI = No extra scale.
PlatformHandle = nil, -- void* to hold higher-level, platform window handle (e.g. HWND for Win32 backend, Uint32 WindowID for SDL, GLFWWindow* for GLFW), for FindViewportByPlatformHandle().
PlatformHandleRaw = nil, -- void* to hold lower-level, platform-native window handle (always HWND on Win32 platform, unused for other platforms).
PlatformIconData = nil, -- [EXPERIMENTAL] Pass opaque data for Platform backend to handle.
PlatformWindowCreated = false -- Platform window has been created (Platform_CreateWindow() has been called). This is false during the first frame where a viewport is being created.
}, MT.ImGuiViewport)
end
-------------------------------------------------------------------------------
-- [SECTION] ImGuiPlatformIO + other Platform Dependent Interfaces (ImGuiPlatformMonitor, ImGuiPlatformImeData)
-------------------------------------------------------------------------------
-- Access via ImGui.GetPlatformIO()
--- @class ImGuiPlatformIO
--- @field Platform_GetClipboardTextFn fun(ctx?: ImGuiContext): string
--- @field Platform_CreateWindow fun(vp: ImGuiViewport)
--- @field Platform_OnChangedViewport fun(vp: ImGuiViewport)
--- @field Monitors ImVector<ImGuiPlatformMonitor>
--- @field Textures ImVector<ImTextureData>
--- @field Viewports ImVector<ImGuiViewport>
MT.ImGuiPlatformIO = {}
MT.ImGuiPlatformIO.__index = MT.ImGuiPlatformIO
--- @return ImGuiPlatformIO
--- @nodiscard
function ImGuiPlatformIO()
local this = {
-------------------------------------------------------------------------------
-- Input - Interface with OS and Platform backend (most common stuff)
-------------------------------------------------------------------------------
-- Optional: Access OS clipboard
-- (default to use native Win32 clipboard on Windows, otherwise uses a private clipboard. Override to access OS clipboard on other architectures)
Platform_GetClipboardTextFn = nil, -- Should return nil on failure (e.g. clipboard data is not text).
Platform_SetClipboardTextFn = nil,
Platform_ClipboardUserData = nil,
-- Optional: Open link/folder/file in OS Shell
-- (default to use ShellExecuteW() on Windows, system() on Linux/Mac. expected to return false on failure, but some platforms may always return true)
Platform_OpenInShellFn = nil,
Platform_OpenInShellUserData = nil,
-- Optional: Notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME on Windows)
Platform_SetImeDataFn = nil, -- fun(ctx, viewport, data)
Platform_ImeUserData = nil,
-- Optional: Platform locale
-- [Experimental] Configure decimal point e.g. '.' or ',' useful for some languages (e.g. German), generally pulled from *localeconv()->decimal_point
Platform_LocaleDecimalPoint = 46, -- '.'
-- Optional: Session date
Platform_SessionDate = 0, -- Integer storing YYYYMMDD e.g. 20261231 corresponding to the beginning of application session.
-------------------------------------------------------------------------------
-- Input - Interface with Renderer Backend
-------------------------------------------------------------------------------
-- Optional: Maximum texture size supported by renderer (used to adjust how we size textures). 0 if not known.
Renderer_TextureMaxWidth = 0,
Renderer_TextureMaxHeight = 0,
-- Written by some backends during ImGui_ImplXXXX_RenderDrawData() call to point backend_specific ImGui_ImplXXXX_RenderState* structure.
Renderer_RenderState = nil,
-- Optional: Standard draw callbacks (set by backends that support them)
DrawCallback_ResetRenderState = nil, -- Request to reset the graphics/render state.
DrawCallback_SetSamplerLinear = nil, -- Request backend to set texture sampling to Linear.
DrawCallback_SetSamplerNearest = nil, -- Request backend to set texture sampling to Nearest/Point.
DrawCallback_SetSamplerFromTex = nil, -- Request backend to use sampler associated to texture - only available in some backends: OpenGL2/3 and SDLRenderer3.
-- (Optional) Monitor list
-- - Updated by: app/backend. Update every frame to dynamically support changing monitor or DPI configuration.
-- - Used by: dear imgui to query DPI info, clamp popups/tooltips within same monitor and not have them straddle monitors.
Monitors = ImVector(),
-- Textures list (the list is updated by calling ImGui.EndFrame or ImGui.Render)
-- The ImGui_ImplXXXX_RenderDrawData() function of each backend generally access this via ImDrawData::Textures which points to this. The array is available here mostly because backends will want to destroy textures on shutdown.
Textures = ImVector(), -- List of textures used by Dear ImGui (most often 1) + contents of external texture list is automatically appended into this.
-- Viewports list (the list is updated by calling ImGui.EndFrame or ImGui.Render)
-- (in the future we will attempt to organize this feature to remove the need for a "main viewport")
Viewports = ImVector(), -- Main viewports, followed by all secondary viewports.
Platform_OnChangedViewport = nil, -- (Optional) [BETA] FIXME-DPI: DPI handling: Called during Begin() every time the viewport we are outputting into changes, so backend has a chance to swap fonts to adjust style.
}
return setmetatable(this, MT.ImGuiPlatformIO)
end
-- (Optional) This is required when enabling multi-viewport. Represent the bounds of each connected monitor/display and their DPI.
-- We use this information for multiple DPI support + clamping the position of popups and tooltips so they don't straddle multiple monitors.
--- @class ImGuiPlatformMonitor
--- @field MainPos ImVec2
--- @field MainSize ImVec2
--- @field WorkPos ImVec2
--- @field WorkSize ImVec2
--- @field DpiScale float
--- @field PlatformHandle any
--- @return ImGuiPlatformMonitor
--- @nodiscard
function ImGuiPlatformMonitor()
return {
MainPos = ImVec2(0, 0), -- Coordinates of the area displayed on this monitor (Min = upper left, Max = bottom right)
MainSize = ImVec2(0, 0),
WorkPos = ImVec2(0, 0), -- Coordinates without task bars / side bars / menu bars. Used to avoid positioning popups/tooltips inside this region. If you don't have this info, please copy the value for MainPos/MainSize.
WorkSize = ImVec2(0, 0),
DpiScale = 1.0, -- 1.0f = 96 DPI
PlatformHandle = nil -- Backend dependant data (e.g. HMONITOR, GLFWmonitor*, SDL Display Index, NSScreen*)
}
end
-------------------------------------------------------------------------------
-- [SECTION] Flags & Enumerations
-------------------------------------------------------------------------------
-- Flags for ImGui.Begin()
-- (Those are per-window flags. There are shared flags in ImGuiIO: io.ConfigWindowsResizeFromEdges and io.ConfigWindowsMoveFromTitleBarOnly)
--- @enum ImGuiWindowFlags
ImGuiWindowFlags = {
None = 0,
NoTitleBar = bitLShift(1, 0), -- Disable title-bar
NoResize = bitLShift(1, 1), -- Disable user resizing with the lower-right grip
NoMove = bitLShift(1, 2), -- Disable user moving the window
NoScrollbar = bitLShift(1, 3), -- Disable scrollbars (window can still scroll with mouse or programmatically)
NoScrollWithMouse = bitLShift(1, 4), -- Disable user vertically scrolling with mouse wheel. On child window, mouse wheel will be forwarded to the parent unless NoScrollbar is also set.
NoCollapse = bitLShift(1, 5), -- Disable user collapsing window by double-clicking on it. Also referred to as Window Menu Button (e.g. within a docking node).
AlwaysAutoResize = bitLShift(1, 6), -- Resize every window to its content every frame
NoBackground = bitLShift(1, 7), -- Disable drawing background color (WindowBg, etc.) and outside border. Similar as using SetNextWindowBgAlpha(0.0f).
NoSavedSettings = bitLShift(1, 8), -- Never load/save settings in .ini file
NoMouseInputs = bitLShift(1, 9), -- Disable catching mouse, hovering test with pass through.
MenuBar = bitLShift(1, 10), -- Has a menu-bar
HorizontalScrollbar = bitLShift(1, 11), -- Allow horizontal scrollbar to appear (off by default). You may use SetNextWindowContentSize(ImVec2(width,0.0f)); prior to calling Begin() to specify width. Read code in imgui_demo in the "Horizontal Scrolling" section.
NoFocusOnAppearing = bitLShift(1, 12), -- Disable taking focus when transitioning from hidden to visible state
NoBringToFrontOnFocus = bitLShift(1, 13), -- Disable bringing window to front when taking focus (e.g. clicking on it or programmatically giving it focus)
AlwaysVerticalScrollbar = bitLShift(1, 14), -- Always show vertical scrollbar (even if ContentSize.y < Size.y)
AlwaysHorizontalScrollbar = bitLShift(1, 15), -- Always show horizontal scrollbar (even if ContentSize.x < Size.x)
NoNavInputs = bitLShift(1, 16), -- No keyboard/gamepad navigation within the window
NoNavFocus = bitLShift(1, 17), -- No focusing toward this window with keyboard/gamepad navigation (e.g. skipped by Ctrl+Tab)
UnsavedDocument = bitLShift(1, 18), -- Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.
NoDocking = bitLShift(1, 19), -- Disable docking of this window
-- [Internal]
DockNodeHost = bitLShift(1, 23), -- Don't use! For internal use by Begin()/NewFrame()
ChildWindow = bitLShift(1, 24), -- Don't use! For internal use by BeginChild()
Tooltip = bitLShift(1, 25), -- Don't use! For internal use by BeginTooltip()
Popup = bitLShift(1, 26), -- Don't use! For internal use by BeginPopup()
Modal = bitLShift(1, 27), -- Don't use! For internal use by BeginPopupModal()
ChildMenu = bitLShift(1, 28) -- Don't use! For internal use by BeginMenu()
}
-- [Internal]
ImGuiWindowFlags.NoNav = bitOr(ImGuiWindowFlags.NoNavInputs, ImGuiWindowFlags.NoNavFocus)
ImGuiWindowFlags.NoDecoration = bitOr(ImGuiWindowFlags.NoTitleBar, ImGuiWindowFlags.NoResize, ImGuiWindowFlags.NoScrollbar, ImGuiWindowFlags.NoCollapse)
ImGuiWindowFlags.NoInputs = bitOr(ImGuiWindowFlags.NoMouseInputs, ImGuiWindowFlags.NoNavInputs, ImGuiWindowFlags.NoNavFocus)
-- Flags for ImGui.PushItemFlag()
-- (Those are shared by all submitted items)
--- @enum ImGuiItemFlags
ImGuiItemFlags = {
None = 0, -- Default:
NoTabStop = bitLShift(1, 0), -- false -- Disable keyboard tabbing. This is a "lighter" version of ImGuiItemFlags.NoNav.
NoNav = bitLShift(1, 1), -- false -- Disable any form of focusing: keyboard/gamepad directional navigation and SetKeyboardFocusHere() calls.
NoNavDefaultFocus = bitLShift(1, 2), -- false -- Disable item being a candidate for default focus (e.g. used by title bar items).
ButtonRepeat = bitLShift(1, 3), -- false -- Any button-like behavior will have repeat mode enabled (based on io.KeyRepeatDelay and io.KeyRepeatRate values). Note that you can also call IsItemActive() after any button to tell if it is being held.
AutoClosePopups = bitLShift(1, 4), -- true -- MenuItem()/Selectable() automatically close their parent popup window.
AllowDuplicateId = bitLShift(1, 5), -- false -- Allow submitting an item with the same identifier as an item already submitted this frame without triggering a warning tooltip if io.ConfigDebugHighlightIdConflicts is set.
Disabled = bitLShift(1, 6), -- false -- [Internal] Disable interactions. DOES NOT affect visuals. This is used by BeginDisabled()/EndDisabled() and only provided here so you can read back via GetItemFlags().
-------------------------------------------------------------------------------
-- LiveEdit refers to applying edits to backing variables _while_ typing a value using the keyboard.
-- Widget: | Input: | w/ LiveEdit: | Output:
-- InputText() | "123" | on(default) | "1" then "12" then "123"
-- InputText() | "123" | off | "123" after validating or tabbing out or losing focus.
-- DragFloat(), SliderInt(), etc. | "123" | on* | 1 then 12 then 123
-- DragFloat(), SliderInt(), etc. | "123" | off(default)* | 123 after validation or tabbing out or losing focus.
-- (*) Since 1.92.9 (July 2026), ImGuiItemFlags.LiveEditOnInputScalar is OFF by default. In prior version it was ON for everything.
-------------------------------------------------------------------------------
LiveEditOnInputText = bitLShift(1, 7), -- true -- InputText: apply keyboard edits to backing value while typing. Otherwise, edits are applied when validating, tabbing out or losing focus.
LiveEditOnInputScalar = bitLShift(1, 8) -- false -- DragXXX, SliderXXX, InputScalar: apply keyboard edits to backing value while typing. Otherwise, edits are applied when validating, tabbing out or losing focus.
}
ImGuiItemFlags.LiveEditOnInput = bitOr(ImGuiItemFlags.LiveEditOnInputText, ImGuiItemFlags.LiveEditOnInputScalar)
-- Status flags for an already submitted item
-- - output: stored in g.LastItemData.StatusFlags
--- @enum ImGuiItemStatusFlags
ImGuiItemStatusFlags = {
None = 0,
HoveredRect = bitLShift(1, 0), -- Mouse position is within item rectangle (does NOT mean that the window is in correct z-order and can be hovered!, this is only one part of the most-common IsItemHovered test)
HasDisplayRect = bitLShift(1, 1), -- g.LastItemData.DisplayRect is valid
Edited = bitLShift(1, 2), -- Value exposed by item was edited in the current frame (should match the bool return value of most widgets)
ToggledSelection = bitLShift(1, 3), -- Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected", only state changes, in order to easily handle clipping with less issues.
ToggledOpen = bitLShift(1, 4), -- Set when TreeNode() reports toggling their open state.
HasDeactivated = bitLShift(1, 5), -- Set if the widget/group is able to provide data for the ImGuiItemStatusFlags.Deactivated flag.
Deactivated = bitLShift(1, 6), -- Only valid if ImGuiItemStatusFlags.HasDeactivated is set.
HoveredWindow = bitLShift(1, 7), -- Override the HoveredWindow test to allow cross-window hover testing.
Visible = bitLShift(1, 8), -- [WIP] Set when item is overlapping the current clipping rectangle (Used internally. Please don't use yet: API/system will change as we refactor Itemadd()).
HasClipRect = bitLShift(1, 9), -- g.LastItemData.ClipRect is valid.
HasShortcut = bitLShift(1, 10), -- g.LastItemData.Shortcut valid. Set by SetNextItemShortcut() -> ItemAdd().
--ImGuiItemStatusFlags.FocusedByTabbing = 1 << 8, -- Removed IN 1.90.1 (Dec 2023). The trigger is part of g.NavActivateId. See commit 54c1bdeceb.
EditedInternal = bitLShift(1, 11) -- Similar to ImGuiItemStatusFlags.Edited but bypassing ImGuiItemFlags.NoMarkEdited.
}
-- Flags for ImGui.BeginChild()
-- (Legacy: bit 0 must always correspond to ImGuiChildFlags.Borders to be backward compatible with old API using 'bool border = false'.)
-- About using AutoResizeX/AutoResizeY flags:
-- - May be combined with SetNextWindowSizeConstraints() to set a min/max size for each axis (see "Demo->Child->Auto-resize with Constraints").
-- - Size measurement for a given axis is only performed when the child window is within visible boundaries, or is just appearing.
-- - This allows BeginChild() to return false when not within boundaries (e.g. when scrolling), which is more optimal. BUT it won't update its auto-size while clipped.
-- While not perfect, it is a better default behavior as the always-on performance gain is more valuable than the occasional "resizing after becoming visible again" glitch.
-- - You may also use ImGuiChildFlags.AlwaysAutoResize to force an update even when child window is not in view.
-- HOWEVER PLEASE UNDERSTAND THAT DOING SO WILL PREVENT BeginChild() FROM EVER RETURNING FALSE, disabling benefits of coarse clipping.
--- @enum ImGuiChildFlags
ImGuiChildFlags = {
None = 0,
Borders = bitLShift(1, 0), -- Show an outer border and enable WindowPadding. (IMPORTANT: this is always == 1 == true for legacy reason)
AlwaysUseWindowPadding = bitLShift(1, 1), -- Pad with style.WindowPadding even if no border are drawn (no padding by default for non-bordered child windows because it makes more sense)
ResizeX = bitLShift(1, 2), -- Allow resize from right border (layout direction). Enable .ini saving (unless ImGuiWindowFlags.NoSavedSettings passed to window flags)
ResizeY = bitLShift(1, 3), -- Allow resize from bottom border (layout direction). "
AutoResizeX = bitLShift(1, 4), -- Enable auto-resizing width. Read "IMPORTANT: Size measurement" details above.
AutoResizeY = bitLShift(1, 5), -- Enable auto-resizing height. Read "IMPORTANT: Size measurement" details above.
AlwaysAutoResize = bitLShift(1, 6), -- Combined with AutoResizeX/AutoResizeY. Always measure size even when child is hidden, always return true, always disable clipping optimization! NOT RECOMMENDED.
FrameStyle = bitLShift(1, 7), -- Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding.
NavFlattened = bitLShift(1, 8) -- [BETA] Share focus scope, allow keyboard/gamepad navigation to cross over parent border to this child or between sibling child windows.
}
ImGuiChildFlags.ResizeBoth = bitOr(ImGuiChildFlags.ResizeX, ImGuiChildFlags.ResizeY)
ImGuiChildFlags.ResizeXAndY = ImGuiChildFlags.ResizeBoth
--- @enum ImGuiNextItemDataFlags
ImGuiNextItemDataFlags = {
None = 0,
HasWidth = bitLShift(1, 0),
HasOpen = bitLShift(1, 1),
HasShortcut = bitLShift(1, 2),
HasRefVal = bitLShift(1, 3),
HasStorageID = bitLShift(1, 4),
HasColorMarker = bitLShift(1, 5)
}
-- Flags for ImDrawList functions
--- @enum ImDrawFlags
ImDrawFlags = {
None = 0,
--ImDrawFlags_Closed = 1, // -> FLAG MOVED BELOW. Prior to 1.92.8 (May 2026): ImDrawFlags_Closed was guaranteed to be == (1<<0) == 1, for legacy compatibility reason. Hardcoded use of 1 or true should be replaced with 'ImDrawFlags_Closed'.
-- About usage of flags:
-- - "Prim" column: 'OK' = flag can be used to configure an individual AddXXX() call.
-- - "Scope" column: 'OK' = initialized by ImGui based on Style options (e.g. whether anti-aliased is enabled) + may be modified using ImDrawList::PushDrawFlag().
-- OK(0)/OK(1) indicates whether this flag is set in the default ImGui Style settings.
-- - Rounding default to ImDrawFlags_RoundAll when 'rounding > 0'.
-- - So you only need to use the ImDrawFlags_RoundXXX flags if you want a special configuration (e.g. a rectangle with one rounded corner).
-- Rounding for AddRectXXX(), PathRect() ------ -- Prim/Scope?
RoundTopLeft = bitLShift(1, 4), -- OK -- -- Round top-left corner only (when 'rounding > 0.0f', we default to all corners).
RoundTopRight = bitLShift(1, 5), -- OK -- -- Round top-right corner only (when 'rounding > 0.0f', we default to all corners).
RoundBottomLeft = bitLShift(1, 6), -- OK -- -- Round bottom-left corner only (when 'rounding > 0.0f', we default to all corners).
RoundBottomRight = bitLShift(1, 7), -- OK -- -- Round bottom-right corner only (when 'rounding > 0.0f', we default to all corners).
RoundNone = bitLShift(1, 8), -- OK -- -- Disable rounding even when 'rounding > 0.0f'. This value is NOT zero, it is NOT an implicit flag!
-- Stroke Options ----------------------------- -- Prim/Scope?
Closed = bitLShift(1, 9), -- OK -- -- PathStroke(), AddPolyline(): specify that shape should be closed.
JoinMiter = bitLShift(1, 10), -- OK -- -- PathStroke(), AddPolyline(): use miter joins/corners only. This assumes that the input polyline does not have corners sharper than 90 degrees. Slightly faster.
CapSquare = bitLShift(1, 11), -- OK -- -- PathStroke(), AddPolyline(): use square cap line ends.
-- About Stroke Ends:
-- - Rendering is optimized for fast pixel-perfect UI, so line ends are not anti-aliased by default. It's cheaper (we can emit less vertices).
-- - For more free-form drawings, light graphs and markers, or when using thick strokes: you can turn them on using the ImDrawFlags_AALineEnds flag.
-- - See 'Demo->Examples->Custom Rendering' to interactively toy with those flags.
-- - 'OK*' indicates using this at the AddXXX() call site is unlikely: it would only makes sense if (1) the option is disabled in style/scope and (2) you want to forcefully enable it for a single primitives. Possible but unlikely! Only supported for functions taking flags inputs.
-- Stroke/Fill Anti-aliasing ------------------ -- Prim/Scope?
AAFill = bitLShift(1, 12), -- OK* OK(1) -- Enable anti-aliasing for Filled shapes.
AALines = bitLShift(1, 13), -- OK* OK(1) -- Enable anti-aliasing for Strokes (lines, borders).
AALineEnds = bitLShift(1, 14), -- OK OK(0) -- Enable anti-aliasing for Strokes Ends. Requires AALines to also be enabled. Useful on thick strokes or for precise continuity of multiple lines. A little more costly.
--ImDrawFlags_NoAA = 1 << 15, // OK -- // Disable anti-aliasing for a given primitive.
--ImDrawFlags_NoAALineEnds = 1 << 16, // OK -- // Disable anti-aliasing ends for a given primitive.
-- About Stroke Position:
-- - Read https://github.com/ocornut/imgui/wiki/Draw-List (this guide includes a precise list of differences between 1.92.9 and 1.93.0)
-- - Read https://github.com/ocornut/imgui/wiki/Pixel-Perfect-Rendering
-- Stroke Position relative to shape outline -- -- Prim/Scope?
StrokeInside = bitLShift(1, 17), -- OK -- -- Draw stroke inside of the shape outline (default for closed shapes and AddLineH, AddLineV functions)
StrokeCenter = bitLShift(2, 17), -- OK -- -- Draw stroke at the center of the shape outline (default for paths, bezier, and AddLine functions)
StrokeCenterBiased = bitLShift(3, 17), -- OK -- -- Draw stroke at the center of the shape outline, so that half thickness rounded down will be outside, and the rest inside the shape outline. Useful for axis-aligned shapes: AddLineH, AddLineV, AddRect. Does not animate well!
StrokeOutside = bitLShift(4, 17), -- OK -- -- Draw stroke outside of the shape outline
StrokeLegacy = bitLShift(7, 17), -- OK OK(0) -- Use legacy positioning + enable JoinMiter + disable AALineEnds. Must be all bits set.
-- Other Options ------------------------------ -- Prim/Scope?
TextNoPixelSnap = bitLShift(1, 20), -- OK OK(0) -- Disable automatically snapping AddText() calls to pixel boundaries.
UseTexForRoundCorners = bitLShift(1, 21), -- -- OK(1) -- Enable using textures instead of strokes to draw rounded corners/circles where possible (faster). Used by default unless 'ImFontAtlasFlags_NoBakedRoundCorners' is enabled in the font atlas.
UseVtxOffset = bitLShift(1, 22), -- -- OK(1) -- Can emit 'VtxOffset > 0' to allow large meshes with 16-bit ImDrawIdx. Used by default when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled by the backend.
AllowTexForRoundCorners_= bitLShift(1, 23), -- -- OK(1) -- [Internal]
}
ImDrawFlags.RoundAll = bitOr(ImDrawFlags.RoundTopLeft, ImDrawFlags.RoundTopRight, ImDrawFlags.RoundBottomLeft, ImDrawFlags.RoundBottomRight) -- (Default!!)
ImDrawFlags.RoundTop = bitOr(ImDrawFlags.RoundTopLeft, ImDrawFlags.RoundTopRight)
ImDrawFlags.RoundBottom = bitOr(ImDrawFlags.RoundBottomLeft, ImDrawFlags.RoundBottomRight)
ImDrawFlags.RoundLeft = bitOr(ImDrawFlags.RoundBottomLeft, ImDrawFlags.RoundTopLeft)
ImDrawFlags.RoundRight = bitOr(ImDrawFlags.RoundBottomRight, ImDrawFlags.RoundTopRight)
-- [Internal]
ImDrawFlags.RoundMask_ = bitOr(ImDrawFlags.RoundAll, ImDrawFlags.RoundNone) -- [Internal]
ImDrawFlags.AllowInPushScope_ = bitOr(ImDrawFlags.AAFill, ImDrawFlags.AALines, ImDrawFlags.AALineEnds, ImDrawFlags.StrokeLegacy, ImDrawFlags.TextNoPixelSnap, ImDrawFlags.UseTexForRoundCorners, ImDrawFlags.UseVtxOffset, ImDrawFlags.RoundMask_) -- [Internal] Values allowed in PushDrawFlag() scope.
ImDrawFlags.AllowInFrameScope_ = bitOr(ImDrawFlags.AllowInPushScope_, ImDrawFlags.AllowTexForRoundCorners_)
ImDrawFlags.StrokeMask_ = bitLShift(0x07, 17) -- [Internal]
ImDrawFlags.InvalidMask_ = 0x8000000F -- [Internal] == ~0x7FFFFFF0 == 0x8000000F. Reserved to detect misuses.
-- Obsolete names
-- RENAMED in 1.93.0 (Oct 2026): ImDrawFlags_RoundCornersXXXX -> ImDrawFlags_RoundXXXX.
ImDrawFlags.RoundCornersTopLeft = ImDrawFlags.RoundTopLeft
ImDrawFlags.RoundCornersTopRight = ImDrawFlags.RoundTopRight
ImDrawFlags.RoundCornersBottomLeft = ImDrawFlags.RoundBottomLeft
ImDrawFlags.RoundCornersBottomRight = ImDrawFlags.RoundBottomRight
ImDrawFlags.RoundCornersNone = ImDrawFlags.RoundNone
ImDrawFlags.RoundCornersAll = ImDrawFlags.RoundAll
ImDrawFlags.RoundCornersTop = ImDrawFlags.RoundTop
ImDrawFlags.RoundCornersBottom = ImDrawFlags.RoundBottom
ImDrawFlags.RoundCornersLeft = ImDrawFlags.RoundLeft
ImDrawFlags.RoundCornersRight = ImDrawFlags.RoundRight
-- -- RENAMED in 1.93.0: Merged ImDrawListFlags into ImDrawFlags.
--- @enum ImDrawListFlags
ImDrawListFlags = {
None = ImDrawFlags.None,
AntiAliasedFill = ImDrawFlags.AAFill,
AntiAliasedLines = ImDrawFlags.AALines,
AntiAliasedLinesUseTex = ImDrawFlags.AALines, -- No effect, anti-aliased rendering always uses textures from 1.93+
AllowVtxOffset = ImDrawFlags.UseVtxOffset,
TextNoPixelSnap = ImDrawFlags.TextNoPixelSnap,
}
-- Font flags
-- (in future versions as we redesign font loading API, this will become more important and better documented. for now please consider this as internal/advanced use)
--- @enum ImFontFlags
ImFontFlags = {
None = 0,
NoLoadError = bitLShift(1, 1), -- Disable throwing an error/assert when calling AddFontXXX() with missing file/data. Calling code is expected to check AddFontXXX() return value.
NoLoadGlyphs = bitLShift(1, 2), -- [Internal] Disable loading new glyphs.
LockBakedSizes = bitLShift(1, 3), -- [Internal] Disable loading new baked sizes, disable garbage collecting current ones. e.g. if you want to lock a font to a single size. Important: if you use this to preload given sizes, consider the possibility of multiple font density used on Retina display.
ImplicitRefSize = bitLShift(1, 4) -- [Internal] Reference size was not set explicitly.
}
-- Enumeration for AddMouseSourceEvent() actual source of Mouse Input data.
-- Historically we use "Mouse" terminology everywhere to indicate pointer data, e.g. MousePos, IsMousePressed(), io.AddMousePosEvent()
-- But that "Mouse" data can come from different source which occasionally may be useful for application to know about.
-- You can submit a change of pointer type using io.AddMouseSourceEvent().
--- @enum ImGuiMouseSource
ImGuiMouseSource = {
Mouse = 0, -- Input is coming from an actual mouse.
TouchScreen = 1, -- Input is coming from a touch screen (no hovering prior to initial press, less precise initial press aiming, dual-axis wheeling possible).
Pen = 2, -- Input is coming from a pressure/magnetic pen (often used in conjunction with high-sampling rates).
COUNT = 3
}
-- Enumeration for ImGui.SetNextWindow***(), SetWindow***(), SetNextItem***() functions
-- Represent a condition.
-- Important: Treat as a regular enum! Do NOT combine multiple values using binary operators! All the functions above treat 0 as a shortcut to ImGuiCond.Always.
--- @enum ImGuiCond
ImGuiCond = {
None = 0, -- No condition (always set the variable), same as _Always
Always = bitLShift(1, 0), -- No condition (always set the variable), same as _None
Once = bitLShift(1, 1), -- Set the variable once per runtime session (only the first call will succeed)
FirstUseEver = bitLShift(1, 2), -- Set the variable if the object/window has no persistently saved data (no entry in .ini file)
Appearing = bitLShift(1, 3) -- Set the variable if the object/window is appearing after being hidden/inactive (or the first time)
}
-- Flags for Shortcut(), SetNextItemShortcut(),
-- (and for upcoming extended versions of IsKeyPressed(), IsMouseClicked(), Shortcut(), SetKeyOwner(), SetItemKeyOwner() that are still in imgui_internal.h)
-- Don't mistake with ImGuiInputTextFlags! (which is for ImGui.InputText() function)
--- @enum ImGuiInputFlags
ImGuiInputFlags = {
None = 0,
Repeat = bitLShift(1, 0), -- Enable repeat. Return true on successive repeats. Default for legacy IsKeyPressed(). NOT Default for legacy IsMouseClicked(). MUST BE == 1.
-- Flags for Shortcut(), SetNextItemShortcut()
-- - Routing policies: RouteGlobal+OverActive >> RouteActive or RouteFocused (if owner is active item) >> RouteGlobal+OverFocused >> RouteFocused (if in focused window stack) >> RouteGlobal.
-- - Default policy is RouteFocused. Can select only 1 policy among all available.
RouteActive = bitLShift(1, 10), -- Route to active item only.
RouteFocused = bitLShift(1, 11), -- Route to windows in the focus stack (DEFAULT). Deep-most focused window takes inputs. Active item takes inputs over deep-most focused window.
RouteGlobal = bitLShift(1, 12), -- Global route (unless a focused window or active item registered the route).
RouteAlways = bitLShift(1, 13), -- Do not register route, poll keys directly.
-- - Routing options
RouteOverFocused = bitLShift(1, 14), -- Option: global route: higher priority than focused route (unless active item in focused route).
RouteOverActive = bitLShift(1, 15), -- Option: global route: higher priority than active item. Unlikely you need to use that: will interfere with every active items, e.g. Ctrl+A registered by InputText will be overridden by this. May not be fully honored as user/internal code is likely to always assume they can access keys when active.
RouteUnlessBgFocused = bitLShift(1, 16), -- Option: global route: will not be applied if underlying background/void is focused (== no Dear ImGui windows are focused). Useful for overlay applications.
RouteFromRootWindow = bitLShift(1, 17), -- Option: route evaluated from the point of view of root window rather than current window.
-- Flags for SetNextItemShortcut()
Tooltip = bitLShift(1, 18) -- Automatically display a tooltip when hovering item [BETA] Unsure of right api (opt-in/opt-out)
}
-- Flags for InvisibleButton() [extended in imgui_internal.h]
--- @enum ImGuiButtonFlags
ImGuiButtonFlags = {
None = 0,
MouseButtonLeft = bitLShift(1, 0), -- React on left mouse button (default)
MouseButtonRight = bitLShift(1, 1), -- React on right mouse button
MouseButtonMiddle = bitLShift(1, 2), -- React on center mouse button
EnableNav = bitLShift(1, 3), -- InvisibleButton(): do not disable navigation/tabbing. Otherwise disabled by default.
PressedOnClick = bitLShift(1, 4), -- return true on click (mouse down event)
PressedOnClickRelease = bitLShift(1, 5), -- [Default] return true on click + release on same item <-- this is what the majority of Button are using
PressedOnClickReleaseAnywhere = bitLShift(1, 6), -- return true on click + release even if the release event is not done while hovering the item
PressedOnRelease = bitLShift(1, 7), -- return true on release (default requires click+release). Prior to 2026/03/20 this implied ImGuiButtonFlags.NoHoldingActiveId but they are separate now.
PressedOnDoubleClick = bitLShift(1, 8), -- return true on double-click (default requires click+release)
PressedOnDragDropHold = bitLShift(1, 9), -- return true when held into while we are drag and dropping another item (used by e.g. tree nodes, collapsing headers)
--ImGuiButtonFlags.Repeat = 1 << 10, -- hold to repeat -> use ImGuiItemFlags.ButtonRepeat instead.
FlattenChildren = bitLShift(1, 11), -- allow interactions even if a child window is overlapping
AllowOverlap = bitLShift(1, 12), -- Hit testing will allow subsequent widgets to overlap this one. Require previous frame HoveredId to match before being usable. Shortcut to calling SetNextItemAllowOverlap().
--ImGuiButtonFlags.DontClosePopups = 1 << 13, -- disable automatically closing parent popup on press
--ImGuiButtonFlags.Disabled = 1 << 14, -- disable interactions -> use BeginDisabled() or ImGuiItemFlags.Disabled
AlignTextBaseLine = bitLShift(1, 15), -- vertically align button to match text baseline - ButtonEx() only -- FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine
NoKeyModsAllowed = bitLShift(1, 16), -- disable mouse interaction if a key modifier is held
NoHoldingActiveId = bitLShift(1, 17), -- don't set ActiveId while holding the mouse (ImGuiButtonFlags.PressedOnClick only)
NoNavFocus = bitLShift(1, 18), -- don't override navigation focus when activated (FIXME: this is essentially used every time an item uses ImGuiItemFlags.NoNav, but because legacy specs don't requires LastItemData to be set ButtonBehavior(), we can't poll g.LastItemData.ItemFlags)
NoHoveredOnFocus = bitLShift(1, 19), -- don't report as hovered when nav focus is on this item
NoSetKeyOwner = bitLShift(1, 20), -- don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey.MouseLeft!)
NoTestKeyOwner = bitLShift(1, 21), -- don't test key/input owner when polling the key (note: mouse buttons are keys! often, the key in question will be ImGuiKey.MouseLeft!)
NoFocus = bitLShift(1, 22) -- [EXPERIMENTAL: Not very well specced]. Don't focus parent window when clicking.
}
ImGuiButtonFlags.MouseButtonMask_ = bitOr(ImGuiButtonFlags.MouseButtonLeft, ImGuiButtonFlags.MouseButtonRight, ImGuiButtonFlags.MouseButtonMiddle) -- [Internal]
ImGuiButtonFlags.PressedOnMask_ = bitOr(ImGuiButtonFlags.PressedOnClick, ImGuiButtonFlags.PressedOnClickRelease, ImGuiButtonFlags.PressedOnClickReleaseAnywhere, ImGuiButtonFlags.PressedOnRelease, ImGuiButtonFlags.PressedOnDoubleClick, ImGuiButtonFlags.PressedOnDragDropHold)
ImGuiButtonFlags.PressedOnDefault_ = ImGuiButtonFlags.PressedOnClickRelease
ImGuiButtonFlags.NoKeyModifiers = ImGuiButtonFlags.NoKeyModsAllowed
-- Enumeration for PushStyleVar() / PopStyleVar() to temporarily modify the ImGuiStyle structure.
-- - The enum only refers to fields of ImGuiStyle which makes sense to be pushed/popped inside UI code.
-- During initialization or between frames, feel free to just poke into ImGuiStyle directly.
-- - When changing this enum, you need to update the associated internal table GStyleVarInfo[] accordingly. This is where we link enum values to members offset/type.
--- @enum ImGuiStyleVar
ImGuiStyleVar = {
-- Enum name -------------------------- -- Member in ImGuiStyle structure (see ImGuiStyle for descriptions)
Alpha = 0, -- float Alpha
DisabledAlpha = 1, -- float DisabledAlpha
WindowPadding = 2, -- ImVec2 WindowPadding
WindowRounding = 3, -- float WindowRounding
WindowBorderSize = 4, -- float WindowBorderSize
WindowMinSize = 5, -- ImVec2 WindowMinSize
WindowTitleAlign = 6, -- ImVec2 WindowTitleAlign
ChildRounding = 7, -- float ChildRounding
ChildBorderSize = 8, -- float ChildBorderSize
PopupRounding = 9, -- float PopupRounding
PopupBorderSize = 10, -- float PopupBorderSize
FramePadding = 11, -- ImVec2 FramePadding
FrameRounding = 12, -- float FrameRounding
FrameBorderSize = 13, -- float FrameBorderSize
ItemSpacing = 14, -- ImVec2 ItemSpacing
ItemInnerSpacing = 15, -- ImVec2 ItemInnerSpacing
IndentSpacing = 16, -- float IndentSpacing
CellPadding = 17, -- ImVec2 CellPadding
ScrollbarSize = 18, -- float ScrollbarSize
ScrollbarRounding = 19, -- float ScrollbarRounding
ScrollbarPadding = 20, -- float ScrollbarPadding
GrabMinSize = 21, -- float GrabMinSize
GrabRounding = 22, -- float GrabRounding
ImageRounding = 23, -- float ImageRounding
ImageBorderSize = 24, -- float ImageBorderSize
TabRounding = 25, -- float TabRounding
TabBorderSize = 26, -- float TabBorderSize
TabMinWidthBase = 27, -- float TabMinWidthBase
TabMinWidthShrink = 28, -- float TabMinWidthShrink
TabBarBorderSize = 29, -- float TabBarBorderSize
TabBarOverlineSize = 30, -- float TabBarOverlineSize
TableAngledHeadersAngle = 31, -- float TableAngledHeadersAngle
TableAngledHeadersTextAlign = 32, -- ImVec2 TableAngledHeadersTextAlign
TreeLinesSize = 33, -- float TreeLinesSize
TreeLinesRounding = 34, -- float TreeLinesRounding
MenuItemRounding = 35, -- float MenuItemRounding
SelectableRounding = 36, -- float SelectableRounding
DragDropTargetRounding = 37, -- float DragDropTargetRounding
ButtonTextAlign = 38, -- ImVec2 ButtonTextAlign
SelectableTextAlign = 39, -- ImVec2 SelectableTextAlign
SeparatorSize = 40, -- float SeparatorSize
SeparatorTextBorderSize = 41, -- float SeparatorTextBorderSize
SeparatorTextAlign = 42, -- ImVec2 SeparatorTextAlign
SeparatorTextPadding = 43, -- ImVec2 SeparatorTextPadding
DockingSeparatorSize = 44, -- float DockingSeparatorSize
COUNT = 45
}
-- Flags for ImGui.IsItemHovered(), ImGui.IsWindowHovered()
-- Note: if you are trying to check whether your mouse should be dispatched to Dear ImGui or to your app, you should use 'io.WantCaptureMouse' instead! Please read the FAQ!
-- Note: windows with the ImGuiWindowFlags.NoInputs flag are ignored by IsWindowHovered() calls.
--- @enum ImGuiHoveredFlags
ImGuiHoveredFlags = {
None = 0, -- Return true if directly over the item/window, not obstructed by another window, not obstructed by an active popup or modal blocking inputs under them.
ChildWindows = bitLShift(1, 0), -- IsWindowHovered() only: Return true if any children of the window is hovered
RootWindow = bitLShift(1, 1), -- IsWindowHovered() only: Test from root window (top most parent of the current hierarchy)
AnyWindow = bitLShift(1, 2), -- IsWindowHovered() only: Return true if any window is hovered
NoPopupHierarchy = bitLShift(1, 3), -- IsWindowHovered() only: Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow)
DockHierarchy = bitLShift(1, 4), -- IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow)
AllowWhenBlockedByPopup = bitLShift(1, 5), -- Return true even if a popup window is normally blocking access to this item/window
--ImGuiHoveredFlags.AllowWhenBlockedByModal = 1 << 6, -- Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet.
AllowWhenBlockedByActiveItem = bitLShift(1, 7), -- Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns.
AllowWhenOverlappedByItem = bitLShift(1, 8), -- IsItemHovered() only: Return true even if the item uses AllowOverlap mode and is overlapped by another hoverable item.
AllowWhenOverlappedByWindow = bitLShift(1, 9), -- IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window.
AllowWhenDisabled = bitLShift(1, 10), -- IsItemHovered() only: Return true even if the item is disabled
NoNavOverride = bitLShift(1, 11), -- IsItemHovered() only: Disable using keyboard/gamepad navigation state when active, always query mouse
-- Tooltips mode
-- - typically used in IsItemHovered() + SetTooltip() sequence.
-- - this is a shortcut to pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' where you can reconfigure desired behavior.
-- e.g. 'HoverFlagsForTooltipMouse' defaults to 'ImGuiHoveredFlags.Stationary | ImGuiHoveredFlags.DelayShort | ImGuiHoveredFlags.AllowWhenDisabled'.
-- - for frequently actioned or hovered items providing a tooltip, you want may to use ImGuiHoveredFlags.ForTooltip (stationary + delay) so the tooltip doesn't show too often.
-- - for items which main purpose is to be hovered, or items with low affordance, or in less consistent apps, prefer no delay or shorter delay.
ForTooltip = bitLShift(1, 12), -- Shortcut for standard flags when using IsItemHovered() + SetTooltip() sequence.
-- (Advanced) Mouse Hovering delays.
-- - generally you can use ImGuiHoveredFlags.ForTooltip to use application-standardized flags.
-- - use those if you need specific overrides.
Stationary = bitLShift(1, 13), -- Require mouse to be stationary for style.HoverStationaryDelay (~0.15 sec) _at least one time_. After this, can move on same item/window. Using the stationary test tends to reduces the need for a long delay.
DelayNone = bitLShift(1, 14), -- IsItemHovered() only: Return true immediately (default). As this is the default you generally ignore this.
DelayShort = bitLShift(1, 15), -- IsItemHovered() only: Return true after style.HoverDelayShort elapsed (~0.15 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item).
DelayNormal = bitLShift(1, 16), -- IsItemHovered() only: Return true after style.HoverDelayNormal elapsed (~0.40 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item).
NoSharedDelay = bitLShift(1, 17) -- IsItemHovered() only: Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays)
}
-- Flags for ImGui.IsWindowFocused()
--- @enum ImGuiFocusedFlags
ImGuiFocusedFlags = {
None = 0,
ChildWindows = bitLShift(1, 0), -- Return true if any children of the window is focused
RootWindow = bitLShift(1, 1), -- Test from root window (top most parent of the current hierarchy)
AnyWindow = bitLShift(1, 2), -- Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ!
NoPopupHierarchy = bitLShift(1, 3), -- Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow)
DockHierarchy = bitLShift(1, 4), -- Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow)
}
ImGuiFocusedFlags.RootAndChildWindows = bitOr(ImGuiFocusedFlags.RootWindow, ImGuiFocusedFlags.ChildWindows)
ImGuiHoveredFlags.AllowWhenOverlapped = bitOr(ImGuiHoveredFlags.AllowWhenOverlappedByItem, ImGuiHoveredFlags.AllowWhenOverlappedByWindow)
ImGuiHoveredFlags.RectOnly = bitOr(ImGuiHoveredFlags.AllowWhenBlockedByPopup, ImGuiHoveredFlags.AllowWhenBlockedByActiveItem, ImGuiHoveredFlags.AllowWhenOverlapped)
ImGuiHoveredFlags.RootAndChildWindows = bitOr(ImGuiHoveredFlags.RootWindow, ImGuiHoveredFlags.ChildWindows)
ImGuiHoveredFlags.DelayMask_ = bitOr(ImGuiHoveredFlags.DelayNone, ImGuiHoveredFlags.DelayShort, ImGuiHoveredFlags.DelayNormal, ImGuiHoveredFlags.NoSharedDelay)
ImGuiHoveredFlags.AllowedMaskForIsWindowHovered = bitOr(ImGuiHoveredFlags.ChildWindows, ImGuiHoveredFlags.RootWindow, ImGuiHoveredFlags.AnyWindow, ImGuiHoveredFlags.NoPopupHierarchy, ImGuiHoveredFlags.DockHierarchy, ImGuiHoveredFlags.AllowWhenBlockedByPopup, ImGuiHoveredFlags.AllowWhenBlockedByActiveItem, ImGuiHoveredFlags.ForTooltip, ImGuiHoveredFlags.Stationary)
ImGuiHoveredFlags.AllowedMaskForIsItemHovered = bitOr(ImGuiHoveredFlags.AllowWhenBlockedByPopup, ImGuiHoveredFlags.AllowWhenBlockedByActiveItem, ImGuiHoveredFlags.AllowWhenOverlapped, ImGuiHoveredFlags.AllowWhenDisabled, ImGuiHoveredFlags.NoNavOverride, ImGuiHoveredFlags.ForTooltip, ImGuiHoveredFlags.Stationary, ImGuiHoveredFlags.DelayMask_)
-- A key identifier (ImGuiKey.XXX or ImGuiMod_XXX value): can represent Keyboard, Mouse and Gamepad values.
-- All our named keys are >= 512. Keys value 0 to 511 are left unused and were legacy native/opaque key values (< 1.87).
-- Support for legacy keys was completely removed in 1.91.5.
-- Read details about the 1.87+ transition : https://github.com/ocornut/imgui/issues/4921
-- Note that "Keys" related to physical keys and are not the same concept as input "Characters", the latter are submitted via io.AddInputCharacter().
-- The keyboard key enum values are named after the keys on a standard US keyboard, and on other keyboard types the keys reported may not match the keycaps.
--- @enum ImGuiKey
ImGuiKey = {
-- Keyboard
None = 0,
NamedKey_BEGIN = 512, -- First valid key value (other than 0)
Tab = 512, -- == ImGuiKey.NamedKey_BEGIN
LeftArrow = 513,
RightArrow = 514,
UpArrow = 515,
DownArrow = 516,
PageUp = 517,
PageDown = 518,
Home = 519,
End = 520,
Insert = 521,
Delete = 522,
Backspace = 523,
Space = 524,
Enter = 525,
Escape = 526,
LeftCtrl = 527, LeftShift = 528, LeftAlt = 529, LeftSuper = 530,
RightCtrl = 531, RightShift = 532, RightAlt = 533, RightSuper = 534,
Menu = 535,
-- 0 ~ 9 (upstream ImGuiKey_0..ImGuiKey_9: K0..K9 in this port, ImGuiKey["0"]..ImGuiKey["9"] are aliases)
K0 = 536, K1 = 537, K2 = 538, K3 = 539, K4 = 540, K5 = 541, K6 = 542, K7 = 543, K8 = 544, K9 = 545,
["0"] = 536, ["1"] = 537, ["2"] = 538, ["3"] = 539, ["4"] = 540, ["5"] = 541, ["6"] = 542, ["7"] = 543, ["8"] = 544, ["9"] = 545,
A = 546, B = 547, C = 548, D = 549, E = 550, F = 551, G = 552, H = 553, I = 554, J = 555,
K = 556, L = 557, M = 558, N = 559, O = 560, P = 561, Q = 562, R = 563, S = 564, T = 565,
U = 566, V = 567, W = 568, X = 569, Y = 570, Z = 571,
F1 = 572, F2 = 573, F3 = 574, F4 = 575, F5 = 576, F6 = 577,
F7 = 578, F8 = 579, F9 = 580, F10 = 581, F11 = 582, F12 = 583,
F13 = 584, F14 = 585, F15 = 586, F16 = 587, F17 = 588, F18 = 589,
F19 = 590, F20 = 591, F21 = 592, F22 = 593, F23 = 594, F24 = 595,
Apostrophe = 596, Comma = 597, Minus = 598, Period = 599, Slash = 600, Semicolon = 601, Equal = 602, LeftBracket = 603, Backslash = 604, RightBracket = 605, GraveAccent = 606,
CapsLock = 607, ScrollLock = 608, NumLock = 609, PrintScreen = 610, Pause = 611,
Keypad0 = 612, Keypad1 = 613, Keypad2 = 614, Keypad3 = 615, Keypad4 = 616, Keypad5 = 617, Keypad6 = 618, Keypad7 = 619, Keypad8 = 620, Keypad9 = 621,
KeypadDecimal = 622, KeypadDivide = 623, KeypadMultiply = 624, KeypadSubtract = 625, KeypadAdd = 626, KeypadEnter = 627, KeypadEqual = 628,
AppBack = 629, -- Available on some keyboard/mouses. Often referred as "Browser Back"
AppForward = 630,
Oem102 = 631, -- Non-US backslash.
-- Gamepad
-- (analog values are 0.0f to 1.0f)
-- (download controller mapping PNG/PSD at http://dearimgui.com/controls_sheets)
-- XBOX | SWITCH | PLAYSTA. | -> ACTION
GamepadStart = 632, GamepadBack = 633,
GamepadFaceLeft = 634, GamepadFaceRight = 635, GamepadFaceUp = 636, GamepadFaceDown = 637,
GamepadDpadLeft = 638, GamepadDpadRight = 639, GamepadDpadUp = 640, GamepadDpadDown = 641,
GamepadL1 = 642, GamepadR1 = 643,
GamepadL2 = 644, GamepadR2 = 645,
GamepadL3 = 646, GamepadR3 = 647,
GamepadLStickLeft = 648, GamepadLStickRight = 649, GamepadLStickUp = 650, GamepadLStickDown = 651,
GamepadRStickLeft = 652, GamepadRStickRight = 653, GamepadRStickUp = 654, GamepadRStickDown = 655,
MouseLeft = 656, MouseRight = 657, MouseMiddle = 658, MouseX1 = 659, MouseX2 = 660, MouseWheelX = 661, MouseWheelY = 662,
ReservedForModCtrl = 663, ReservedForModShift = 664, ReservedForModAlt = 665, ReservedForModSuper = 666,
-- [Internal] If you need to iterate all keys (for e.g. an input mapper) you may use ImGuiKey.NamedKey_BEGIN..ImGuiKey.NamedKey_END.
NamedKey_END = 667
}
ImGuiKey.NamedKey_COUNT = ImGuiKey.NamedKey_END - ImGuiKey.NamedKey_BEGIN
-- Keyboard Modifiers (explicitly submitted by backend via AddKeyEvent() calls)
-- - Any functions taking a ImGuiKeyChord parameter can binary-or those with regular keys, e.g. Shortcut(ImGuiMod_Ctrl | ImGuiKey.S).
-- - Those are written back into io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper for convenience,
-- but may be accessed via standard key API such as IsKeyPressed(), IsKeyReleased(), querying duration etc.
-- - Code polling every key (e.g. an interface to detect a key press for input mapping) might want to ignore those
-- and prefer using the real keys (e.g. ImGuiKey.LeftCtrl, ImGuiKey.RightCtrl instead of ImGuiMod_Ctrl).
-- - In theory the value of keyboard modifiers should be roughly equivalent to a logical or of the equivalent left/right keys.
-- In practice: it's complicated; mods are often provided from different sources. Keyboard layout, IME, sticky keys and
-- backends tend to interfere and break that equivalence. The safer decision is to relay that ambiguity down to the end-user...
-- - On macOS, we swap Cmd(Super) and Ctrl keys at the time of the io.AddKeyEvent() call.
ImGuiMod_None = 0
ImGuiMod_Ctrl = bitLShift(1, 12) -- Ctrl (non-macOS), Cmd (macOS)
ImGuiMod_Shift = bitLShift(1, 13) -- Shift
ImGuiMod_Alt = bitLShift(1, 14) -- Option/Menu
ImGuiMod_Super = bitLShift(1, 15) -- Windows/Super (non-macOS), Ctrl (macOS)
ImGuiMod_Mask_ = 0xF000 -- 4-bits
-- Enumeration for PushStyleColor() / PopStyleColor()
--- @enum ImGuiCol
ImGuiCol = {
Text = 0,
TextDisabled = 1,
WindowBg = 2, -- Background of normal windows
ChildBg = 3, -- Background of child windows
PopupBg = 4, -- Background of popups, menus, tooltips windows
Border = 5,
BorderShadow = 6,
FrameBg = 7, -- Background of checkbox, radio button, plot, slider, text input
FrameBgHovered = 8,
FrameBgActive = 9,
TitleBg = 10, -- Title bar
TitleBgActive = 11, -- Title bar when focused
TitleBgCollapsed = 12, -- Title bar when collapsed
MenuBarBg = 13,
ScrollbarBg = 14,
ScrollbarGrab = 15,
ScrollbarGrabHovered = 16,
ScrollbarGrabActive = 17,
CheckMark = 18, -- Checkbox tick and RadioButton circle
CheckboxSelectedBg = 19, -- Checkbox background when Selected, otherwise use FrameBg
SliderGrab = 20,
SliderGrabActive = 21,
Button = 22,
ButtonHovered = 23,
ButtonActive = 24,
Header = 25, -- Header* colors are used for CollapsingHeader, TreeNode, Selectable, MenuItem
HeaderHovered = 26,
HeaderActive = 27,
Separator = 28,
SeparatorHovered = 29,
SeparatorActive = 30,
ResizeGrip = 31, -- Resize grip in lower-right and lower-left corners of windows.
ResizeGripHovered = 32,
ResizeGripActive = 33,
InputTextCursor = 34, -- InputText cursor/caret
TabHovered = 35, -- Tab background, when hovered
Tab = 36, -- Tab background, when tab-bar is focused & tab is unselected
TabSelected = 37, -- Tab background, when tab-bar is focused & tab is selected
TabSelectedOverline = 38, -- Tab horizontal overline, when tab-bar is focused & tab is selected
TabDimmed = 39, -- Tab background, when tab-bar is unfocused & tab is unselected
TabDimmedSelected = 40, -- Tab background, when tab-bar is unfocused & tab is selected
TabDimmedSelectedOverline = 41, -- ..horizontal overline, when tab-bar is unfocused & tab is selected
DockingPreview = 42, -- Preview overlay color when about to docking something
DockingEmptyBg = 43, -- Background color for empty node (e.g. CentralNode with no window docked into it)
PlotLines = 44,
PlotLinesHovered = 45,
PlotHistogram = 46,
PlotHistogramHovered = 47,
TableHeaderBg = 48, -- Table header background
TableBorderStrong = 49, -- Table outer and header borders (prefer using Alpha=1.0 here)
TableBorderLight = 50, -- Table inner borders (prefer using Alpha=1.0 here)
TableRowBg = 51, -- Table row background (even rows)
TableRowBgAlt = 52, -- Table row background (odd rows)
TextLink = 53, -- Hyperlink color
TextSelectedBg = 54, -- Selected text inside an InputText
TreeLines = 55, -- Tree node hierarchy outlines when using ImGuiTreeNodeFlags.DrawLines
DragDropTarget = 56, -- Rectangle border highlighting a drop target
DragDropTargetBg = 57, -- Rectangle background highlighting a drop target
UnsavedMarker = 58, -- Unsaved Document marker (in window title and tabs)
NavCursor = 59, -- Color of keyboard/gamepad navigation cursor/rectangle, when visible
NavWindowingHighlight = 60, -- Highlight window when using Ctrl+Tab
NavWindowingDimBg = 61, -- Darken/colorize entire screen behind the Ctrl+Tab window list, when active
ModalWindowDimBg = 62, -- Darken/colorize entire screen behind a modal window, when one is active
COUNT = 63
}
-- Backend capabilities flags stored in io.BackendFlags. Set by imgui_impl_xxx or custom backend.
--- @enum ImGuiBackendFlags
ImGuiBackendFlags = {
None = 0,
HasGamepad = bitLShift(1, 0), -- Backend Platform supports gamepad and currently has one connected.
HasMouseCursors = bitLShift(1, 1), -- Backend Platform supports honoring GetMouseCursor() value to change the OS cursor shape.
HasSetMousePos = bitLShift(1, 2), -- Backend Platform supports io.WantSetMousePos requests to reposition the OS mouse position (only used if io.ConfigNavMoveSetMousePos is set).
RendererHasVtxOffset = bitLShift(1, 3), -- Backend Renderer supports ImDrawCmd::VtxOffset. This enables output of large meshes (64K+ vertices) while still using 16-bit indices.
RendererHasTextures = bitLShift(1, 4), -- Backend Renderer supports ImTextureData requests to create/update/destroy textures. This enables incremental texture updates and texture reloads. See https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md for instructions on how to upgrade your custom backend.
-- [BETA] Multi-Viewports
RendererHasViewports = bitLShift(1, 10), -- Backend Renderer supports multiple viewports.
PlatformHasViewports = bitLShift(1, 11), -- Backend Platform supports multiple viewports.
HasMouseHoveredViewport = bitLShift(1, 12), -- Backend Platform supports calling io.AddMouseViewportEvent() with the viewport under the mouse. IF POSSIBLE, ignore viewports with the ImGuiViewportFlags.NoInputs flag (Win32 backend, GLFW 3.30+ backend can do this, SDL backend cannot). If this cannot be done, Dear ImGui needs to use a flawed heuristic to find the viewport under.
HasParentViewport = bitLShift(1, 13) -- Backend Platform supports honoring viewport->ParentViewport/ParentViewportId value, by applying the corresponding parent/child relationship at the Platform level. Child windows always appear in front of their parent window.
}
-- Flags for ImGui.BeginDragDropSource(), ImGui.AcceptDragDropPayload()
--- @enum ImGuiDragDropFlags
ImGuiDragDropFlags = {
None = 0,
-- BeginDragDropSource() flags
SourceNoPreviewTooltip = bitLShift(1, 0), -- Disable preview tooltip. By default, a successful call to BeginDragDropSource opens a tooltip so you can display a preview or description of the source contents. This flag disables this behavior.
SourceNoDisableHover = bitLShift(1, 1), -- By default, when dragging we clear data so that IsItemHovered() will return false, to avoid subsequent user code submitting tooltips. This flag disables this behavior so you can still call IsItemHovered() on the source item.
SourceNoHoldToOpenOthers = bitLShift(1, 2), -- Disable the behavior that allows to open tree nodes and collapsing header by holding over them while dragging a source item.
SourceAllowNullID = bitLShift(1, 3), -- Allow items such as Text(), Image() that have no unique identifier to be used as drag source, by manufacturing a temporary identifier based on their window-relative position. This is extremely unusual within the dear imgui ecosystem and so we made it explicit.
SourceExtern = bitLShift(1, 4), -- External source (from outside of dear imgui), won't attempt to read current item/window info. Will always return true. Only one Extern source can be active simultaneously.
PayloadAutoExpire = bitLShift(1, 5), -- Automatically expire the payload if the source cease to be submitted (otherwise payloads are persisting while being dragged)
PayloadNoCrossContext = bitLShift(1, 6), -- Hint to specify that the payload may not be copied outside current dear imgui context.
PayloadNoCrossProcess = bitLShift(1, 7), -- Hint to specify that the payload may not be copied outside current process.
-- AcceptDragDropPayload() flags
AcceptBeforeDelivery = bitLShift(1, 10), -- AcceptDragDropPayload() will returns true even before the mouse button is released. You can then call IsDelivery() to test if the payload needs to be delivered.
AcceptNoDrawDefaultRect = bitLShift(1, 11), -- Do not draw the default highlight rectangle when hovering over target.
AcceptNoPreviewTooltip = bitLShift(1, 12), -- Request hiding the BeginDragDropSource tooltip from the BeginDragDropTarget site.
AcceptDrawAsHovered = bitLShift(1, 13) -- Accepting item will render as if hovered. Useful for e.g. a Button() used as a drop target.
}
ImGuiDragDropFlags.AcceptPeekOnly = bitOr(ImGuiDragDropFlags.AcceptBeforeDelivery, ImGuiDragDropFlags.AcceptNoDrawDefaultRect) -- For peeking ahead and inspecting the payload before delivery.
-- Standard Drag and Drop payload types. You can define you own payload types using short strings. Types starting with '_' are defined by Dear ImGui.
IMGUI_PAYLOAD_TYPE_COLOR_3F = "_COL3F" -- float[3]: Standard type for colors, without alpha. User code may use this type. [Port] payload.Data = { r, g, b } (1-based)
IMGUI_PAYLOAD_TYPE_COLOR_4F = "_COL4F" -- float[4]: Standard type for colors. User code may use this type. [Port] payload.Data = { r, g, b, a } (1-based)
-- Helpers macros to generate 32-bit encoded colors
IM_COL32_R_SHIFT = 0
IM_COL32_G_SHIFT = 8
IM_COL32_B_SHIFT = 16
IM_COL32_A_SHIFT = 24
IM_COL32_A_MASK = 0xFF000000
--- @param R ImU32
--- @param G ImU32
--- @param B ImU32
--- @param A ImU32
IM_COL32_WHITE = IM_COL32(255, 255, 255, 255)
IM_COL32_BLACK = IM_COL32(0, 0, 0, 255)
IM_COL32_BLACK_TRANS = IM_COL32(0, 0, 0, 0)
-- Flags for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() functions.
-- - IMPORTANT: If you ever used the left mouse button with BeginPopupContextXXX() helpers before 1.92.6: Read "API BREAKING CHANGES" 2026/01/07 (1.92.6) entry in imgui.cpp or GitHub topic #9157.
-- - Multiple buttons currently cannot be combined/or-ed in those functions (we could allow it later).
--- @enum ImGuiPopupFlags
ImGuiPopupFlags = {
None = 0,
MouseButtonLeft = bitLShift(1, 2), -- For BeginPopupContext*(): open on Left Mouse release. Only one button allowed!
MouseButtonRight = bitLShift(2, 2), -- For BeginPopupContext*(): open on Right Mouse release. Only one button allowed! (default)
MouseButtonMiddle = bitLShift(3, 2), -- For BeginPopupContext*(): open on Middle Mouse release. Only one button allowed!
NoReopen = bitLShift(1, 5), -- For OpenPopup*(), BeginPopupContext*(): don't reopen same popup if already open (won't reposition, won't reinitialize navigation)
--ImGuiPopupFlags.NoReopenAlwaysNavInit = 1 << 6, -- For OpenPopup*(), BeginPopupContext*(): focus and initialize navigation even when not reopening.
NoOpenOverExistingPopup = bitLShift(1, 7), -- For OpenPopup*(), BeginPopupContext*(): don't open if there's already a popup at the same level of the popup stack
NoOpenOverItems = bitLShift(1, 8), -- For BeginPopupContextWindow(): don't return true when hovering items, only when hovering empty space
AnyPopupId = bitLShift(1, 10), -- For IsPopupOpen(): ignore the ImGuiID parameter and test for any popup.
AnyPopupLevel = bitLShift(1, 11) -- For IsPopupOpen(): search/test at any level of the popup stack (default test in the current level)
}
ImGuiPopupFlags.AnyPopup = bitOr(ImGuiPopupFlags.AnyPopupId, ImGuiPopupFlags.AnyPopupLevel)
ImGuiPopupFlags.MouseButtonShift_ = 2 -- [Internal]
ImGuiPopupFlags.MouseButtonMask_ = 0x0C -- [Internal]
ImGuiPopupFlags.InvalidMask_ = 0x03 -- [Internal] Reserve legacy bits 0-1 to detect incorrectly passing 1 or 2 to the function.
-- Flags for ImGui.BeginCombo()
--- @enum ImGuiComboFlags
ImGuiComboFlags = {
None = 0,
PopupAlignLeft = bitLShift(1, 0), -- Align the popup toward the left by default
HeightSmall = bitLShift(1, 1), -- Max ~4 items visible. Tip: If you want your combo popup to be a specific size you can use SetNextWindowSizeConstraints() prior to calling BeginCombo()
HeightRegular = bitLShift(1, 2), -- Max ~8 items visible (default)
HeightLarge = bitLShift(1, 3), -- Max ~20 items visible
HeightLargest = bitLShift(1, 4), -- As many fitting items as possible
NoArrowButton = bitLShift(1, 5), -- Display on the preview box without the square arrow button
NoPreview = bitLShift(1, 6), -- Display only a square arrow button
WidthFitPreview = bitLShift(1, 7) -- Width dynamically calculated from preview contents
}
ImGuiComboFlags.HeightMask_ = bitOr(ImGuiComboFlags.HeightSmall, ImGuiComboFlags.HeightRegular, ImGuiComboFlags.HeightLarge, ImGuiComboFlags.HeightLargest)
ImGuiComboFlags.CustomPreview = bitLShift(1, 20) -- [Luau] obsolete: removed in 1.93.0 (BeginComboPreview() no longer needs a flag), kept as an ignored value
-- Flags for ImGui.Selectable()
--- @enum ImGuiSelectableFlags
ImGuiSelectableFlags = {
None = 0,
NoAutoClosePopups = bitLShift(1, 0), -- Clicking this doesn't close parent popup window (overrides ImGuiItemFlags.AutoClosePopups)
SpanAllColumns = bitLShift(1, 1), -- Frame will span all columns of its container table (text will still fit in current column)
AllowDoubleClick = bitLShift(1, 2), -- Generate press events on double clicks too
Disabled = bitLShift(1, 3), -- Cannot be selected, display grayed out text
AllowOverlap = bitLShift(1, 4), -- Hit testing will allow subsequent widgets to overlap this one. Require previous frame HoveredId to match before being usable. Shortcut to calling SetNextItemAllowOverlap().
Highlight = bitLShift(1, 5), -- Make the item be displayed as if it is hovered
SelectOnNav = bitLShift(1, 6), -- Auto-select when moved into, unless Ctrl is held. Automatic when in a BeginMultiSelect() block.
-- NB: need to be in sync with last value of ImGuiSelectableFlags_
NoHoldingActiveID = bitLShift(1, 20),
SelectOnClick = bitLShift(1, 22), -- Override button behavior to react on Click (default is Click+Release)
SelectOnRelease = bitLShift(1, 23), -- Override button behavior to react on Release (default is Click+Release)
SpanAvailWidth = bitLShift(1, 24), -- Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus)
SetNavIdOnHover = bitLShift(1, 25), -- Set Nav/Focus ID on mouse hover (used by MenuItem)
NoPadWithHalfSpacing = bitLShift(1, 26), -- Disable padding each side with ItemSpacing * 0.5f
NoSetKeyOwner = bitLShift(1, 27), -- Don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey.MouseLeft!)
}
-- Flags for ColorEdit3() / ColorEdit4() / ColorPicker3() / ColorPicker4() / ColorButton()
--- @enum ImGuiColorEditFlags
ImGuiColorEditFlags = {
None = 0,
NoAlpha = bitLShift(1, 1), -- ColorEdit, ColorPicker, ColorButton: ignore Alpha component (will only read 3 components from the input pointer).
NoPicker = bitLShift(1, 2), -- ColorEdit: disable picker when clicking on color square.
NoOptions = bitLShift(1, 3), -- ColorEdit: disable toggling options menu when right-clicking on inputs/small preview.
NoSmallPreview = bitLShift(1, 4), -- ColorEdit, ColorPicker: disable color square preview next to the inputs. (e.g. to show only the inputs)
NoInputs = bitLShift(1, 5), -- ColorEdit, ColorPicker: disable inputs sliders/text widgets (e.g. to show only the small preview color square).
NoTooltip = bitLShift(1, 6), -- ColorEdit, ColorPicker, ColorButton: disable tooltip when hovering the preview.
NoLabel = bitLShift(1, 7), -- ColorEdit, ColorPicker: disable display of inline text label (the label is still forwarded to the tooltip and picker).
NoSidePreview = bitLShift(1, 8), -- ColorPicker: disable bigger color preview on right side of the picker, use small color square preview instead.
NoDragDrop = bitLShift(1, 9), -- ColorEdit: disable drag and drop target/source. ColorButton: disable drag and drop source.
NoBorder = bitLShift(1, 10), -- ColorButton: disable border (which is enforced by default)
NoColorMarkers = bitLShift(1, 11), -- ColorEdit: disable rendering R/G/B/A color marker. May also be disabled globally by setting style.ColorMarkerSize = 0.
-- Alpha preview
AlphaOpaque = bitLShift(1, 12), -- ColorEdit, ColorPicker, ColorButton: disable alpha in the preview,. Contrary to _NoAlpha it may still be edited when calling ColorEdit4()/ColorPicker4(). For ColorButton() this does the same as _NoAlpha.
AlphaNoBg = bitLShift(1, 13), -- ColorEdit, ColorPicker, ColorButton: disable rendering a checkerboard background behind transparent color.
AlphaPreviewHalf = bitLShift(1, 14), -- ColorEdit, ColorPicker, ColorButton: display half opaque / half transparent preview.
-- User Options (right-click on widget to change some of them)
AlphaBar = bitLShift(1, 18), -- ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker.
HDR = bitLShift(1, 19), -- (WIP) ColorEdit: Currently only disable 0.0f..1.0f limits in RGBA edition (note: you probably want to use ImGuiColorEditFlags.Float flag as well).
DisplayRGB = bitLShift(1, 20), -- [Display] -- ColorEdit: override _display_ type among RGB/HSV/Hex. ColorPicker: select any combination using one or more of RGB/HSV/Hex.
DisplayHSV = bitLShift(1, 21), -- [Display] -- "
DisplayHex = bitLShift(1, 22), -- [Display] -- "
Uint8 = bitLShift(1, 23), -- [DataType] -- ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0..255.
Float = bitLShift(1, 24), -- [DataType] -- ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0.0f..1.0f floats instead of 0..255 integers. No round-trip of value via integers.
PickerHueBar = bitLShift(1, 25), -- [Picker] -- ColorPicker: bar for Hue, rectangle for Sat/Value.
PickerHueWheel = bitLShift(1, 26), -- [Picker] -- ColorPicker: wheel for Hue, triangle for Sat/Value.
PickerNoRotate = bitLShift(1, 27), -- [Picker] -- ColorPicker: disable rotating Sat/Value triangle. Best set in io.ConfigColorEditFlags once.
InputRGB = bitLShift(1, 28), -- [Input] -- ColorEdit, ColorPicker: input and output data in RGB format.
InputHSV = bitLShift(1, 29) -- [Input] -- ColorEdit, ColorPicker: input and output data in HSV format.
}
-- Defaults Options copied to io.ConfigColorEditFlags during initialization.
-- The intent is that you probably don't want to override them in most of your calls.
-- Let the user choose via the option menu and/or modify io.ConfigColorEditFlags directly during startup if you want.
ImGuiColorEditFlags.DefaultOptions_ = bitOr(ImGuiColorEditFlags.Uint8, ImGuiColorEditFlags.DisplayRGB, ImGuiColorEditFlags.InputRGB, ImGuiColorEditFlags.PickerHueBar)
-- [Internal] Masks
ImGuiColorEditFlags.AlphaMask_ = bitOr( ImGuiColorEditFlags.NoAlpha, ImGuiColorEditFlags.AlphaOpaque, ImGuiColorEditFlags.AlphaNoBg, ImGuiColorEditFlags.AlphaPreviewHalf )
ImGuiColorEditFlags.DisplayMask_ = bitOr( ImGuiColorEditFlags.DisplayRGB, ImGuiColorEditFlags.DisplayHSV, ImGuiColorEditFlags.DisplayHex )
ImGuiColorEditFlags.DataTypeMask_ = bitOr( ImGuiColorEditFlags.Uint8, ImGuiColorEditFlags.Float )
ImGuiColorEditFlags.PickerMask_ = bitOr( ImGuiColorEditFlags.PickerHueWheel, ImGuiColorEditFlags.PickerHueBar )
ImGuiColorEditFlags.InputMask_ = bitOr( ImGuiColorEditFlags.InputRGB, ImGuiColorEditFlags.InputHSV )
-- Flags for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc.
-- We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them.
-- (Those are per-item flags. There is shared behavior flag too: ImGuiIO: io.ConfigDragClickToInputText)
--- @enum ImGuiSliderFlags
ImGuiSliderFlags = {
None = 0,
Logarithmic = bitLShift(1, 5), -- Make the widget logarithmic (linear otherwise). Consider using ImGuiSliderFlags.NoRoundToFormat with this if using a format-string with small amount of digits.
NoRoundToFormat = bitLShift(1, 6), -- Disable rounding underlying value to match precision of the display format string (e.g. %.3f values are rounded to those 3 digits).
NoInput = bitLShift(1, 7), -- Disable Ctrl+Click or Enter key allowing to input text directly into the widget.
WrapAround = bitLShift(1, 8), -- Enable wrapping around from max to min and from min to max. Only supported by DragXXX() functions for now.
ClampOnInput = bitLShift(1, 9), -- Clamp value to min/max bounds when input manually with Ctrl+Click. By default Ctrl+Click allows going out of bounds.
ClampZeroRange = bitLShift(1, 10), -- Clamp even if min==max==0.0f. Otherwise due to legacy reason DragXXX functions don't clamp with those values. When your clamping limits are dynamic you almost always want to use it.
NoSpeedTweaks = bitLShift(1, 11), -- Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic.
ColorMarkers = bitLShift(1, 12), -- DragScalarN(), SliderScalarN(): Draw R/G/B/A color markers on each component.
--ImGuiSliderFlags.LiveEditOnInput = 1 << 13, -- Shortcut to enable LiveEdit for this field.
--ImGuiSliderFlags.NoLiveEditOnInput= 1 << 14, -- Shortcut to disable LiveEdit for this field.
InvalidMask_ = 0x7000000F, -- [Internal] We treat using those bits as being potentially a 'float power' argument from legacy API (obsoleted 2020-08) that has got miscast to this enum, and will trigger an assert if needed.
}
ImGuiSliderFlags.AlwaysClamp = bitOr(ImGuiSliderFlags.ClampOnInput, ImGuiSliderFlags.ClampZeroRange)
ImGuiSliderFlags.Vertical = bitLShift(1, 20) -- Should this slider be orientated vertically?
ImGuiSliderFlags.ReadOnly = bitLShift(1, 21) -- Consider using g.NextItemData.ItemFlags |= ImGuiItemFlags.ReadOnly instead.
-- [ALPHA] Rarely used / very advanced uses only. Use with SetNextWindowClass() and DockSpace() functions.
-- Important: the content of this class is still highly WIP and likely to change and be refactored
-- before we stabilize Docking features. Please be mindful if using this.
-- Provide hints:
-- - To the platform backend via altered viewport flags (enable/disable OS decoration, OS task bar icons, etc.)
-- - To the platform backend for OS level parent/child relationships of viewport (otherwise: default is configured via io.ConfigViewportsNoDefaultParent)
-- - To the docking system for various options and filtering.
--- @class ImGuiWindowClass
--- @field ClassId ImGuiID
--- @field ParentViewportId ImGuiID
--- @field FocusRouteParentWindowId ImGuiID
--- @field ViewportFlagsOverrideSet ImGuiViewportFlags
--- @field ViewportFlagsOverrideClear ImGuiViewportFlags
--- @return ImGuiWindowClass
--- @nodiscard
function ImGuiWindowClass()
return {
ClassId = 0, -- User data. 0 = Default class (unclassed). Windows of different classes cannot be docked with each others.
ParentViewportId = 0xFFFFFFFF, -- Hint for the platform backend. -1: use default. 0: request platform backend to not parent the platform. != 0: request platform backend to create a parent<>child relationship between the platform windows. Not conforming backends are free to e.g. parent every viewport to the main viewport or not.
FocusRouteParentWindowId = 0, -- ID of parent window for shortcut focus route evaluation, e.g. Shortcut() call from Parent Window will succeed when this window is focused.
ViewportFlagsOverrideSet = 0, -- Viewport flags to set when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis.
ViewportFlagsOverrideClear = 0, -- Viewport flags to clear when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis.
TabItemFlagsOverrideSet = 0, -- [EXPERIMENTAL] TabItem flags to set when a window of this class gets submitted into a dock node tab bar. May use with ImGuiTabItemFlags.Leading or ImGuiTabItemFlags.Trailing.
DockNodeFlagsOverrideSet = 0, -- [EXPERIMENTAL] Dock node flags to set when a window of this class is hosted by a dock node (it doesn't have to be selected!)
DockingAlwaysTabBar = false, -- Set to true to enforce single floating windows of this class always having their own docking node (equivalent of setting the global io.ConfigDockingAlwaysTabBar)
PlatformIconData = nil, -- void* to hold custom data structure for the OS / platform to specify an icon. Currently unused for exposed to allow experiments.
DockingAllowUnclassed = true -- Set to true to allow windows of this class to be docked/merged with an unclassed window. -- FIXME-DOCK: Move to DockNodeFlags override?
}
end
-- Flags for ImGui.InputText()
-- (Those are per-item flags. There are shared flags in ImGuiIO: io.ConfigInputTextCursorBlink and io.ConfigInputTextEnterKeepActive)
--- @enum ImGuiInputTextFlags
ImGuiInputTextFlags = {
-- Basic filters (also see ImGuiInputTextFlags.CallbackCharFilter)
None = 0,
-- Basic filters (also see ImGuiInputTextFlags.CallbackCharFilter)
CharsDecimal = bitLShift(1, 0), -- Allow 0123456789.+-*/
CharsHexadecimal = bitLShift(1, 1), -- Allow 0123456789ABCDEFabcdef
CharsScientific = bitLShift(1, 2), -- Allow 0123456789.+-*/eE (Scientific notation input)
CharsUppercase = bitLShift(1, 3), -- Turn a..z into A..Z
CharsNoBlank = bitLShift(1, 4), -- Filter out spaces, tabs
-- Inputs
AllowTabInput = bitLShift(1, 5), -- Pressing TAB input a '\t' character into the text field
EnterReturnsTrue = bitLShift(1, 6), -- Return 'true' when Enter is pressed (as opposed to every time the value was modified). Consider disabling LiveEdit! or using IsItemDeactivatedAfterEdit() instead!
EscapeClearsAll = bitLShift(1, 7), -- Escape key clears content if not empty, and deactivate otherwise (contrast to default behavior of Escape to revert)
CtrlEnterForNewLine = bitLShift(1, 8), -- In multi-line mode: validate with Enter, add new line with Ctrl+Enter (default is opposite: validate with Ctrl+Enter, add line with Enter). Note that Shift+Enter always enter a new line either way.
-- Other options
ReadOnly = bitLShift(1, 9), -- Read-only mode
Password = bitLShift(1, 10), -- Password mode, display all characters as '*', disable copy
AlwaysOverwrite = bitLShift(1, 11), -- Overwrite mode
AutoSelectAll = bitLShift(1, 12), -- Select entire text when first taking mouse focus
ParseEmptyRefVal = bitLShift(1, 13), -- InputFloat(), InputInt(), InputScalar() etc. only: parse empty string as zero value.
DisplayEmptyRefVal = bitLShift(1, 14), -- InputFloat(), InputInt(), InputScalar() etc. only: when value is zero, do not display it. Generally used with ImGuiInputTextFlags.ParseEmptyRefVal.
NoHorizontalScroll = bitLShift(1, 15), -- Disable following the cursor horizontally
NoUndoRedo = bitLShift(1, 16), -- Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID().
-- Elide display / Alignment
ElideLeft = bitLShift(1, 17), -- When text doesn't fit, elide left side to ensure right side stays visible. Useful for path/filenames. Single-line only!
-- Callback features
CallbackCompletion = bitLShift(1, 18), -- Callback on pressing TAB (for completion handling)
CallbackHistory = bitLShift(1, 19), -- Callback on pressing Up/Down arrows (for history handling)
CallbackAlways = bitLShift(1, 20), -- Callback on each iteration. User code may query cursor position, modify text buffer.
CallbackCharFilter = bitLShift(1, 21), -- Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard.
CallbackResize = bitLShift(1, 22), -- Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this)
CallbackEdit = bitLShift(1, 23), -- Callback on any edit. Note that InputText() already returns true on edit + you can always use IsItemEdited(). The callback is useful to manipulate the underlying buffer while focus is active.
-- Multi-line Word-Wrapping [BETA]
WordWrap = bitLShift(1, 24), -- InputTextMultiline(): word-wrap lines that are too long.
-- [Internal]
Multiline = bitLShift(1, 26), -- For internal use by InputTextMultiline()
TempInput = bitLShift(1, 27), -- For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match.
LocalizeDecimalPoint = bitLShift(1, 28), -- For internal use by InputScalar() and TempInputScalar()
}
-------------------------------------------------------------------------------
-- [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload)
-------------------------------------------------------------------------------
-- Shared state of InputText(), passed as an argument to your callback when a ImGuiInputTextFlags.Callback* flag is used.
-- The callback function should return 0 by default.
-- Callbacks (follow a flag name and see comments in ImGuiInputTextFlags_ declarations for more details)
-- - ImGuiInputTextFlags.CallbackEdit: Callback on buffer edit. Note that InputText() already returns true on edit + you can always use IsItemEdited(). The callback is useful to manipulate the underlying buffer while focus is active.
-- - ImGuiInputTextFlags.CallbackAlways: Callback on each iteration
-- - ImGuiInputTextFlags.CallbackCompletion: Callback on pressing TAB
-- - ImGuiInputTextFlags.CallbackHistory: Callback on pressing Up/Down arrows
-- - ImGuiInputTextFlags.CallbackCharFilter: Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard.
-- - ImGuiInputTextFlags.CallbackResize: Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow.
--- @class ImGuiInputTextCallbackData
--- @return ImGuiInputTextCallbackData
-- Buf is a 1-based zero-terminated byte table; positions (CursorPos, pos...) are 0-based byte offsets like upstream.
MT.ImGuiInputTextCallbackData = {}
MT.ImGuiInputTextCallbackData.__index = MT.ImGuiInputTextCallbackData
function ImGuiInputTextCallbackData()
return setmetatable({}, MT.ImGuiInputTextCallbackData)
end
function MT.ImGuiInputTextCallbackData:DeleteChars(pos, bytes_count)
assert(pos + bytes_count <= self.BufTextLen)
local buf = self.Buf
local n = self.BufTextLen - bytes_count - pos + 1 -- includes zero terminator
for i = 1, n do buf[pos + i] = buf[pos + bytes_count + i] end
if self.CursorPos >= pos + bytes_count then
self.CursorPos = self.CursorPos - bytes_count
elseif self.CursorPos >= pos then
self.CursorPos = pos
end
self.SelectionStart = self.CursorPos; self.SelectionEnd = self.CursorPos
self.BufDirty = true
self.BufTextLen = self.BufTextLen - bytes_count
end
--- @param new_text string
function MT.ImGuiInputTextCallbackData:InsertChars(pos, new_text)
local new_text_len = #new_text
if new_text_len == 0 then return end
local is_resizable = bit32.band(self.Flags, 4194304 --[[ImGuiInputTextFlags.CallbackResize]]) ~= 0
-- We support partial insertion
local avail = self.BufSize - 1 - self.BufTextLen
if not is_resizable and new_text_len > avail then
new_text_len = avail -- ponytail: truncates by bytes, not to the closest UTF-8 codepoint
end
if new_text_len <= 0 then return end
-- ponytail: CallbackResize buffer growth not supported, text is truncated at BufSize instead
if self.BufTextLen + new_text_len + 1 > self.BufSize then
new_text_len = self.BufSize - 1 - self.BufTextLen
if new_text_len <= 0 then return end
end
local buf = self.Buf
for i = self.BufTextLen, pos + 1, -1 do buf[i + new_text_len] = buf[i] end
for i = 1, new_text_len do buf[pos + i] = string.byte(new_text, i) end
buf[self.BufTextLen + new_text_len + 1] = 0
self.BufDirty = true
self.BufTextLen = self.BufTextLen + new_text_len
if self.CursorPos >= pos then
self.CursorPos = self.CursorPos + new_text_len
end
self.SelectionStart = self.CursorPos; self.SelectionEnd = self.CursorPos
end
function MT.ImGuiInputTextCallbackData:SelectAll() self.SelectionStart = 0; self.CursorPos = self.BufTextLen; self.SelectionEnd = self.BufTextLen end
function MT.ImGuiInputTextCallbackData:SetSelection(s, e) assert(s >= 0 and s <= self.BufTextLen); 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:HasSelection() return self.SelectionStart ~= self.SelectionEnd end
-- Callback and functions types
--- @alias ImGuiInputTextCallback fun(data: ImGuiInputTextCallbackData)
--- @alias ImGuiMemAllocFunc fun(T: function, start_idx: int, end_idx: int, userdata: any): table
--- @alias ImGuiMemFreeFunc fun(owner: table, field: string, userdata: any)
-- ImDrawCallback: Draw callbacks for advanced uses [configurable type: override in imconfig.h]
-- NB: You most likely do NOT need to use draw callbacks just to create your own widget or customized UI rendering,
-- you can poke into the draw list for that! Draw callback may be useful for example to:
-- A) Change your GPU render state,
-- B) render a complex 3D scene inside a UI element without an intermediate texture/render target, etc.
-- The expected behavior from your rendering function is 'if (cmd.UserCallback != nil) { cmd.UserCallback(parent_list, cmd); } else { RenderTriangles() }'
--- @alias ImDrawCallback fun(parent_list: ImDrawList, cmd: ImDrawCmd)
-- Flags for ImGui.TreeNodeEx(), ImGui.CollapsingHeader*()
--- @enum ImGuiTreeNodeFlags
ImGuiTreeNodeFlags = {
None = 0,
Selected = bitLShift(1, 0), -- Draw as selected
Framed = bitLShift(1, 1), -- Draw frame with background (e.g. for CollapsingHeader)
AllowOverlap = bitLShift(1, 2), -- Hit testing will allow subsequent widgets to overlap this one. Require previous frame HoveredId to match before being usable. Shortcut to calling SetNextItemAllowOverlap().
NoTreePushOnOpen = bitLShift(1, 3), -- Don't do a TreePush() when open (e.g. for CollapsingHeader) = no extra indent nor pushing on ID stack
NoAutoOpenOnLog = bitLShift(1, 4), -- Don't automatically and temporarily open node when Logging is active (by default logging will automatically open tree nodes)
DefaultOpen = bitLShift(1, 5), -- Default node to be open
OpenOnDoubleClick = bitLShift(1, 6), -- Open on double-click instead of simple click (default for multi-select unless any _OpenOnXXX behavior is set explicitly). Both behaviors may be combined.
OpenOnArrow = bitLShift(1, 7), -- Open when clicking on the arrow part (default for multi-select unless any _OpenOnXXX behavior is set explicitly). Both behaviors may be combined.
Leaf = bitLShift(1, 8), -- No collapsing, no arrow (use as a convenience for leaf nodes). Note: will always open a tree/id scope and return true. If you never use that scope, add ImGuiTreeNodeFlags.NoTreePushOnOpen.
Bullet = bitLShift(1, 9), -- Display a bullet instead of arrow. IMPORTANT: node can still be marked open/close if you don't set the _Leaf flag!
FramePadding = bitLShift(1, 10), -- Use FramePadding (even for an unframed text node) to vertically align text baseline to regular widget height. Equivalent to calling AlignTextToFramePadding() before the node.
SpanAvailWidth = bitLShift(1, 11), -- Extend hit box to the right-most edge, even if not framed. This is not the default in order to allow adding other items on the same line without using AllowOverlap mode.
SpanFullWidth = bitLShift(1, 12), -- Extend hit box to the left-most and right-most edges (cover the indent area).
SpanLabelWidth = bitLShift(1, 13), -- Narrow hit box + narrow hovering highlight, will only cover the label text.
SpanAllColumns = bitLShift(1, 14), -- Frame will span all columns of its container table (label will still fit in current column)
LabelSpanAllColumns = bitLShift(1, 15), -- Label will span all columns of its container table
--ImGuiTreeNodeFlags.NoScrollOnOpen = 1 << 16, -- FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible
NavLeftJumpsToParent = bitLShift(1, 17), -- Nav: left arrow moves back to parent. This is processed in TreePop() when there's an unfulfilled Left nav request remaining.
-- [EXPERIMENTAL] Draw lines connecting TreeNode hierarchy. Discuss in GitHub issue #2920.
-- Default value is pulled from style.TreeLinesFlags. May be overridden in TreeNode calls.
DrawLinesNone = bitLShift(1, 18), -- No lines drawn
DrawLinesFull = bitLShift(1, 19), -- Horizontal lines to child nodes. Vertical line drawn down to TreePop() position: cover full contents. Faster (for large trees).
DrawLinesToNodes = bitLShift(1, 20), -- Horizontal lines to child nodes. Vertical line drawn down to bottom-most child node. Slower (for large trees).
NoNavFocus = bitLShift(1, 27), -- Don't claim nav focus when interacting with this item (#8551)
ClipLabelForTrailingButton = bitLShift(1, 28), -- FIXME-WIP: Hard-coded for CollapsingHeader()
UpsideDownArrow = bitLShift(1, 29), -- FIXME-WIP: Turn Down arrow into an Up arrow, for reversed trees (#6517)
}
ImGuiTreeNodeFlags.CollapsingHeader = bitOr(ImGuiTreeNodeFlags.Framed, ImGuiTreeNodeFlags.NoTreePushOnOpen, ImGuiTreeNodeFlags.NoAutoOpenOnLog)
ImGuiTreeNodeFlags.OpenOnMask_ = bitOr(ImGuiTreeNodeFlags.OpenOnDoubleClick, ImGuiTreeNodeFlags.OpenOnArrow)
ImGuiTreeNodeFlags.DrawLinesMask_ = bitOr(ImGuiTreeNodeFlags.DrawLinesNone, ImGuiTreeNodeFlags.DrawLinesFull, ImGuiTreeNodeFlags.DrawLinesToNodes)
-------------------------------------------------------------------------------
-- [SECTION] Tables API flags and structures (ImGuiTableFlags, ImGuiTableColumnFlags, ImGuiTableRowFlags, ImGuiTableBgTarget, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs)
-------------------------------------------------------------------------------
-- Flags for ImGui.BeginTable()
-- - Important! Sizing policies have complex and subtle side effects, much more so than you would expect.
-- Read comments/demos carefully + experiment with live demos to get acquainted with them.
-- - The DEFAULT sizing policies are:
-- - Default to ImGuiTableFlags.SizingFixedFit if ScrollX is on, or if host window has ImGuiWindowFlags.AlwaysAutoResize.
-- - Default to ImGuiTableFlags.SizingStretchSame if ScrollX is off.
-- - When ScrollX is off:
-- - Table defaults to ImGuiTableFlags.SizingStretchSame -> all Columns defaults to ImGuiTableColumnFlags.WidthStretch with same weight.
-- - Columns sizing policy allowed: Stretch (default), Fixed/Auto.
-- - Fixed Columns (if any) will generally obtain their requested width (unless the table cannot fit them all).
-- - Stretch Columns will share the remaining width according to their respective weight.
-- - Mixed Fixed/Stretch columns is possible but has various side-effects on resizing behaviors.
-- The typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns.
-- (this is because the visible order of columns have subtle but necessary effects on how they react to manual resizing).
-- - When ScrollX is on:
-- - Table defaults to ImGuiTableFlags.SizingFixedFit -> all Columns defaults to ImGuiTableColumnFlags.WidthFixed
-- - Columns sizing policy allowed: Fixed/Auto mostly.
-- - Fixed Columns can be enlarged as needed. Table will show a horizontal scrollbar if needed.
-- - When using auto-resizing (non-resizable) fixed columns, querying the content width to use item right-alignment e.g. SetNextItemWidth(-FLT_MIN) doesn't make sense, would create a feedback loop.
-- - Using Stretch columns OFTEN DOES NOT MAKE SENSE if ScrollX is on, UNLESS you have specified a value for 'inner_width' in BeginTable().
-- If you specify a value for 'inner_width' then effectively the scrolling space is known and Stretch or mixed Fixed/Stretch columns become meaningful again.
-- - Read on documentation at the top of imgui_tables.cpp for details.
--- @enum ImGuiTableFlags
ImGuiTableFlags =
{
-- Features
None = 0,
Resizable = bitLShift(1, 0), -- Enable resizing columns.
Reorderable = bitLShift(1, 1), -- Enable reordering columns in header row. (Need calling TableSetupColumn() + TableHeadersRow() to display headers, or using ImGuiTableFlags.ContextMenuInBody to access context-menu without headers).
Hideable = bitLShift(1, 2), -- Enable hiding/disabling columns in context menu.
Sortable = bitLShift(1, 3), -- Enable sorting. Call TableGetSortSpecs() to obtain sort specs. Also see ImGuiTableFlags.SortMulti and ImGuiTableFlags.SortTristate.
NoSavedSettings = bitLShift(1, 4), -- Disable persisting columns order, width, visibility and sort settings in the .ini file.
ContextMenuInBody = bitLShift(1, 5), -- Right-click on columns body/contents will also display table context menu. By default it is available in TableHeadersRow().
-- Decorations
RowBg = bitLShift(1, 6), -- Set each RowBg color with ImGuiCol.TableRowBg or ImGuiCol.TableRowBgAlt (equivalent of calling TableSetBgColor with ImGuiTableBgFlags.RowBg0 on each row manually)
BordersInnerH = bitLShift(1, 7), -- Draw horizontal borders between rows.
BordersOuterH = bitLShift(1, 8), -- Draw horizontal borders at the top and bottom.
BordersInnerV = bitLShift(1, 9), -- Draw vertical borders between columns.
BordersOuterV = bitLShift(1, 10), -- Draw vertical borders on the left and right sides.
NoBordersInBody = bitLShift(1, 11), -- [ALPHA] Disable vertical borders in columns Body (borders will always appear in Headers). -> May move to style
NoBordersInBodyUntilResize = bitLShift(1, 12), -- [ALPHA] Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers). -> May move to style
-- Sizing Policy (read above for defaults)
SizingFixedFit = bitLShift(1, 13), -- Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching contents width.
SizingFixedSame = bitLShift(2, 13), -- Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching the maximum contents width of all columns. Implicitly enable ImGuiTableFlags.NoKeepColumnsVisible.
SizingStretchProp = bitLShift(3, 13), -- Columns default to _WidthStretch with default weights proportional to each columns contents widths.
SizingStretchSame = bitLShift(4, 13), -- Columns default to _WidthStretch with default weights all equal, unless overridden by TableSetupColumn().
-- Sizing Extra Options
NoHostExtendX = bitLShift(1, 16), -- Make outer width auto-fit to columns, overriding outer_size.x value. Only available when ScrollX/ScrollY are disabled and Stretch columns are not used.
NoHostExtendY = bitLShift(1, 17), -- Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible.
NoKeepColumnsVisible = bitLShift(1, 18), -- Disable keeping column always minimally visible when ScrollX is off and table gets too small. Not recommended if columns are resizable.
PreciseWidths = bitLShift(1, 19), -- Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth.
-- Clipping
NoClip = bitLShift(1, 20), -- Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze().
-- Padding
PadOuterX = bitLShift(1, 21), -- Default if BordersOuterV is on. Enable outermost padding. Generally desirable if you have headers.
NoPadOuterX = bitLShift(1, 22), -- Default if BordersOuterV is off. Disable outermost padding.
NoPadInnerX = bitLShift(1, 23), -- Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off).
-- Scrolling
ScrollX = bitLShift(1, 24), -- Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this creates a child window, ScrollY is currently generally recommended when using ScrollX.
ScrollY = bitLShift(1, 25), -- Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size.
-- Sorting
SortMulti = bitLShift(1, 26), -- Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).
SortTristate = bitLShift(1, 27), -- Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).
-- Miscellaneous
HighlightHoveredColumn = bitLShift(1, 28), -- Highlight column headers when hovered (may evolve into a fuller highlight)
}
ImGuiTableFlags.BordersH = bitOr(ImGuiTableFlags.BordersInnerH, ImGuiTableFlags.BordersOuterH) -- Draw horizontal borders.
ImGuiTableFlags.BordersV = bitOr(ImGuiTableFlags.BordersInnerV, ImGuiTableFlags.BordersOuterV) -- Draw vertical borders.
ImGuiTableFlags.BordersInner = bitOr(ImGuiTableFlags.BordersInnerV, ImGuiTableFlags.BordersInnerH) -- Draw inner borders.
ImGuiTableFlags.BordersOuter = bitOr(ImGuiTableFlags.BordersOuterV, ImGuiTableFlags.BordersOuterH) -- Draw outer borders.
ImGuiTableFlags.Borders = bitOr(ImGuiTableFlags.BordersInner, ImGuiTableFlags.BordersOuter) -- Draw all borders.
-- [Internal] Combinations and masks
ImGuiTableFlags.SizingMask_ = bitOr(ImGuiTableFlags.SizingFixedFit, ImGuiTableFlags.SizingFixedSame, ImGuiTableFlags.SizingStretchProp, ImGuiTableFlags.SizingStretchSame)
-- Flags for ImGui.TableSetupColumn()
--- @enum ImGuiTableColumnFlags
ImGuiTableColumnFlags =
{
-- Input configuration flags
None = 0,
Disabled = bitLShift(1, 0), -- Overriding/master disable flag: hide column, won't show in context menu (unlike calling TableSetColumnEnabled() which manipulates the user accessible state)
DefaultHide = bitLShift(1, 1), -- Default as a hidden/disabled column.
DefaultSort = bitLShift(1, 2), -- Default as a sorting column.
WidthStretch = bitLShift(1, 3), -- Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _SizingStretchSame or _SizingStretchProp).
WidthFixed = bitLShift(1, 4), -- Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _SizingFixedFit and table is resizable).
NoResize = bitLShift(1, 5), -- Disable manual resizing.
NoReorder = bitLShift(1, 6), -- Disable manual reordering this column, this will also prevent other columns from crossing over this column.
NoHide = bitLShift(1, 7), -- Disable ability to hide/disable this column.
NoClip = bitLShift(1, 8), -- Disable clipping for this column (all NoClip columns will render in a same draw command).
NoSort = bitLShift(1, 9), -- Disable ability to sort on this field (even if ImGuiTableFlags.Sortable is set on the table).
NoSortAscending = bitLShift(1, 10), -- Disable ability to sort in the ascending direction.
NoSortDescending = bitLShift(1, 11), -- Disable ability to sort in the descending direction.
NoHeaderLabel = bitLShift(1, 12), -- TableHeadersRow() will submit an empty label for this column. Convenient for some small columns. Name will still appear in context menu or in angled headers. You may append into this cell by calling TableSetColumnIndex() right after the TableHeadersRow() call.
NoHeaderWidth = bitLShift(1, 13), -- Disable header text width contribution to automatic column width.
PreferSortAscending = bitLShift(1, 14), -- Make the initial sort direction Ascending when first sorting on this column (default).
PreferSortDescending = bitLShift(1, 15), -- Make the initial sort direction Descending when first sorting on this column.
IndentEnable = bitLShift(1, 16), -- Use current Indent value when entering cell (default for column 0).
IndentDisable = bitLShift(1, 17), -- Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored.
AngledHeader = bitLShift(1, 18), -- TableHeadersRow() will submit an angled header row for this column. Note this will add an extra row.
-- Output status flags, read-only via TableGetColumnFlags()
IsEnabled = bitLShift(1, 24), -- Status: is enabled == not hidden by user/api (referred to as "Hide" in _DefaultHide and _NoHide) flags.
IsVisible = bitLShift(1, 25), -- Status: is visible == is enabled AND not clipped by scrolling.
IsSorted = bitLShift(1, 26), -- Status: is currently part of the sort specs
IsHovered = bitLShift(1, 27), -- Status: is hovered by mouse
NoDirectResize_ = bitLShift(1, 30), -- [Internal] Disable user resizing this column directly (it may however we resized indirectly from its left edge)
}
-- [Internal] Combinations and masks
ImGuiTableColumnFlags.WidthMask_ = bitOr(ImGuiTableColumnFlags.WidthStretch, ImGuiTableColumnFlags.WidthFixed)
ImGuiTableColumnFlags.IndentMask_ = bitOr(ImGuiTableColumnFlags.IndentEnable, ImGuiTableColumnFlags.IndentDisable)
ImGuiTableColumnFlags.StatusMask_ = bitOr(ImGuiTableColumnFlags.IsEnabled, ImGuiTableColumnFlags.IsVisible, ImGuiTableColumnFlags.IsSorted, ImGuiTableColumnFlags.IsHovered)
-- Flags for ImGui.TableNextRow()
-- - Headers: Identify header row (set default background color + width of its contents accounted differently for auto column width)
--- @enum ImGuiTableRowFlags
ImGuiTableRowFlags = { None = 0, Headers = bitLShift(1, 0) }
-- Enum for ImGui.TableSetBgColor()
-- Background colors are rendering in 3 layers:
-- - Layer 0: draw with RowBg0 color if set, otherwise draw with ColumnBg0 if set.
-- - Layer 1: draw with RowBg1 color if set, otherwise draw with ColumnBg1 if set.
-- - Layer 2: draw with CellBg color if set.
-- The purpose of the two row/columns layers is to let you decide if a background color change should override or blend with the existing color.
-- When using ImGuiTableFlags.RowBg on the table, each row has the RowBg0 color automatically set for odd/even rows.
-- If you set the color of RowBg0 target, your color will override the existing RowBg0 color.
-- If you set the color of RowBg1 or ColumnBg1 target, your color will blend over the RowBg0 color.
-- - RowBg0: Set row background color 0 (generally used for background, automatically set when ImGuiTableFlags.RowBg is used)
-- - RowBg1: Set row background color 1 (generally used for selection marking)
-- - CellBg: Set cell background color (top-most color)
--- @enum ImGuiTableBgTarget
ImGuiTableBgTarget = { None = 0, RowBg0 = 1, RowBg1 = 2, CellBg = 3 }
-- A sorting direction
-- - Ascending = 0->9, A->Z etc.
-- - Descending = 9->0, Z->A etc.
--- @enum ImGuiSortDirection
ImGuiSortDirection = { None = 0, Ascending = 1, Descending = 2 }
-- Data payload for Drag and Drop operations: AcceptDragDropPayload(), GetDragDropPayload()
--- @class ImGuiPayload
--- Drag and drop payload. `Data` is any Lua value (C++ stores raw bytes); `DataSize` kept for API parity.
local IM_PAYLOAD = {}
IM_PAYLOAD.__index = IM_PAYLOAD
function IM_PAYLOAD:Clear() ImGuiPayload_Clear(self) end
function IM_PAYLOAD:IsDataType(t) return ImGuiPayload_IsDataType(self, t) end
function IM_PAYLOAD:IsPreview() return self.Preview end
function IM_PAYLOAD:IsDelivery() return self.Delivery end
function ImGuiPayload()
return setmetatable({ Data = nil, DataSize = 0, SourceId = 0, SourceParentId = 0, DataFrameCount = -1, DataType = "", Preview = false, Delivery = false }, IM_PAYLOAD)
end
function ImGuiPayload_Clear(p) p.SourceId = 0; p.SourceParentId = 0; p.Data = nil; p.DataSize = 0; p.DataType = ""; p.DataFrameCount = -1; p.Preview = false; p.Delivery = false end
function ImGuiPayload_IsDataType(p, t) return p.DataFrameCount ~= -1 and p.DataType == t end
function ImGuiPayload_IsPreview(p) return p.Preview end
function ImGuiPayload_IsDelivery(p) return p.Delivery end
-------------------------------------------------------------------------------
-- [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor)
-------------------------------------------------------------------------------
-- Flags for ImGuiListClipper (currently not fully exposed in function calls: a future refactor will likely add this to ImGuiListClipper::Begin function equivalent)
--- @enum ImGuiListClipperFlags
ImGuiListClipperFlags = {
None = 0,
NoSetTableRowCounters = bitLShift(1, 0), -- [Internal] Disabled modifying table row counters. Avoid assumption that 1 clipper item == 1 table row.
}
-- Helper: Growable text buffer for logging/accumulating text
-- (this could be called 'ImGuiTextBuilder' / 'ImGuiStringBuilder')
--- @class ImGuiTextBuffer # string builder: Buf is a list of string chunks
local ImGuiTextBufferMT = {}
ImGuiTextBufferMT.__index = ImGuiTextBufferMT
function ImGuiTextBuffer() return setmetatable({ Buf = {} }, ImGuiTextBufferMT) end
function ImGuiTextBufferMT:clear() self.Buf = {} end
function ImGuiTextBufferMT:empty() return #self.Buf == 0 end
function ImGuiTextBufferMT:append(str) self.Buf[#self.Buf + 1] = str end
function ImGuiTextBufferMT:appendf(fmt, ...) self.Buf[#self.Buf + 1] = string.format(fmt, ...) end
function ImGuiTextBufferMT:c_str() return table.concat(self.Buf) end
function ImGuiTextBufferMT:size() return #self:c_str() end
-- Helpers: ImColor() implicitly converts colors to either ImU32 (packed 4x1 byte) or ImVec4 (4x1 float)
-- Prefer using IM_COL32() macros. Avoid storing ImColor! Store either u32 of ImVec4. This is not a full-featured color class.
-- **None of the ImGui API are using ImColor directly but you can use it as a convenience to pass colors in either ImU32 or ImVec4 formats. Explicitly cast to ImU32 or ImVec4 if needed.
-- [Luau] no implicit conversions: use :U32() / .Value (ImVec4). Overloads: ImColor(), ImColor(r, g, b, a) floats,
-- ImColor(ImVec4), ImColor(ImU32). The C++ int (0-255) overload can't be told apart from floats: use ImColor(IM_COL32(r, g, b, a)).
--- @class ImColor
local ImColorMT = {}
ImColorMT.__index = ImColorMT
ImColor = setmetatable({}, { __call = function(_, r, g, b, a)
local self = setmetatable({ Value = ImVec4() }, ImColorMT)
if type(r) == "table" then
ImVec4_Copy(self.Value, r)
elseif r ~= nil and g == nil then
local s = 1.0 / 255.0
self.Value.x = bit32.band(bit32.rshift(r, 0 --[[IM_COL32_R_SHIFT]]), 0xFF) * s; self.Value.y = bit32.band(bit32.rshift(r, 8 --[[IM_COL32_G_SHIFT]]), 0xFF) * s
self.Value.z = bit32.band(bit32.rshift(r, 16 --[[IM_COL32_B_SHIFT]]), 0xFF) * s; self.Value.w = bit32.band(bit32.rshift(r, 24 --[[IM_COL32_A_SHIFT]]), 0xFF) * s
elseif r ~= nil then
self.Value.x = r; self.Value.y = g; self.Value.z = b; self.Value.w = a or 1.0
end
return self
end })
function ImColorMT:U32() return ImGui.ColorConvertFloat4ToU32(self.Value) end
-- FIXME-OBSOLETE: May need to obsolete/cleanup those helpers.
function ImColorMT:SetHSV(h, s, v, a) self.Value.x, self.Value.y, self.Value.z = ImGui.ColorConvertHSVtoRGB(h, s, v); self.Value.w = a or 1.0 end
function ImColor.HSV(h, s, v, a) local r, g, b = ImGui.ColorConvertHSVtoRGB(h, s, v); return ImColor(r, g, b, a or 1.0) end
-------------------------------------------------------------------------------
-- [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiSelectionRequestType, ImGuiSelectionRequest, ImGuiMultiSelectIO, ImGuiSelectionBasicStorage)
-------------------------------------------------------------------------------
-- Multi-selection system
-- Documentation at: https://github.com/ocornut/imgui/wiki/Multi-Select
-- - Refer to 'Demo->Widgets->Selection State & Multi-Select' for demos using this.
-- - This system implements standard multi-selection idioms (Ctrl+Mouse/Keyboard, Shift+Mouse/Keyboard, etc)
-- with support for clipper (skipping non-visible items), box-select and many other details.
-- - Selectable(), Checkbox() are supported but custom widgets may use it as well.
-- - TreeNode() is technically supported but... using this correctly is more complicated: you need some sort of linear/random access to your tree,
-- which is suited to advanced trees setups also implementing filters and clipper. We will work toward simplifying and demoing it.
-- - In the spirit of Dear ImGui design, your code owns actual selection data.
-- This is designed to allow all kinds of selection storage you may use in your application e.g. set/map/hash.
-- About ImGuiSelectionBasicStorage:
-- - This is an optional helper to store a selection state and apply selection requests.
-- - It is used by our demos and provided as a convenience to quickly implement multi-selection.
-- Usage:
-- - Identify submitted items with SetNextItemSelectionUserData(), most likely using an index into your current data-set.
-- - Store and maintain actual selection data using persistent object identifiers.
-- - Usage flow:
-- BEGIN - (1) Call BeginMultiSelect() and retrieve the ImGuiMultiSelectIO result.
-- - (2) Honor request list (SetAll/SetRange requests) by updating your selection data. Same code as Step 6.
-- - (3) [If using clipper] You need to make sure RangeSrcItem is always submitted. Calculate its index and pass to clipper:IncludeItemByIndex(). If storing indices in ImGuiSelectionUserData, a simple clipper:IncludeItemByIndex(ms_io.RangeSrcItem) call will work.
-- LOOP - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls.
-- END - (5) Call EndMultiSelect() and retrieve the ImGuiMultiSelectIO result.
-- - (6) Honor request list (SetAll/SetRange requests) by updating your selection data. Same code as Step 2.
-- If you submit all items (no clipper), Step 2 and 3 are optional and will be handled by each item themselves. It is fine to always honor those steps.
-- About ImGuiSelectionUserData:
-- - This can store an application-defined identifier (e.g. index or pointer) submitted via SetNextItemSelectionUserData().
-- - In return we store them into RangeSrcItem/RangeFirstItem/RangeLastItem and other fields in ImGuiMultiSelectIO.
-- - Most applications will store an object INDEX, hence the chosen name and type. Storing an index is natural, because
-- SetRange requests will give you two end-points and you will need to iterate/interpolate between them to update your selection.
-- - However it is perfectly possible to store a POINTER or another IDENTIFIER inside ImGuiSelectionUserData.
-- Our system never assume that you identify items by indices, it never attempts to interpolate between two values.
-- - If you enable ImGuiMultiSelectFlags.NoRangeSelect then it is guaranteed that you will never have to interpolate
-- between two ImGuiSelectionUserData, which may be a convenient way to use part of the feature with less code work.
-- - As most users will want to store an index, for convenience and to reduce confusion we use ImS64 instead of void*,
-- being syntactically easier to downcast. Feel free to reinterpret_cast and store a pointer inside.
-- [Port] ImGuiSelectionUserData is any Lua value compared with '==' (an integer index, or e.g. a table reference instead of a pointer).
-- -1 (ImGuiSelectionUserData_Invalid) is reserved. ImGuiSelectionBasicStorage/ImGuiSelectionExternalStorage require integer indices.
-- Flags for BeginMultiSelect()
--- @enum ImGuiMultiSelectFlags
ImGuiMultiSelectFlags = {
None = 0,
SingleSelect = bitLShift(1, 0), -- Disable selecting more than one item. This is available to allow single-selection code to share same code/logic if desired. It essentially disables the main purpose of BeginMultiSelect() tho!
NoSelectAll = bitLShift(1, 1), -- Disable Ctrl+A shortcut to select all.
NoRangeSelect = bitLShift(1, 2), -- Disable Shift+selection mouse/keyboard support (useful for unordered 2D selection). With BoxSelect is also ensure contiguous SetRange requests are not combined into one. This allows not handling interpolation in SetRange requests.
NoAutoSelect = bitLShift(1, 3), -- Disable selecting items when navigating (useful for e.g. supporting range-select in a list of checkboxes).
NoAutoClear = bitLShift(1, 4), -- Disable clearing selection when navigating or selecting another one (generally used with ImGuiMultiSelectFlags.NoAutoSelect. useful for e.g. supporting range-select in a list of checkboxes).
NoAutoClearOnReselect = bitLShift(1, 5), -- Disable clearing selection when clicking/selecting an already selected item.
BoxSelect1d = bitLShift(1, 6), -- Enable box-selection with same width and same x pos items (e.g. full row Selectable()). Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space.
BoxSelect2d = bitLShift(1, 7), -- Enable box-selection with varying width or varying x pos items support (e.g. different width labels, or 2D layout/grid). This is slower: alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items.
BoxSelectNoScroll = bitLShift(1, 8), -- Disable scrolling when box-selecting and moving mouse near edges of scope.
ClearOnEscape = bitLShift(1, 9), -- Clear selection when pressing Escape while scope is focused.
ClearOnClickVoid = bitLShift(1, 10), -- Clear selection when clicking on empty location within scope.
ScopeWindow = bitLShift(1, 11), -- Scope for _BoxSelect and _ClearOnClickVoid is whole window (Default). Use if BeginMultiSelect() covers a whole window or used a single time in same window.
ScopeRect = bitLShift(1, 12), -- Scope for _BoxSelect and _ClearOnClickVoid is rectangle encompassing BeginMultiSelect()/EndMultiSelect(). Use if BeginMultiSelect() is called multiple times in same window.
SelectOnAuto = bitLShift(1, 13), -- Apply selection on mouse down when clicking on unselected item, on mouse up when clicking on selected item. (Default)
SelectOnClickAlways = bitLShift(1, 14), -- Apply selection on mouse down when clicking on any items. Prevents Drag and Drop from being used on multiple-selection, but allows e.g. BoxSelect to always reselect even when clicking inside an existing selection. (Excel style behavior)
SelectOnClickRelease = bitLShift(1, 15), -- Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection.
--RangeSelect2d = bitLShift(1, 15), -- Shift+Selection uses 2d geometry instead of linear sequence, so possible to use Shift+up/down to select vertically in grid. Analogous to what BoxSelect does.
NavWrapX = bitLShift(1, 16), -- [Temporary] Enable navigation wrapping on X axis. Provided as a convenience because we don't have a design for the general Nav API for this yet. When the more general feature be public we may obsolete this flag in favor of new one.
NoSelectOnRightClick = bitLShift(1, 17), -- Disable default right-click processing, which selects item on mouse down, and is designed for context-menus.
CheckboxMode_ = bitLShift(1, 20), -- [Internal]
}
ImGuiMultiSelectFlags.SelectOnMask_ = bit32.bor(ImGuiMultiSelectFlags.SelectOnAuto, ImGuiMultiSelectFlags.SelectOnClickAlways, ImGuiMultiSelectFlags.SelectOnClickRelease)
ImGuiMultiSelectFlags.SelectOnClick = ImGuiMultiSelectFlags.SelectOnAuto -- RENAMED in 1.92.6
-- Main IO structure returned by BeginMultiSelect()/EndMultiSelect().
-- This mainly contains a list of selection requests.
-- - Use 'Demo->Tools->Debug Log->Selection' to see requests as they happen.
-- - Some fields are only useful if your list is dynamic and allows deletion (getting post-deletion focus/state right is shown in the demo)
-- - Below: who reads/writes each fields? 'r'=read, 'w'=write, 'ms'=multi-select code, 'app'=application/user code.
--- @class ImGuiMultiSelectIO
--- @field Requests ImVector<ImGuiSelectionRequest> # ms:w, app:r / ms:w app:r // Requests to apply to your selection data. (1-based .Data)
--- @field RangeSrcItem ImGuiSelectionUserData # ms:w app:r / // (If using clipper) Begin: Source item (often the first selected item) must never be clipped: use clipper:IncludeItemByIndex() to ensure it is submitted.
--- @field NavIdItem ImGuiSelectionUserData # ms:w, app:r / // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items).
--- @field NavIdSelected bool # ms:w, app:r / app:r // (If using deletion) Last known selection state for NavId (if part of submitted items).
--- @field RangeSrcReset bool # app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection).
--- @field ItemsCount int # ms:w, app:r / app:r // 'int items_count' parameter to BeginMultiSelect() is copied here for convenience, allowing simpler calls to your ApplyRequests handler. Not used internally.
-- Selection request type
--- @enum ImGuiSelectionRequestType
ImGuiSelectionRequestType = {
None = 0,
SetAll = 1, -- Request app to clear selection (if Selected==false) or select all items (if Selected==true). We cannot set RangeFirstItem/RangeLastItem as its contents is entirely up to user (not necessarily an index)
SetRange = 2, -- Request app to select/unselect [RangeFirstItem..RangeLastItem] items (inclusive) based on value of Selected. Only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false.
}
-- Selection request item
--- @class ImGuiSelectionRequest
--- @field Type ImGuiSelectionRequestType # ms:w, app:r / ms:w, app:r // Request type. You'll most often receive 1 Clear + 1 SetRange with a single-item range.
--- @field Selected bool # ms:w, app:r / ms:w, app:r // Parameter for SetAll/SetRange requests (true = select, false = unselect)
--- @field RangeDirection int # / ms:w app:r // Parameter for SetRange request: +1 when RangeFirstItem comes before RangeLastItem, -1 otherwise. Useful if you want to preserve selection order on a backward Shift+Click.
--- @field RangeFirstItem ImGuiSelectionUserData # / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom).
--- @field RangeLastItem ImGuiSelectionUserData # / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top). Inclusive!
function ImGuiSelectionRequest()
return { Type = 0 --[[ImGuiSelectionRequestType.None]], Selected = false, RangeDirection = 0, RangeFirstItem = -1, RangeLastItem = -1 }
end
-- [Port] ImVector<ImGuiSelectionRequest> copy function: push_back() copies fields into the preallocated element (C++ copies by value),
-- so callers can push a reused scratch request without allocating.
function ImGuiSelectionRequest_Copy(t, k, v)
local dst = t[k]
dst.Type = v.Type; dst.Selected = v.Selected; dst.RangeDirection = v.RangeDirection; dst.RangeFirstItem = v.RangeFirstItem; dst.RangeLastItem = v.RangeLastItem
end
--- @return ImGuiMultiSelectIO
function ImGuiMultiSelectIO()
return { Requests = ImVector(ImGuiSelectionRequest, ImGuiSelectionRequest_Copy), RangeSrcItem = -1, NavIdItem = -1, NavIdSelected = false, RangeSrcReset = false, ItemsCount = 0 }
end
-- ImGuiSelectionBasicStorage, ImGuiSelectionExternalStorage: see [SECTION] Widgets: Multi-Select helpers in imgui_widgets.lua
-- (constructors and methods are defined there, like upstream's out-of-line definitions in imgui_widgets.cpp).
--- @class ImGuiSettingsHandler
--- Fns take (ctx, handler, ...) like upstream. ReadOpenFn(ctx, handler, name) returns entry; ReadLineFn(ctx, handler, entry, line);
--- WriteAllFn(ctx, handler, buf: ImGuiTextBuffer)
function ImGuiSettingsHandler()
return { TypeName = nil, TypeHash = 0, ClearAllFn = nil, ReadInitFn = nil, ReadOpenFn = nil, ReadLineFn = nil,
ApplyAllFn = nil, WriteAllFn = nil, CleanupFn = nil, UserData = nil }
end
return true -- [Roblox] ModuleScripts must return exactly one value
end
__FILES['imgui_impl_roblox.lua'] = function()
--- ImGui Backend for Roblox
--- - Renderer: exact software rasterizer (1:1 with imgui's triangles) into 256x256 EditableImage tiles at native
--- screen resolution; only tiles whose content changed are redrawn/uploaded (see RenderDrawData).
--- - Platform: UserInputService
--- - Files: an in-memory file system (VFS), pre-filled with the fonts from imgui_impl_roblox_fonts.lua
--- Requires "Allow Mesh / Image APIs" (EditableImage) in game settings.
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local AssetService = game:GetService("AssetService")
local floor, ceil, min, max = math.floor, math.ceil, math.min, math.max
local readu8, readu32, writeu32, readstring = buffer.readu8, buffer.readu32, buffer.writeu32, buffer.readstring
local readf64 = buffer.readf64
local band, rshift = bit32.band, bit32.rshift
local ImGui_ImplRoblox_GetBackendData
local ImGui_ImplRoblox_UpdateTexture
local ImGui_ImplRoblox_RenderDrawData
local ImGui_ImplRoblox_Shutdown
local KEY_MAP = {} -- Enum.KeyCode -> ImGuiKey
do
local names = {
Return = "Enter", Escape = "Escape", Backspace = "Backspace", Tab = "Tab", Space = "Space", KeypadEnter = "KeypadEnter",
Up = "UpArrow", Down = "DownArrow", Left = "LeftArrow", Right = "RightArrow",
Home = "Home", End = "End", PageUp = "PageUp", PageDown = "PageDown", Insert = "Insert", Delete = "Delete",
LeftControl = "LeftCtrl", RightControl = "RightCtrl", LeftShift = "LeftShift", RightShift = "RightShift",
LeftAlt = "LeftAlt", RightAlt = "RightAlt", LeftSuper = "LeftSuper", RightSuper = "RightSuper",
Quote = "Apostrophe", Comma = "Comma", Minus = "Minus", Period = "Period", Slash = "Slash", Semicolon = "Semicolon",
Equals = "Equal", LeftBracket = "LeftBracket", BackSlash = "Backslash", RightBracket = "RightBracket", Backquote = "GraveAccent",
CapsLock = "CapsLock",
KeypadPeriod = "KeypadDecimal", KeypadDivide = "KeypadDivide", KeypadMultiply = "KeypadMultiply",
KeypadMinus = "KeypadSubtract", KeypadPlus = "KeypadAdd", KeypadEquals = "KeypadEqual",
}
local digits = { "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine" }
for i = 0, 9 do
names[digits[i + 1]] = "K" .. i
names["Keypad" .. digits[i + 1]] = "Keypad" .. i
end
for i = 1, 12 do names["F" .. i] = "F" .. i end
for c in ("ABCDEFGHIJKLMNOPQRSTUVWXYZ"):gmatch(".") do names[c] = c end
for rbx_name, imgui_name in pairs(names) do
KEY_MAP[Enum.KeyCode[rbx_name]] = ImGuiKey[imgui_name]
end
end
-- Roblox has no "text input" event, so characters come from KeyCode.Value (ASCII for printable keys) + a US layout shift table.
local SHIFTED = {
["1"] = "!", ["2"] = "@", ["3"] = "#", ["4"] = "$", ["5"] = "%", ["6"] = "^", ["7"] = "&", ["8"] = "*", ["9"] = "(", ["0"] = ")",
["-"] = "_", ["="] = "+", ["["] = "{", ["]"] = "}", ["\\"] = "|", [";"] = ":", ["'"] = "\"", [","] = "<", ["."] = ">", ["/"] = "?", ["`"] = "~",
}
local MOUSE_BUTTONS = {
[Enum.UserInputType.MouseButton1] = 0, -- ImGuiMouseButton.Left
[Enum.UserInputType.MouseButton2] = 1, -- ImGuiMouseButton.Right
[Enum.UserInputType.MouseButton3] = 2, -- ImGuiMouseButton.Middle
}
--- @return boolean ctrl, boolean shift, boolean alt
local function ImGui_ImplRoblox_UpdateKeyModifiers()
local io = ImGui.GetIO()
local K = Enum.KeyCode
local ctrl = UserInputService:IsKeyDown(K.LeftControl) or UserInputService:IsKeyDown(K.RightControl)
local shift = UserInputService:IsKeyDown(K.LeftShift) or UserInputService:IsKeyDown(K.RightShift)
local alt = UserInputService:IsKeyDown(K.LeftAlt) or UserInputService:IsKeyDown(K.RightAlt)
io:AddKeyEvent(4096 --[[ImGuiMod_Ctrl]], ctrl)
io:AddKeyEvent(8192 --[[ImGuiMod_Shift]], shift)
io:AddKeyEvent(16384 --[[ImGuiMod_Alt]], alt)
io:AddKeyEvent(32768 --[[ImGuiMod_Super]], UserInputService:IsKeyDown(K.LeftSuper) or UserInputService:IsKeyDown(K.RightSuper))
return ctrl, shift, alt
end
--- @return table?
function ImGui_ImplRoblox_GetBackendData()
return ImGui.GetCurrentContext() and ImGui.GetIO().BackendPlatformUserData or nil
end
local function ImGui_ImplRoblox_UpdateMonitors(w, h)
local platform_io = ImGui.GetPlatformIO()
platform_io.Monitors:resize(0)
local imgui_monitor = ImGuiPlatformMonitor()
ImVec2_Copy(imgui_monitor.MainSize, ImVec2(w, h))
ImVec2_Copy(imgui_monitor.WorkSize, ImVec2(w, h))
platform_io.Monitors:push_back(imgui_monitor)
end
---------------------------------------------------------
-- PLATFORM DATA & CALLBACKS
---------------------------------------------------------
-- Games can't touch the OS clipboard, so copy/paste only works inside ImGui
local clipboard = ""
local function ImGui_ImplRoblox_PlatformSetClipboardText(ctx, text) clipboard = text end
local proxy_focused = false
local function ImGui_ImplRoblox_PlatformGetClipboardText(ctx) if proxy_focused then return "" end return clipboard end
---------------------------------------------------------
-- FILE SYSTEM (in-memory, Roblox has no file access)
---------------------------------------------------------
local VFS = {} -- path -> string of raw bytes
--- Registers a file so ImGui can load it, e.g. ImGui_ImplRoblox.AddFile("fonts/MyFont.ttf", bytes)
--- then io.Fonts:AddFontFromFileTTF("fonts/MyFont.ttf", 16)
local function ImGui_ImplRoblox_AddFile(path, bytes) VFS[path] = bytes end
for path, hex in pairs(IM_INCLUDE("imgui_impl_roblox_fonts.lua")) do
VFS[path] = hex:gsub("%s", ""):gsub("%x%x", function(h) return string.char(tonumber(h, 16)) end)
end
function ImStd.ImFileOpen(filename, mode)
local data = VFS[filename]
return data and { Data = data, Pos = 0 } or nil
end
function ImStd.ImFileClose(f) end
function ImStd.ImFileGetSize(f) return #f.Data end
function ImStd.ImFileRead(f, data, count)
local s, pos = f.Data, f.Pos
count = min(count, #s - pos)
for i = 1, count do data[i] = string.byte(s, pos + i) end
f.Pos = pos + count
return count
end
function ImStd.ImFileLoadToMemory(filename, mode)
local f = ImStd.ImFileOpen(filename, mode)
if not f or #f.Data == 0 then return end
local file_data = {}
local size = ImStd.ImFileRead(f, file_data, #f.Data)
return file_data, size
end
---------------------------------------------------------
-- TEXTURE MANAGEMENT
---------------------------------------------------------
-- Copies a region of ImTextureData pixels (1-based byte table) into our RGBA8 buffer
@native
local function CopyTexturePixels(tex, dst, x, y, w, h)
local bpp, tex_w = tex.BytesPerPixel, tex.Width
for row = 0, h - 1 do
local src, src_base = tex:GetPixelsAt(x, y + row)
local dst_off = ((y + row) * tex_w + x) * 4
if bpp == 4 then
for i = 0, w - 1 do
local s = src_base + i * 4
writeu32(dst, dst_off + i * 4, src[s] + src[s + 1] * 256 + src[s + 2] * 65536 + src[s + 3] * 16777216)
end
else -- Alpha8: white with alpha
for i = 0, w - 1 do
writeu32(dst, dst_off + i * 4, 16777215 + src[src_base + i] * 16777216)
end
end
end
end
function ImGui_ImplRoblox_UpdateTexture(tex)
local bd = ImGui_ImplRoblox_GetBackendData()
if tex.Status == 2 --[[ImTextureStatus.WantCreate]] then
local t = { Buffer = buffer.create(tex.Width * tex.Height * 4), Width = tex.Width, Height = tex.Height }
CopyTexturePixels(tex, t.Buffer, 0, 0, tex.Width, tex.Height)
bd.TextureCount = bd.TextureCount + 1
bd.Textures[bd.TextureCount] = t
tex.BackendUserData = bd.TextureCount
tex:SetTexID(bd.TextureCount)
tex:SetStatus(0 --[[ImTextureStatus.OK]])
elseif tex.Status == 3 --[[ImTextureStatus.WantUpdates]] then
local t = bd.Textures[tex.BackendUserData]
assert(t ~= nil)
for _, r in tex.Updates:iter() do
CopyTexturePixels(tex, t.Buffer, r.x, r.y, r.w, r.h)
end
tex:SetStatus(0 --[[ImTextureStatus.OK]])
elseif tex.Status == 4 --[[ImTextureStatus.WantDestroy]] then
if tex.BackendUserData then
bd.Textures[tex.BackendUserData] = nil
tex.BackendUserData = nil
end
tex:SetTexID((-1) --[[ImTextureID_Invalid]])
tex:SetStatus(1 --[[ImTextureStatus.Destroyed]])
end
end
--- User texture from RGBA8 pixels (e.g. EditableImage:ReadPixelsBuffer()). Returns an ImTextureRef for
--- ImGui.Image / draw_list:AddImage. Ids are never reused, so cached tiles can't show a stale image.
function ImGui_ImplRoblox_CreateTexture(pixels, w, h)
local bd = ImGui_ImplRoblox_GetBackendData()
bd.TextureCount = bd.TextureCount + 1
bd.Textures[bd.TextureCount] = { Buffer = pixels, Width = w, Height = h }
return ImTextureRef(bd.TextureCount)
end
function ImGui_ImplRoblox_DestroyTexture(tex_ref)
ImGui_ImplRoblox_GetBackendData().Textures[tex_ref._TexID] = nil
end
---------------------------------------------------------
-- RENDERING (software rasterizer into tile buffers)
---------------------------------------------------------
-- Buffer layout: 4 bytes per pixel, R G B A, row-major, 0-based offsets. ImU32 colours are already R | G<<8 | B<<16 | A<<24.
-- Blending is non-premultiplied "over" with a transparent (alpha 0) background, so the game stays visible behind ImGui.
local buf, buf_w -- buffer/width of the tile being drawn
local rect_cols = table.create(256, 0) -- scratch: texel column offsets for DrawRect
local sub_bufs, n_sub_bufs = {}, 0 -- scratch: partial tile upload buffers by size
local TILE_SIZE -- Vector2, set below
-- Blends one pixel (r, g, b, a are 0..255). One u32 read/write, one division in the general case.
-- FillSpan below inlines the same math with per-span constants - keep the two in sync.
@native
local function BlendPixel(o, r, g, b, a)
if a >= 255 then
writeu32(buf, o, floor(r + 0.5) + floor(g + 0.5) * 256 + floor(b + 0.5) * 65536 + 4278190080)
return
end
if a <= 0 then return end
local d = readu32(buf, o)
local da = rshift(d, 24)
if da == 0 then
writeu32(buf, o, floor(r + 0.5) + floor(g + 0.5) * 256 + floor(b + 0.5) * 65536 + floor(a + 0.5) * 16777216)
return
end
local sa = a / 255
local dr, dg, db = band(d, 255), band(rshift(d, 8), 255), band(rshift(d, 16), 255)
if da == 255 then
local ns = 1 - sa
writeu32(buf, o, floor(r * sa + dr * ns + 0.5) + floor(g * sa + dg * ns + 0.5) * 256 + floor(b * sa + db * ns + 0.5) * 65536 + 4278190080)
else
local k = da * ((1 - sa) / 255)
local oa = sa + k
local inv = 1 / oa
writeu32(buf, o, floor((r * sa + dr * k) * inv + 0.5) + floor((g * sa + dg * k) * inv + 0.5) * 256
+ floor((b * sa + db * k) * inv + 0.5) * 65536 + floor(oa * 255 + 0.5) * 16777216)
end
end
-- Fills pixels [x0, x1) of row y with one colour
@native
local function FillSpan(y, x0, x1, r, g, b, a)
if a <= 0 then return end
local o0 = (y * buf_w + x0) * 4
local o1 = o0 + (x1 - x0 - 1) * 4
if a >= 255 then
local packed = floor(r + 0.5) + floor(g + 0.5) * 256 + floor(b + 0.5) * 65536 + 4278190080
for o = o0, o1, 4 do writeu32(buf, o, packed) end
return
end
local packed0 = floor(r + 0.5) + floor(g + 0.5) * 256 + floor(b + 0.5) * 65536 + floor(a + 0.5) * 16777216
local sa = a / 255
local ns = 1 - sa
local c1 = ns / 255
local rs, gs, bs = r * sa, g * sa, b * sa
local last_d, last_v = -1, 0 -- UI backgrounds are mostly flat: reuse the previous result for the same destination
for o = o0, o1, 4 do
local d = readu32(buf, o)
local da = rshift(d, 24)
if d == last_d then
writeu32(buf, o, last_v)
elseif da == 0 then
writeu32(buf, o, packed0)
else
last_d = d
local dr, dg, db = band(d, 255), band(rshift(d, 8), 255), band(rshift(d, 16), 255)
if da == 255 then
last_v = floor(rs + dr * ns + 0.5) + floor(gs + dg * ns + 0.5) * 256 + floor(bs + db * ns + 0.5) * 65536 + 4278190080
else
local k = da * c1
local oa = sa + k
local inv = 1 / oa
last_v = floor((rs + dr * k) * inv + 0.5) + floor((gs + dg * k) * inv + 0.5) * 256
+ floor((bs + db * k) * inv + 0.5) * 65536 + floor(oa * 255 + 0.5) * 16777216
end
writeu32(buf, o, last_v)
end
end
end
@native
local function UnpackColor(col)
return col % 256, floor(col / 256) % 256, floor(col / 65536) % 256, floor(col / 16777216) % 256
end
-- Nearest texel at (u, v) as r, g, b, a (0..255)
@native
local function SampleTexture(t, u, v)
local tw, th = t.Width, t.Height
local tx, ty = floor(u * tw), floor(v * th)
if tx < 0 then tx = 0 elseif tx >= tw then tx = tw - 1 end
if ty < 0 then ty = 0 elseif ty >= th then ty = th - 1 end
local o = (ty * tw + tx) * 4
local tb = t.Buffer
return readu8(tb, o), readu8(tb, o + 1), readu8(tb, o + 2), readu8(tb, o + 3)
end
-- Axis aligned, single colour quad (rects, glyphs, images). Pixel centers inside [x0, x1) x [y0, y1) are drawn.
@native
local function DrawRect(x0, y0, x1, y1, u0, v0, u1, v1, col, t, cx0, cy0, cx1, cy1)
local r, g, b, a = UnpackColor(col)
if a == 0 then return end
local px0, py0 = max(ceil(x0 - 0.5), cx0), max(ceil(y0 - 0.5), cy0)
local px1, py1 = min(ceil(x1 - 0.5), cx1), min(ceil(y1 - 0.5), cy1)
if px0 >= px1 or py0 >= py1 then return end
if not t or (u0 == u1 and v0 == v1) then
if t then
local tr, tg, tb, ta = SampleTexture(t, u0, v0)
r, g, b, a = r * tr / 255, g * tg / 255, b * tb / 255, a * ta / 255
end
if a <= 0 then return end
-- Rows of a flat rect usually blend over identical rows: when this row's destination equals the previous
-- row's (exact string compare), the result is identical too, so memcpy the previous row's result.
local len = (px1 - px0) * 4
local prev_dst, prev_o = nil, 0
for y = py0, py1 - 1 do
local o = (y * buf_w + px0) * 4
if a >= 255 then
if prev_dst then buffer.copy(buf, o, buf, prev_o, len) else FillSpan(y, px0, px1, r, g, b, a); prev_dst = true end
else
local dst = readstring(buf, o, len)
if dst == prev_dst then
buffer.copy(buf, o, buf, prev_o, len)
else
FillSpan(y, px0, px1, r, g, b, a)
prev_dst = dst
end
end
prev_o = o
end
return
end
local du, dv = (u1 - u0) / (x1 - x0), (v1 - v0) / (y1 - y0)
local tw, th, tbuf = t.Width, t.Height, t.Buffer
local r255, g255, b255, a255 = r / 255, g / 255, b / 255, a / 255
-- The texel column only depends on x: compute it once per column instead of once per pixel
local ncol = px1 - px0
local cols = rect_cols
for c = 1, ncol do
local tx = floor((u0 + (px0 + c - 1 + 0.5 - x0) * du) * tw)
if tx < 0 then tx = 0 elseif tx >= tw then tx = tw - 1 end
cols[c] = tx * 4
end
for y = py0, py1 - 1 do
local ty = floor((v0 + (y + 0.5 - y0) * dv) * th)
if ty < 0 then ty = 0 elseif ty >= th then ty = th - 1 end
local trow4 = ty * tw * 4
local o = (y * buf_w + px0) * 4
for c = 1, ncol do
local to = trow4 + cols[c]
local ta = readu8(tbuf, to + 3)
if ta > 0 then
local sa = ta * a255
local sr, sg, sb = readu8(tbuf, to) * r255, readu8(tbuf, to + 1) * g255, readu8(tbuf, to + 2) * b255
if sa >= 255 then -- (BlendPixel's opaque case, inlined: most glyph texels)
writeu32(buf, o, floor(sr + 0.5) + floor(sg + 0.5) * 256 + floor(sb + 0.5) * 65536 + 4278190080)
else
BlendPixel(o, sr, sg, sb, sa)
end
end
o = o + 4
end
end
end
-- Generic triangle: per-vertex colour + uv, top-left fill rule, clipped to [cx0, cx1) x [cy0, cy1)
@native
local function DrawTriangle(vbuf, va, vb, vc, t, cx0, cy0, cx1, cy1, ox, oy)
local x0, y0 = readf64(vbuf, va) - ox, readf64(vbuf, va + 8) - oy
local x1, y1 = readf64(vbuf, vb) - ox, readf64(vbuf, vb + 8) - oy
local x2, y2 = readf64(vbuf, vc) - ox, readf64(vbuf, vc + 8) - oy
local area = (x1 - x0) * (y2 - y0) - (y1 - y0) * (x2 - x0)
if area == 0 then return end
if area < 0 then -- make the winding consistent so "inside" means all edge functions >= 0
vb, vc = vc, vb
x1, y1, x2, y2 = x2, y2, x1, y1
area = -area
end
local minx, maxx = max(floor(min(x0, x1, x2)), cx0), min(ceil(max(x0, x1, x2)), cx1 - 1)
local miny, maxy = max(floor(min(y0, y1, y2)), cy0), min(ceil(max(y0, y1, y2)), cy1 - 1)
if minx > maxx or miny > maxy then return end
-- Edge functions E(p) = A*px + B*py + C, w0 is opposite va, w1 opposite vb, w2 opposite vc
local A0, B0 = y1 - y2, x2 - x1; local C0 = -(A0 * x1 + B0 * y1)
local A1, B1 = y2 - y0, x0 - x2; local C1 = -(A1 * x2 + B1 * y2)
local A2, B2 = y0 - y1, x1 - x0; local C2 = -(A2 * x0 + B2 * y0)
local tl0 = A0 > 0 or (A0 == 0 and B0 > 0)
local tl1 = A1 > 0 or (A1 == 0 and B1 > 0)
local tl2 = A2 > 0 or (A2 == 0 and B2 > 0)
local inv = 1 / area
local ra, ga, ba, aa = UnpackColor(readu32(vbuf, va + 32))
local rb, gb, bb, ab = UnpackColor(readu32(vbuf, vb + 32))
local rc, gc, bc, ac = UnpackColor(readu32(vbuf, vc + 32))
local ua, vva, ub, vvb, uc, vvc = readf64(vbuf, va + 16), readf64(vbuf, va + 24), readf64(vbuf, vb + 16), readf64(vbuf, vb + 24), readf64(vbuf, vc + 16), readf64(vbuf, vc + 24)
local textured = t and not (ua == ub and ua == uc and vva == vvb and vva == vvc)
if t and not textured then -- constant uv (e.g. the atlas white pixel): fold the texel into the vertex colours
local tr, tg, tb, ta = SampleTexture(t, ua, vva)
ra, ga, ba, aa = ra * tr / 255, ga * tg / 255, ba * tb / 255, aa * ta / 255
rb, gb, bb, ab = rb * tr / 255, gb * tg / 255, bb * tb / 255, ab * ta / 255
rc, gc, bc, ac = rc * tr / 255, gc * tg / 255, bc * tb / 255, ac * ta / 255
end
local flat = not textured and ra == rb and ra == rc and ga == gb and ga == gc and ba == bb and ba == bc and aa == ab and aa == ac
if flat and aa <= 0 then return end
for y = miny, maxy do
local py = y + 0.5
-- Narrow the x range of this row using the edges (expanded by one pixel, exact test below)
local xl, xr = minx, maxx
local e0, e1, e2 = B0 * py + C0, B1 * py + C1, B2 * py + C2
if A0 ~= 0 then local xe = -e0 / A0 - 0.5; if A0 > 0 then xl = max(xl, floor(xe)) else xr = min(xr, ceil(xe)) end end
if A1 ~= 0 then local xe = -e1 / A1 - 0.5; if A1 > 0 then xl = max(xl, floor(xe)) else xr = min(xr, ceil(xe)) end end
if A2 ~= 0 then local xe = -e2 / A2 - 0.5; if A2 > 0 then xl = max(xl, floor(xe)) else xr = min(xr, ceil(xe)) end end
local row = y * buf_w
for x = xl, xr do
local px = x + 0.5
local w0, w1, w2 = A0 * px + e0, A1 * px + e1, A2 * px + e2
if (w0 > 0 or (w0 == 0 and tl0)) and (w1 > 0 or (w1 == 0 and tl1)) and (w2 > 0 or (w2 == 0 and tl2)) then
local o = (row + x) * 4
if flat then
BlendPixel(o, ra, ga, ba, aa)
else
w0, w1, w2 = w0 * inv, w1 * inv, w2 * inv
local r = w0 * ra + w1 * rb + w2 * rc
local g = w0 * ga + w1 * gb + w2 * gc
local b = w0 * ba + w1 * bb + w2 * bc
local a = w0 * aa + w1 * ab + w2 * ac
if textured then
local tr, tg, tb, ta = SampleTexture(t, w0 * ua + w1 * ub + w2 * uc, w0 * vva + w1 * vvb + w2 * vvc)
r, g, b, a = r * tr / 255, g * tg / 255, b * tb / 255, a * ta / 255
end
BlendPixel(o, r, g, b, a)
end
end
end
end
end
-- Layers: every ImDrawList (one per top level window, popup, tooltip, plus the viewport bg/fg lists) gets its own
-- Frame, stacked by draw order. A layer is covered by TILE x TILE EditableImages (one ImageLabel each) placed
-- relative to the layer's origin (top-left of its content). Primitives are hashed into tiles in layer-relative
-- coordinates, so moving a window only moves its Frame: no tile changes, nothing re-rasterized or uploaded.
-- Only tiles whose hash changed are redrawn; the hash covers everything that affects a tile's pixels.
local TILE = 128
TILE_SIZE = Vector2.new(TILE, TILE)
local P1, P2 = 2147483647, 2147483629 -- two independent 31 bit hashes per tile (collision odds ~2^-62)
local KEY_W = 64 -- tile key = ty * KEY_W + tx (layers up to 16384 px wide)
local CLIP_PAD = 2048 -- content up to this far off screen is kept, so dragging a window past the edge doesn't redraw it
local LAYER_KEEP_FRAMES = 120 -- unused layers are destroyed after this many rendered frames
local MAX_DEFER_FRAMES = 8 -- a tile is redrawn at the latest this many frames after it changed, even over the redraw budget
-- Primitive arrays (reused between frames, no per-frame allocation)
local pr_a, pr_b, pr_c, pr_d, pr_t, pr_g = {}, {}, {}, {}, {}, {} -- vertices (pr_d == false -> triangle), texture, gradient kind
local pr_cx0, pr_cy0, pr_cx1, pr_cy1 = {}, {}, {}, {} -- clip (screen pixels)
local pr_bx0, pr_by0, pr_bx1, pr_by1 = {}, {}, {}, {} -- clipped bounds (screen pixels)
local pr_tex = {} -- texture id
local pr_vb = {} -- vertex buffer (draw list VtxBuffer.Buf)
local pr_k = {} -- layer kind (1..3)
local plist1, plist2, plist3 = {}, {}, {} -- primitive indices per layer kind
local cm_free, cm_kind, cm_x0, cm_y0, cm_x1, cm_y1 = {}, {}, {}, {}, {}, {} -- per draw command (collect -> Snapshot)
@native
local function HashVertex(h1, h2, vbuf, v, ox, oy)
local x, y = readf64(vbuf, v) - ox, readf64(vbuf, v + 8) - oy
local uv = readf64(vbuf, v + 16) * 8192 + readf64(vbuf, v + 24)
local c = readu32(vbuf, v + 32)
h1 = (h1 * 48271 + x * 4096 + y) % P1
h1 = (h1 * 48271 + uv * 4096) % P1
h1 = (h1 * 48271 + c) % P1
h2 = (h2 * 16807 + y * 4093 + x) % P2
h2 = (h2 * 16807 + uv * 65521) % P2
h2 = (h2 * 16807 + c) % P2
return h1, h2
end
-- Axis aligned quad with a linear colour gradient and constant uv (ColorPicker hue/SV/alpha bars, AddRectFilledMultiColor).
-- kind 1: colour varies along y (ca top, cb bottom), kind 2: along x (ca left, cb right). Same pixels as the two triangles.
@native
local function DrawRectGradient(x0, y0, x1, y1, ca, cb, kind, t, u, v, cx0, cy0, cx1, cy1)
local px0, py0 = max(ceil(x0 - 0.5), cx0), max(ceil(y0 - 0.5), cy0)
local px1, py1 = min(ceil(x1 - 0.5), cx1), min(ceil(y1 - 0.5), cy1)
if px0 >= px1 or py0 >= py1 then return end
local ra, ga, ba, aa = UnpackColor(ca)
local rb, gb, bb, ab = UnpackColor(cb)
if t then
local tr, tg, tb, ta = SampleTexture(t, u, v)
ra, ga, ba, aa = ra * tr / 255, ga * tg / 255, ba * tb / 255, aa * ta / 255
rb, gb, bb, ab = rb * tr / 255, gb * tg / 255, bb * tb / 255, ab * ta / 255
end
if kind == 1 then
local inv = 1 / (y1 - y0)
for y = py0, py1 - 1 do
local f = (y + 0.5 - y0) * inv
local nf = 1 - f
FillSpan(y, px0, px1, ra * nf + rb * f, ga * nf + gb * f, ba * nf + bb * f, aa * nf + ab * f)
end
else
-- Every row gets the same colours: when a row's destination equals the previous row's, copy its result
local inv = 1 / (x1 - x0)
local len = (px1 - px0) * 4
local opaque = aa >= 255 and ab >= 255
local prev_dst, prev_o = nil, 0
for y = py0, py1 - 1 do
local o = (y * buf_w + px0) * 4
local dst = (not opaque) and readstring(buf, o, len) or true
if prev_dst ~= nil and dst == prev_dst then
buffer.copy(buf, o, buf, prev_o, len)
else
local oo = o
for x = px0, px1 - 1 do
local f = (x + 0.5 - x0) * inv
local nf = 1 - f
BlendPixel(oo, ra * nf + rb * f, ga * nf + gb * f, ba * nf + bb * f, aa * nf + ab * f)
oo = oo + 4
end
prev_dst = dst
end
prev_o = o
end
end
end
-- Layer kinds per draw list: 1 = drawn before the window content (background, title bar, borders, scrollbar),
-- 2 = window content (positioned at the scroll origin inside a ClipsDescendants frame, so scrolling just moves it),
-- 3 = anything not clipped to the content drawn after content started (menu bar, overlays). Stacked 1 < 2 < 3.
local function NewFrame(parent, clips)
local frame = Instance.new("Frame")
frame.Name = "Layer"
frame.BackgroundTransparency = 1
frame.BorderSizePixel = 0
frame.Size = UDim2.fromOffset(0, 0)
frame.ClipsDescendants = clips
frame.Visible = false
frame.Parent = parent
return frame
end
local function GetLayer(bd, draw_list, kind)
local set = bd.Layers[draw_list]
if not set then set = { LastUsed = 0 }; bd.Layers[draw_list] = set end
local layer = set[kind]
if layer then return layer end
local root, frame
if kind == 2 then
root = NewFrame(bd.Gui, true)
frame = NewFrame(root, false); frame.Visible = true
else
frame = NewFrame(bd.Gui, false); root = frame
end
layer = { Root = root, Frame = frame, Tiles = {}, TH1 = {}, TH2 = {}, TN = {}, TL = {}, TL1 = {}, TL2 = {}, TLF = {}, TX = {}, TY = {}, Touched = {}, NTouched = 0,
Visible = false, X = false, Y = false, Z = -1, CX0 = false, CY0 = false, CX1 = false, CY1 = false }
set[kind] = layer
return layer
end
local function HideLayer(layer)
if layer.Visible then layer.Visible = false; layer.Root.Visible = false end
end
local function GetTile(bd, layer, key, tx, ty)
local tile = layer.Tiles[key]
if tile then return tile end
local img = AssetService:CreateEditableImage({ Size = Vector2.new(TILE, TILE) })
assert(img, "imgui_impl_roblox: EditableImage memory budget exceeded")
local label = Instance.new("ImageLabel")
label.Name = "Tile" .. key
label.BackgroundTransparency = 1
label.BorderSizePixel = 0
local scale = bd.Scale
label.Size = UDim2.fromOffset(TILE / scale, TILE / scale)
label.Position = UDim2.fromOffset(tx * TILE / scale, ty * TILE / scale)
label.ResampleMode = Enum.ResamplerMode.Pixelated
label.ImageContent = Content.fromObject(img)
label.Visible = false
label.Active = bd.TilesActive
label.Parent = layer.Frame
tile = { Image = img, Label = label, Buffer = buffer.create(TILE * TILE * 4), H1 = -1, H2 = -1, Visible = false, BX0 = TILE, BY0 = TILE, BX1 = 0, BY1 = 0,
ON = -1, OH1 = {}, OH2 = {}, OF = {}, OB0 = {}, OB1 = {}, OB2 = {}, OB3 = {} } -- last drawn primitive list (hashes, bounds in tile)
layer.Tiles[key] = tile
return tile
end
local function DestroyLayerSet(set)
for kind = 1, 3 do
local layer = set[kind]
if layer then
for _, tile in pairs(layer.Tiles) do tile.Image:Destroy() end
layer.Root:Destroy()
end
end
end
local function DestroyTiles(bd)
for _, set in pairs(bd.Layers) do DestroyLayerSet(set) end
bd.Layers = {}
end
-- Snapshot of everything in a draw list that affects its layers, compared exactly with the next frame's list:
-- 1 = identical (only restack), 2 = everything moved by the same whole pixels (dx, dy) (only move the layers: a window
-- being dragged), 0 = changed (full path). Compared in place (no strings built): early exit at the first difference.
local writef64 = buffer.writef64
local CMD_STRIDE = 70 -- clip (4 f64), ElemCount, IdxOffset, VtxOffset (u32), tex id (f64), kind (u8), free (u8), bounds of its primitives (4 x i32)
@native
local function Snapshot(set, draw_list, ox, oy, sw, sh)
local nv = draw_list.VtxBuffer.Size * 36 --[[VTX_STRIDE]]
local idx, ni = draw_list.IdxBuffer.Data, draw_list.IdxBuffer.Size
local ncmd = draw_list.CmdBuffer.Size
local sv, si, sc = set.SV, set.SI, set.SC
if not sv or buffer.len(sv) < nv then sv = buffer.create(max(nv, 64) * 2); set.SV = sv end
if not si or buffer.len(si) < ni * 4 then si = buffer.create(max(ni, 16) * 8); set.SI = si end
if not sc or buffer.len(sc) < ncmd * CMD_STRIDE then sc = buffer.create(max(ncmd, 4) * CMD_STRIDE * 2); set.SC = sc end
buffer.copy(sv, 0, draw_list.VtxBuffer.Buf, 0, nv)
for i = 1, ni do writeu32(si, (i - 1) * 4, idx[i]) end
local o, ci = 0, 0
for _, pcmd in draw_list.CmdBuffer:iter() do
if pcmd.UserCallback ~= nil then set.SnapOK = false; return end
local c = pcmd.ClipRect
writef64(sc, o, c.x); writef64(sc, o + 8, c.y); writef64(sc, o + 16, c.z); writef64(sc, o + 24, c.w)
writeu32(sc, o + 32, pcmd.ElemCount); writeu32(sc, o + 36, pcmd.IdxOffset); writeu32(sc, o + 40, pcmd.VtxOffset)
writef64(sc, o + 44, pcmd:GetTexID())
ci += 1
buffer.writeu8(sc, o + 52, cm_kind[ci] or 0); buffer.writeu8(sc, o + 53, cm_free[ci] and 1 or 0)
if cm_free[ci] then
buffer.writei32(sc, o + 54, cm_x0[ci]); buffer.writei32(sc, o + 58, cm_y0[ci]); buffer.writei32(sc, o + 62, cm_x1[ci]); buffer.writei32(sc, o + 66, cm_y1[ci])
end
o += CMD_STRIDE
end
set.SVN, set.SIN, set.SCN = nv, ni, ncmd
set.SOX, set.SOY, set.SSW, set.SSH = ox, oy, sw, sh
set.SCC = draw_list._ContentClipX0 ~= nil
set.SCX0, set.SCY0, set.SCX1, set.SCY1 = draw_list._ContentClipX0 or 0, draw_list._ContentClipY0 or 0, draw_list._ContentClipX1 or 0, draw_list._ContentClipY1 or 0
set.SSX, set.SSY = draw_list._ScrollOriginX or 0, draw_list._ScrollOriginY or 0
set.SnapOK = true
end
-- a + d == b exactly, with no rounding in a + d (so every later "x - origin" gives the same result as before the move)
@native
local function ShiftedExact(a, d, b)
local s = a + d
if s ~= b then return false end
local bb = s - a
return (a - (s - bb)) + (d - bb) == 0
end
@native
local function CompareDrawList(set, draw_list, ox, oy, sw, sh)
if not set or not set.SnapOK then return 0 end
local nv = draw_list.VtxBuffer.Size * 36 --[[VTX_STRIDE]]
local ni, ncmd = draw_list.IdxBuffer.Size, draw_list.CmdBuffer.Size
if nv ~= set.SVN or ni ~= set.SIN or ncmd ~= set.SCN or ox ~= set.SOX or oy ~= set.SOY or sw ~= set.SSW or sh ~= set.SSH then return 0 end
if (draw_list._ContentClipX0 ~= nil) ~= set.SCC then return 0 end
local vb, sv = draw_list.VtxBuffer.Buf, set.SV
if nv == 0 then return 1 end
-- translation from the first vertex: whole pixels only, and only with DisplayPos at 0 (so it stays exact)
local dx, dy = readf64(vb, 0) - readf64(sv, 0), readf64(vb, 8) - readf64(sv, 8)
if dx ~= dx or dy ~= dy then return 0 end
if dx ~= 0 or dy ~= 0 then
if dx ~= floor(dx) or dy ~= floor(dy) or ox ~= 0 or oy ~= 0 or not set.SLimFree then return 0 end
-- everything must stay as far from the clip limits as before (nothing newly clamped)
if set.SEX0 + dx < -CLIP_PAD or set.SEY0 + dy < -CLIP_PAD or set.SEX1 + dx > sw + CLIP_PAD or set.SEY1 + dy > sh + CLIP_PAD then return 0 end
end
local idx, si = draw_list.IdxBuffer.Data, set.SI
for i = 1, ni do if idx[i] ~= readu32(si, (i - 1) * 4) then return 0 end end
local sc, o = set.SC, 0
local ccx0, ccy0, ccx1, ccy1 = draw_list._ContentClipX0, draw_list._ContentClipY0, draw_list._ContentClipX1, draw_list._ContentClipY1
local seen_content = false
for _, pcmd in draw_list.CmdBuffer:iter() do
if pcmd.UserCallback ~= nil then return 0 end
if pcmd.ElemCount ~= readu32(sc, o + 32) or pcmd.IdxOffset ~= readu32(sc, o + 36) or pcmd.VtxOffset ~= readu32(sc, o + 40) or pcmd:GetTexID() ~= readf64(sc, o + 44) then return 0 end
local c = pcmd.ClipRect
if dx == 0 and dy == 0 then
if c.x ~= readf64(sc, o) or c.y ~= readf64(sc, o + 8) or c.z ~= readf64(sc, o + 16) or c.w ~= readf64(sc, o + 24) then return 0 end
elseif not (ShiftedExact(readf64(sc, o), dx, c.x) and ShiftedExact(readf64(sc, o + 8), dy, c.y)
and ShiftedExact(readf64(sc, o + 16), dx, c.z) and ShiftedExact(readf64(sc, o + 24), dy, c.w)) then
-- An unchanged clip rect (e.g. the viewport) is fine if it clipped none of this command's primitives before
-- the move and still clips none after it
if c.x ~= readf64(sc, o) or c.y ~= readf64(sc, o + 8) or c.z ~= readf64(sc, o + 16) or c.w ~= readf64(sc, o + 24)
or buffer.readu8(sc, o + 53) == 0 then return 0 end
if buffer.readi32(sc, o + 54) + dx < floor(c.x) or buffer.readi32(sc, o + 58) + dy < floor(c.y)
or buffer.readi32(sc, o + 62) + dx > floor(c.z) or buffer.readi32(sc, o + 66) + dy > floor(c.w) then return 0 end
end
-- same layer kind as before (same rule as the collect pass)
if pcmd.ElemCount > 0 then
local kind = 1
if ccx0 and c.x >= ccx0 and c.y >= ccy0 and c.z <= ccx1 and c.w <= ccy1 then kind = 2; seen_content = true
elseif seen_content then kind = 3 end
if kind ~= buffer.readu8(sc, o + 52) then return 0 end
end
o += CMD_STRIDE
end
if dx == 0 and dy == 0 then
if set.SCC and (draw_list._ContentClipX0 ~= set.SCX0 or draw_list._ContentClipY0 ~= set.SCY0 or draw_list._ContentClipX1 ~= set.SCX1
or draw_list._ContentClipY1 ~= set.SCY1 or draw_list._ScrollOriginX ~= set.SSX or draw_list._ScrollOriginY ~= set.SSY) then return 0 end
for v = 0, nv - 36 --[[VTX_STRIDE]], 36 --[[VTX_STRIDE]] do
if readf64(vb, v) ~= readf64(sv, v) or readf64(vb, v + 8) ~= readf64(sv, v + 8) or readf64(vb, v + 16) ~= readf64(sv, v + 16)
or readf64(vb, v + 24) ~= readf64(sv, v + 24) or readu32(vb, v + 32) ~= readu32(sv, v + 32) then return 0 end
end
return 1
end
if set.SCC and not (ShiftedExact(set.SCX0, dx, draw_list._ContentClipX0) and ShiftedExact(set.SCY0, dy, draw_list._ContentClipY0)
and ShiftedExact(set.SCX1, dx, draw_list._ContentClipX1) and ShiftedExact(set.SCY1, dy, draw_list._ContentClipY1)
and ShiftedExact(set.SSX, dx, draw_list._ScrollOriginX) and ShiftedExact(set.SSY, dy, draw_list._ScrollOriginY)) then return 0 end
for v = 0, nv - 36 --[[VTX_STRIDE]], 36 --[[VTX_STRIDE]] do
if not ShiftedExact(readf64(sv, v), dx, readf64(vb, v)) or not ShiftedExact(readf64(sv, v + 8), dy, readf64(vb, v + 8))
or readf64(vb, v + 16) ~= readf64(sv, v + 16) or readf64(vb, v + 24) ~= readf64(sv, v + 24) or readu32(vb, v + 32) ~= readu32(sv, v + 32) then return 0 end
end
return 2, dx, dy
end
-- A window moved by whole pixels: its tiles are unchanged, only the layer frames move
local function ShiftLayer(layer, dx, dy, scale)
layer.X += dx; layer.Y += dy
local root = layer.Root
if root ~= layer.Frame then
layer.CX0 += dx; layer.CY0 += dy; layer.CX1 += dx; layer.CY1 += dy
root.Position = UDim2.fromOffset(layer.CX0 / scale, layer.CY0 / scale)
else
root.Position = UDim2.fromOffset(layer.X / scale, layer.Y / scale)
end
end
-- Overlays: user Instances (e.g. a Frame holding a ViewportFrame) placed inside a window, registered every frame with
-- ImGui_ImplRoblox.SetOverlay() while that window is current. They stack right above the window's layers, move with
-- it, and (clip = true) are clipped to the window's content area like its widgets.
local function OverlayHolder(bd, draw_list, clip)
local hs = bd.OverlayHolders[draw_list]
if not hs then hs = {}; bd.OverlayHolders[draw_list] = hs end
local h = hs[clip]
if not h then
h = Instance.new("Frame")
h.Name = clip and "OverlayClip" or "Overlay"
h.BackgroundTransparency = 1
h.BorderSizePixel = 0
h.ClipsDescendants = clip
h.Size = UDim2.fromOffset(0, 0)
h.Parent = bd.Gui
hs[clip] = h
end
return h
end
local function PlaceOverlays(bd, draw_list, z, ox, oy)
local list = bd.Overlays[draw_list]
local hs = bd.OverlayHolders[draw_list]
if not list then
if hs then for _, h in pairs(hs) do h.Visible = false end end
return z
end
local scale, frame_no = bd.Scale, bd.FrameNo
z += 1
local used = {}
for _, o in ipairs(list) do
local clip = o.Clip and draw_list._ContentClipX0 ~= nil
local h = OverlayHolder(bd, draw_list, clip)
local hx, hy = 0, 0
if clip then
hx, hy = draw_list._ContentClipX0 - ox, draw_list._ContentClipY0 - oy
if not used[h] then
h.Position = UDim2.fromOffset(hx / scale, hy / scale)
h.Size = UDim2.fromOffset((draw_list._ContentClipX1 - draw_list._ContentClipX0) / scale, (draw_list._ContentClipY1 - draw_list._ContentClipY0) / scale)
end
elseif not used[h] then
h.Position = UDim2.fromOffset(0, 0)
end
used[h] = true
h.ZIndex = z
h.Visible = true
local f = o.Frame
if f.Parent ~= h then f.Parent = h end
f.Position = UDim2.fromOffset((o.X - ox - hx) / scale, (o.Y - oy - hy) / scale)
f.Size = UDim2.fromOffset(o.W / scale, o.H / scale)
f.Visible = true
bd.OverlayShown[f] = frame_no
end
if hs then for _, h in pairs(hs) do if not used[h] then h.Visible = false end end end
return z
end
--- Show `frame` this frame at (x, y, w, h) (imgui coordinates) inside the current window. Call every frame it should
--- be visible; frames not submitted are hidden. clip: clip to the window's content area (scrolls with it).
function ImGui_ImplRoblox_SetOverlay(frame, x, y, w, h, clip)
local bd = ImGui_ImplRoblox_GetBackendData()
local dl = ImGui.GetWindowDrawList()
local list = bd.Overlays[dl]
if not list then list = {}; bd.Overlays[dl] = list end
list[#list + 1] = { Frame = frame, X = x, Y = y, W = w, H = h, Clip = clip ~= false }
end
-- MicroProfiler labels, enabled with ImGui_ImplRoblox.SetProfiling(true)
local profilebegin, profileend = debug.profilebegin, debug.profileend
local profiling = false
local function PB(name) if profiling then profilebegin(name) end end
local function PE() if profiling then profileend() end end
@native
local function DrawLayer(bd, layer, plist, np, lx0, ly0, ox, oy, force, z, clip_x0, clip_y0, clip_x1, clip_y1)
local scale = bd.Scale
local deferred = false
-- Pass 2: hash primitives into layer-relative tiles
PB("ImGui tile hash")
local th1, th2, tn, tl, touched = layer.TH1, layer.TH2, layer.TN, layer.TL, layer.Touched
local tl1, tl2, tlf = layer.TL1, layer.TL2, layer.TLF -- per-tile list of the primitives' hashes (for partial redraws)
for k = 1, layer.NTouched do tn[touched[k]] = 0 end
local nt = 0
for pi = 1, np do
local p = plist[pi]
local tex_id = pr_tex[p]
-- The clip rect only affects pixels inside the primitive's bounds, so hash the clipped bounds (relative)
-- instead of the clip itself: a window clipped by the (fixed) viewport rect stays hash-stable when moved.
local cx0, cy0, cx1, cy1 = pr_bx0[p] - lx0, pr_by0[p] - ly0, pr_bx1[p] - lx0, pr_by1[p] - ly0
local vbuf = pr_vb[p]
local va, vd = pr_a[p], pr_d[p]
-- Flat rect (single colour, no gradient, constant uv): its pixels in a tile are exactly its clipped pixel rect
-- inside that tile filled with one colour, so hash that per tile instead of the vertices. Resizing a window
-- then only changes the tiles its edges cross, not every tile its background covers.
local flat_rect, rcol, ruv = false, 0, 0
if vd and not pr_g[p] then
local u0, v0 = readf64(vbuf, va + 16), readf64(vbuf, va + 24)
local vc = pr_c[p]
if not pr_t[p] or (u0 == readf64(vbuf, vc + 16) and v0 == readf64(vbuf, vc + 24)) then
flat_rect, rcol, ruv = true, readu32(vbuf, va + 32), u0 * 8192 + v0
end
end
local h1, h2 = 0, 0
local fsig = -1 -- flat rect: colour/uv/texture without the geometry (-1: not a flat rect)
if flat_rect then fsig = ((rcol * 8191 + ruv * 4096) % P1 * 64 + tex_id) % P1 end
if not flat_rect then
h1 = ((cx0 * 8191 + cy0) * 8191 + cx1) % P1
h2 = ((cy1 * 8191 + cx1) * 8191 + cy0 + tex_id) % P2
local vax, vay = lx0 + ox, ly0 + oy
if vd then
-- Axis aligned quad: the collect pass checked that TR/BL (positions, uvs, colours) follow from TL/BR
-- (and the gradient kind), so TL + BR + kind determine its pixels
h1, h2 = HashVertex(h1, h2, vbuf, va, vax, vay)
h1, h2 = HashVertex(h1, h2, vbuf, pr_c[p], vax, vay)
local gk = pr_g[p]
if gk then h1 = (h1 * 48271 + gk) % P1; h2 = (h2 * 16807 + gk * 3) % P2 end
else
h1, h2 = HashVertex(h1, h2, vbuf, va, vax, vay)
h1, h2 = HashVertex(h1, h2, vbuf, pr_b[p], vax, vay)
h1, h2 = HashVertex(h1, h2, vbuf, pr_c[p], vax, vay)
end
end
for ty = floor((pr_by0[p] - ly0) / TILE), floor((pr_by1[p] - 1 - ly0) / TILE) do
for tx = floor((pr_bx0[p] - lx0) / TILE), floor((pr_bx1[p] - 1 - lx0) / TILE) do
if flat_rect then
local tx0, ty0 = tx * TILE, ty * TILE
local ix0, iy0 = max(cx0 - tx0, 0), max(cy0 - ty0, 0)
local ix1, iy1 = min(cx1 - tx0, TILE), min(cy1 - ty0, TILE)
local r = ((ix0 * 131 + iy0) * 131 + ix1) * 131 + iy1
h1 = ((r * 48271 + rcol) % P1 * 48271 + ruv * 4096 + 7) % P1
h2 = ((r * 16807 + rcol + tex_id) % P2 * 16807 + ruv * 65521 + 11) % P2
end
local key = ty * KEY_W + tx
local k = tn[key]
if not k or k == 0 then
k = 0
th1[key], th2[key] = 0, 0
if not tl[key] then tl[key] = {}; tl1[key] = {}; tl2[key] = {}; tlf[key] = {} end
nt = nt + 1
touched[nt] = key
layer.TX[key], layer.TY[key] = tx, ty
end
k = k + 1
tn[key] = k
tl[key][k] = p
tl1[key][k], tl2[key][k], tlf[key][k] = h1, h2, fsig
th1[key] = (th1[key] * 48271 + h1) % P1
th2[key] = (th2[key] * 16807 + h2 + k) % P2
end
end
end
layer.NTouched = nt
PE()
-- Place the layer (this is all a window move costs)
local root = layer.Root
if root ~= layer.Frame then
-- content: clip frame at the content clip rect, tiles frame at the scroll origin relative to it
if layer.CX0 ~= clip_x0 or layer.CY0 ~= clip_y0 or layer.CX1 ~= clip_x1 or layer.CY1 ~= clip_y1 then
layer.CX0, layer.CY0, layer.CX1, layer.CY1 = clip_x0, clip_y0, clip_x1, clip_y1
root.Position = UDim2.fromOffset(clip_x0 / scale, clip_y0 / scale)
root.Size = UDim2.fromOffset((clip_x1 - clip_x0) / scale, (clip_y1 - clip_y0) / scale)
layer.X = false
end
if layer.X ~= lx0 or layer.Y ~= ly0 then
layer.X, layer.Y = lx0, ly0
layer.Frame.Position = UDim2.fromOffset((lx0 - clip_x0) / scale, (ly0 - clip_y0) / scale)
end
elseif layer.X ~= lx0 or layer.Y ~= ly0 then
layer.X, layer.Y = lx0, ly0
root.Position = UDim2.fromOffset(lx0 / scale, ly0 / scale)
end
if layer.Z ~= z then layer.Z = z; root.ZIndex = z end
if not layer.Visible then layer.Visible = true; root.Visible = true end
-- Pass 3: redraw + upload changed tiles, hide unused ones
PB("ImGui raster+upload")
for key, tile in pairs(layer.Tiles) do
if not tn[key] or tn[key] == 0 then
if tile.Visible then tile.Visible = false; tile.Label.Visible = false; tile.H1 = -1; tile.ON = -1 end
end
end
for kk = 1, nt do
local key = touched[kk]
local count = tn[key]
local tx, ty = layer.TX[key], layer.TY[key]
local tile = GetTile(bd, layer, key, tx, ty)
local dirty = force or tile.H1 ~= th1[key] or tile.H2 ~= th2[key]
if dirty and bd.RedrawStarted and (tile.Deferred or 0) < MAX_DEFER_FRAMES
and ((bd.RedrawBudget > 0 and os.clock() - bd.FrameT0 >= bd.RedrawBudget) or (bd.TileLimit and bd.FrameTiles >= bd.TileLimit)) then -- (TileLimit: test hook, tools/test/scripts/t_pixels.luau)
-- Over this frame's redraw budget: keep showing the old pixels, redraw on a following frame. The tile's
-- stored hashes/list still describe what it shows, so the next comparison is against the real content.
tile.Deferred = (tile.Deferred or 0) + 1
if force then tile.H1 = -1; tile.ON = -1 end -- full redraw later (force doesn't persist)
deferred = true
dirty = false
end
if dirty then
bd.RedrawStarted = true
bd.FrameTiles += 1
tile.Deferred = 0
tile.H1, tile.H2 = th1[key], th2[key]
local x0, y0 = lx0 + tx * TILE, ly0 + ty * TILE
local x1, y1 = x0 + TILE, y0 + TILE
buf, buf_w = tile.Buffer, TILE
local list, lh1, lh2 = tl[key], tl1[key], tl2[key]
local oh1, oh2, ob0, ob1, ob2, ob3, on = tile.OH1, tile.OH2, tile.OB0, tile.OB1, tile.OB2, tile.OB3, tile.ON
-- New content bounds (union of the primitives' bounds in this tile)
local nx0, ny0, nx1, ny1 = TILE, TILE, 0, 0
for k = 1, count do
local p = list[k]
local a, b = pr_bx0[p] - x0, pr_by0[p] - y0
local c, d = pr_bx1[p] - x0, pr_by1[p] - y0
if a < nx0 then nx0 = a end
if b < ny0 then ny0 = b end
if c > nx1 then nx1 = c end
if d > ny1 then ny1 = d end
end
nx0, ny0, nx1, ny1 = max(nx0, 0), max(ny0, 0), min(nx1, TILE), min(ny1, TILE)
local rx0, ry0, rx1, ry1
if force or on < 0 then
-- Dirty region = old content bounds + new content bounds. Outside it the tile is already
-- transparent, so only this region is cleared and uploaded (edge tiles are often mostly empty).
rx0, ry0, rx1, ry1 = min(nx0, tile.BX0), min(ny0, tile.BY0), max(nx1, tile.BX1), max(ny1, tile.BY1)
else
-- Partial redraw: the primitives before the first and after the last changed one are the same as last
-- time. A pixel that no changed primitive (old or new) touches is covered by the same primitives in the
-- same order, so it is already correct: only the bounds of the changed ones are cleared and redrawn.
local i, lim = 1, min(on, count)
while i <= lim and oh1[i] == lh1[i] and oh2[i] == lh2[i] do i += 1 end
local j = 0
while j <= lim - i and oh1[on - j] == lh1[count - j] and oh2[on - j] == lh2[count - j] do j += 1 end
rx0, ry0, rx1, ry1 = TILE, TILE, 0, 0
local of, lf = tile.OF, tlf[key]
local paired = on - j - i == count - j - i
for k = i, count - j do
local p = list[k]
local a, b, c, d = max(pr_bx0[p] - x0, 0), max(pr_by0[p] - y0, 0), min(pr_bx1[p] - x0, TILE), min(pr_by1[p] - y0, TILE)
local oa, ob, oc, od = ob0[k], ob1[k], ob2[k], ob3[k]
if paired and lf[k] >= 0 and lf[k] == of[k] then
-- Same flat rect (colour/uv/texture) with new bounds: pixels inside both are unchanged, only the
-- strips between the old and new edges can differ (e.g. a window background growing on resize)
local uy0, uy1 = min(b, ob), max(d, od)
local ux0, ux1 = min(a, oa), max(c, oc)
if a ~= oa then ux0 = min(a, oa); local e = max(a, oa); rx0 = min(rx0, ux0); rx1 = max(rx1, e); ry0 = min(ry0, uy0); ry1 = max(ry1, uy1) end
if c ~= oc then local e = min(c, oc); rx0 = min(rx0, e); rx1 = max(rx1, max(c, oc)); ry0 = min(ry0, uy0); ry1 = max(ry1, uy1) end
if b ~= ob then ry0 = min(ry0, min(b, ob)); ry1 = max(ry1, max(b, ob)); rx0 = min(rx0, ux0); rx1 = max(rx1, ux1) end
if d ~= od then ry0 = min(ry0, min(d, od)); ry1 = max(ry1, max(d, od)); rx0 = min(rx0, ux0); rx1 = max(rx1, ux1) end
else
if a < rx0 then rx0 = a end
if b < ry0 then ry0 = b end
if c > rx1 then rx1 = c end
if d > ry1 then ry1 = d end
if k <= on - j then
if oa < rx0 then rx0 = oa end
if ob < ry0 then ry0 = ob end
if oc > rx1 then rx1 = oc end
if od > ry1 then ry1 = od end
end
end
end
for k = count - j + 1, on - j do -- old middle longer than the new one
if ob0[k] < rx0 then rx0 = ob0[k] end
if ob1[k] < ry0 then ry0 = ob1[k] end
if ob2[k] > rx1 then rx1 = ob2[k] end
if ob3[k] > ry1 then ry1 = ob3[k] end
end
end
tile.BX0, tile.BY0, tile.BX1, tile.BY1 = nx0, ny0, nx1, ny1
-- Remember this list for the next partial redraw
for k = 1, count do
local p = list[k]
oh1[k], oh2[k], tile.OF[k] = lh1[k], lh2[k], tlf[key][k]
ob0[k], ob1[k], ob2[k], ob3[k] = max(pr_bx0[p] - x0, 0), max(pr_by0[p] - y0, 0), min(pr_bx1[p] - x0, TILE), min(pr_by1[p] - y0, TILE)
end
tile.ON = count
local rw = rx1 - rx0
if rw == TILE then
buffer.fill(buf, ry0 * TILE * 4, 0, (ry1 - ry0) * TILE * 4)
else
for y = ry0, ry1 - 1 do buffer.fill(buf, (y * TILE + rx0) * 4, 0, rw * 4) end
end
local tox, toy = ox + x0, oy + y0
for k = 1, count do
local p = list[k]
-- clip to the dirty region (only those pixels are redrawn)
local cx0, cy0 = max(pr_cx0[p] - x0, rx0), max(pr_cy0[p] - y0, ry0)
local cx1, cy1 = min(pr_cx1[p] - x0, rx1), min(pr_cy1[p] - y0, ry1)
if cx0 >= cx1 or cy0 >= cy1 or pr_bx0[p] - x0 >= rx1 or pr_by0[p] - y0 >= ry1 or pr_bx1[p] - x0 <= rx0 or pr_by1[p] - y0 <= ry0 then continue end
local va, vc, vd, vbuf = pr_a[p], pr_c[p], pr_d[p], pr_vb[p]
if vd then
local ax, ay, cx, cy = readf64(vbuf, va) - tox, readf64(vbuf, va + 8) - toy, readf64(vbuf, vc) - tox, readf64(vbuf, vc + 8) - toy
local gk = pr_g[p]
if gk then
DrawRectGradient(ax, ay, cx, cy, readu32(vbuf, va + 32), readu32(vbuf, vc + 32), gk, pr_t[p], readf64(vbuf, va + 16), readf64(vbuf, va + 24), cx0, cy0, cx1, cy1)
else
DrawRect(ax, ay, cx, cy, readf64(vbuf, va + 16), readf64(vbuf, va + 24), readf64(vbuf, vc + 16), readf64(vbuf, vc + 24), readu32(vbuf, va + 32), pr_t[p], cx0, cy0, cx1, cy1)
end
else
DrawTriangle(vbuf, va, pr_b[p], vc, pr_t[p], cx0, cy0, cx1, cy1, tox, toy)
end
end
PB("ImGui upload")
if rw == TILE and ry0 == 0 and ry1 == TILE then
tile.Image:WritePixelsBuffer(Vector2.zero, TILE_SIZE, buf)
elseif rw > 0 and ry1 > ry0 then
local rh = ry1 - ry0
local sk = rw * 1024 + rh -- partial upload buffers are reused (WritePixelsBuffer needs the exact size)
local sub = sub_bufs[sk]
if not sub then
if n_sub_bufs >= 256 then table.clear(sub_bufs); n_sub_bufs = 0 end
sub = buffer.create(rw * rh * 4); sub_bufs[sk] = sub; n_sub_bufs += 1
end
for y = 0, rh - 1 do buffer.copy(sub, y * rw * 4, buf, ((ry0 + y) * TILE + rx0) * 4, rw * 4) end
tile.Image:WritePixelsBuffer(Vector2.new(rx0, ry0), Vector2.new(rw, rh), sub)
end
PE()
end
if not tile.Visible and (tile.Deferred or 0) == 0 then -- (a deferred tile that was hidden holds old pixels: stays hidden until drawn)
tile.Visible = true
tile.Label.Visible = true
end
end
PE()
return deferred
end
@native
function ImGui_ImplRoblox_RenderDrawData(draw_data)
local bd = ImGui_ImplRoblox_GetBackendData()
PB("ImGui RenderDrawData")
bd.FrameT0 = os.clock()
bd.FrameTiles = 0
bd.RedrawStarted = false -- the first dirty tile of a frame is always redrawn (progress), the rest within the budget
bd.Stale = nil
PB("ImGui textures")
if draw_data.Textures ~= nil then
for _, tex in draw_data.Textures:iter() do
if tex.Status ~= 0 --[[ImTextureStatus.OK]] then
-- No redraw needed for WantUpdates: it only writes texture portions which have never been used before
-- (new glyphs/rects), so no existing tile samples them. Created textures get a new (never reused) id.
ImGui_ImplRoblox_UpdateTexture(tex)
end
end
end
PE()
-- Render rate cap: textures are always updated (imgui expects it), tiles only at bd.RenderRate Hz
-- Optional lower cap while scrolling / resizing / moving a window (SetBusyRenderRate, off by default)
local rate = bd.RenderRate
if bd.BusyRenderRate > 0 then
local g = ImGui.GetCurrentContext()
local busy = g.WheelingWindow ~= nil or g.MovingWindow ~= nil -- (io.MouseWheel is already cleared by EndFrame)
if not busy and g.ActiveId ~= 0 then
local w = g.ActiveIdWindow
local cur = g.MouseCursor -- ResizeAll..ResizeNWSE while a resize grip/border is held
busy = (cur >= 2 --[[ImGuiMouseCursor.ResizeAll]] and cur <= 6 --[[ImGuiMouseCursor.ResizeNWSE]])
or (w ~= nil and (g.ActiveId == ImGui.GetWindowScrollbarID(w, 1 --[[ImGuiAxis.X]]) or g.ActiveId == ImGui.GetWindowScrollbarID(w, 2 --[[ImGuiAxis.Y]])))
end
if busy and (rate <= 0 or bd.BusyRenderRate < rate) then rate = bd.BusyRenderRate end
end
if rate > 0 then
local now = os.clock()
-- Skipped: kept pending, so if no new frame comes (the app stopped rendering) Heartbeat still draws it
if now + 0.002 < bd.NextRender then bd.Pending = draw_data; PE(); return end
bd.NextRender = math.max(bd.NextRender + 1 / rate, now)
end
local sw, sh = floor(draw_data.DisplaySize.x), floor(draw_data.DisplaySize.y)
if sw <= 0 or sh <= 0 then PE(); return end
local ox, oy = draw_data.DisplayPos.x, draw_data.DisplayPos.y
local lim_x0, lim_y0, lim_x1, lim_y1 = -CLIP_PAD, -CLIP_PAD, sw + CLIP_PAD, sh + CLIP_PAD
bd.FrameNo += 1
local frame_no = bd.FrameNo
local force = bd.ForceRedraw
bd.ForceRedraw = false
local n = 0
local z = 0
local any_deferred = false
for _, draw_list in draw_data.CmdLists:iter() do
-- Unchanged draw list (same vertices, indices, commands, origin and scroll): its layers are already correct,
-- only restack them. Moved by whole pixels (a dragged window): only move its layers. Either way no collect + hash.
PB("ImGui unchanged check")
local set = bd.Layers[draw_list]
local same, sdx, sdy = 0, 0, 0
if not force then same, sdx, sdy = CompareDrawList(set, draw_list, ox, oy, sw, sh) end
if same ~= 0 then
for kind = 1, 3 do
local layer = set[kind]
if layer and layer.Visible then
if same == 2 then ShiftLayer(layer, sdx, sdy, bd.Scale) end
z += 1
if layer.Z ~= z then layer.Z = z; layer.Root.ZIndex = z end
end
end
if same == 2 then
Snapshot(set, draw_list, ox, oy, sw, sh)
set.SEX0 += sdx; set.SEY0 += sdy; set.SEX1 += sdx; set.SEY1 += sdy
end
set.LastUsed = frame_no
z = PlaceOverlays(bd, draw_list, z, ox, oy)
PE()
continue
end
PE()
-- Pass 1: collect this list's primitives and their bounds
PB("ImGui collect")
local first = n + 1
local vbuf, idx = draw_list.VtxBuffer.Buf, draw_list.IdxBuffer.Data
local ccx0, ccy0, ccx1, ccy1 = draw_list._ContentClipX0, draw_list._ContentClipY0, draw_list._ContentClipX1, draw_list._ContentClipY1
local content_x0, content_y0, content_x1, content_y1
if ccx0 then
content_x0, content_y0 = max(floor(ccx0 - ox), lim_x0), max(floor(ccy0 - oy), lim_y0)
content_x1, content_y1 = min(floor(ccx1 - ox), lim_x1), min(floor(ccy1 - oy), lim_y1)
end
local seen_content = false
-- extents of everything clamped by the clip limits below (lim_free: nothing was), for the move fast path
local ex0, ey0, ex1, ey1 = math.huge, math.huge, -math.huge, -math.huge
local lim_free = true
if ccx0 then
local a, b, c, d = floor(ccx0 - ox), floor(ccy0 - oy), floor(ccx1 - ox), floor(ccy1 - oy)
if a < lim_x0 or b < lim_y0 or c > lim_x1 or d > lim_y1 then lim_free = false end
ex0, ey0, ex1, ey1 = min(ex0, a), min(ey0, b), max(ex1, c), max(ey1, d)
end
local ci = 0
for _, pcmd in draw_list.CmdBuffer:iter() do
ci += 1
cm_free[ci], cm_kind[ci] = false, 0
if pcmd.UserCallback ~= nil then
pcmd.UserCallback(draw_list, pcmd)
elseif pcmd.ElemCount > 0 then
local clip = pcmd.ClipRect
local rcx0, rcy0, rcx1, rcy1 = floor(clip.x - ox), floor(clip.y - oy), floor(clip.z - ox), floor(clip.w - oy)
if rcx0 < lim_x0 or rcy0 < lim_y0 or rcx1 > lim_x1 or rcy1 > lim_y1 then lim_free = false end
if rcx0 < ex0 then ex0 = rcx0 end
if rcy0 < ey0 then ey0 = rcy0 end
if rcx1 > ex1 then ex1 = rcx1 end
if rcy1 > ey1 then ey1 = rcy1 end
local cx0, cy0 = max(rcx0, lim_x0), max(rcy0, lim_y0)
local cx1, cy1 = min(rcx1, lim_x1), min(rcy1, lim_y1)
local kind = 1
if ccx0 and clip.x >= ccx0 and clip.y >= ccy0 and clip.z <= ccx1 and clip.w <= ccy1 then
kind = 2; seen_content = true
if clip.x == ccx0 and clip.y == ccy0 and clip.z == ccx1 and clip.w == ccy1 then
-- clipped by the layer's clip frame instead: partly visible rows stay hash-stable while scrolling
cx0, cy0, cx1, cy1 = lim_x0, lim_y0, lim_x1, lim_y1
end
elseif seen_content then
kind = 3
end
cm_kind[ci] = kind
local cfree, cex0, cey0, cex1, cey1 = cx1 > cx0 and cy1 > cy0, math.huge, math.huge, -math.huge, -math.huge
if cx1 > cx0 and cy1 > cy0 then
local tex_id = pcmd:GetTexID()
local t = bd.Textures[tex_id]
local vo = pcmd.VtxOffset
local i, last = pcmd.IdxOffset + 1, pcmd.IdxOffset + pcmd.ElemCount
while i <= last do
local ia, ic = idx[i], idx[i + 2]
-- byte offsets of the vertices (index is 1-based)
local va, vb, vc = (vo + ia - 1) * 36 --[[VTX_STRIDE]], (vo + idx[i + 1] - 1) * 36 --[[VTX_STRIDE]], (vo + ic - 1) * 36 --[[VTX_STRIDE]]
local vd = false
local gk = false
local pax, pay = readf64(vbuf, va), readf64(vbuf, va + 8)
local pbx, pby = readf64(vbuf, vb), readf64(vbuf, vb + 8)
local pcx, pcy = readf64(vbuf, vc), readf64(vbuf, vc + 8)
local bx0, by0, bx1, by1
if i + 5 <= last and idx[i + 3] == ia and idx[i + 4] == ic then
local d = (vo + idx[i + 5] - 1) * 36 --[[VTX_STRIDE]]
local pdx, pdy = readf64(vbuf, d), readf64(vbuf, d + 8)
-- axis aligned quad (TL, TR, BR, BL)
if pay == pby and pbx == pcx and pcy == pdy and pdx == pax and pax < pbx and pay < pdy then
local col, colb, colc, cold = readu32(vbuf, va + 32), readu32(vbuf, vb + 32), readu32(vbuf, vc + 32), readu32(vbuf, d + 32)
local tax, tay = readf64(vbuf, va + 16), readf64(vbuf, va + 24)
local tbx, tby = readf64(vbuf, vb + 16), readf64(vbuf, vb + 24)
local tcx, tcy = readf64(vbuf, vc + 16), readf64(vbuf, vc + 24)
local tdx, tdy = readf64(vbuf, d + 16), readf64(vbuf, d + 24)
if colb == col and colc == col and cold == col then
if tbx == tcx and tby == tay and tdx == tax and tdy == tcy then vd = d end
elseif tax == tbx and tax == tcx and tax == tdx and tay == tby and tay == tcy and tay == tdy then
-- constant uv, linear gradient: vertical (top/bottom pairs equal) or horizontal (left/right pairs equal)
if colb == col and cold == colc then vd = d; gk = 1
elseif cold == col and colb == colc then vd = d; gk = 2 end
end
if vd then
bx0, by0 = ceil(pax - ox - 0.5), ceil(pay - oy - 0.5)
bx1, by1 = ceil(pcx - ox - 0.5), ceil(pcy - oy - 0.5)
end
end
end
if vd then
i = i + 6
else
bx0, by0 = floor(min(pax, pbx, pcx) - ox), floor(min(pay, pby, pcy) - oy)
bx1, by1 = ceil(max(pax, pbx, pcx) - ox) + 1, ceil(max(pay, pby, pcy) - oy) + 1
i = i + 3
end
if bx0 < cx0 or by0 < cy0 or bx1 > cx1 or by1 > cy1 then cfree = false end
if bx0 < cex0 then cex0 = bx0 end
if by0 < cey0 then cey0 = by0 end
if bx1 > cex1 then cex1 = bx1 end
if by1 > cey1 then cey1 = by1 end
if bx0 < ex0 then ex0 = bx0 end
if by0 < ey0 then ey0 = by0 end
if bx1 > ex1 then ex1 = bx1 end
if by1 > ey1 then ey1 = by1 end
if bx0 < lim_x0 or by0 < lim_y0 or bx1 > lim_x1 or by1 > lim_y1 then lim_free = false end
if bx0 < cx0 then bx0 = cx0 end
if by0 < cy0 then by0 = cy0 end
if bx1 > cx1 then bx1 = cx1 end
if by1 > cy1 then by1 = cy1 end
if bx0 < bx1 and by0 < by1 then
n = n + 1
pr_a[n], pr_b[n], pr_c[n], pr_d[n], pr_t[n], pr_g[n], pr_tex[n], pr_vb[n] = va, vb, vc, vd, t, gk, tex_id, vbuf
pr_cx0[n], pr_cy0[n], pr_cx1[n], pr_cy1[n] = cx0, cy0, cx1, cy1
pr_bx0[n], pr_by0[n], pr_bx1[n], pr_by1[n] = bx0, by0, bx1, by1
pr_k[n] = kind
end
end
end
if cex0 > cex1 then cex0, cey0, cex1, cey1 = 0, 0, 0, 0 end -- (no primitives)
cm_free[ci] = cfree
cm_x0[ci], cm_y0[ci], cm_x1[ci], cm_y1[ci] = cex0, cey0, cex1, cey1
end
end
PE()
-- Split into layer kinds and place each one
set = bd.Layers[draw_list]
local list_deferred = false
local nk1, nk2, nk3 = 0, 0, 0
local o1x, o1y, o3x, o3y = math.huge, math.huge, math.huge, math.huge
for p = first, n do
local k = pr_k[p]
if k == 1 then
nk1 += 1; plist1[nk1] = p
if pr_bx0[p] < o1x then o1x = pr_bx0[p] end
if pr_by0[p] < o1y then o1y = pr_by0[p] end
elseif k == 2 then
nk2 += 1; plist2[nk2] = p
else
nk3 += 1; plist3[nk3] = p
if pr_bx0[p] < o3x then o3x = pr_bx0[p] end
if pr_by0[p] < o3y then o3y = pr_by0[p] end
end
end
if nk1 > 0 then
z += 1
if DrawLayer(bd, GetLayer(bd, draw_list, 1), plist1, nk1, o1x, o1y, ox, oy, force, z) then list_deferred = true end
elseif set and set[1] then HideLayer(set[1]) end
if nk2 > 0 then
z += 1
if DrawLayer(bd, GetLayer(bd, draw_list, 2), plist2, nk2, floor(draw_list._ScrollOriginX - ox), floor(draw_list._ScrollOriginY - oy), ox, oy, force, z,
content_x0, content_y0, content_x1, content_y1) then list_deferred = true end
elseif set and set[2] then HideLayer(set[2]) end
if nk3 > 0 then
z += 1
if DrawLayer(bd, GetLayer(bd, draw_list, 3), plist3, nk3, o3x, o3y, ox, oy, force, z) then list_deferred = true end
elseif set and set[3] then HideLayer(set[3]) end
set = bd.Layers[draw_list]
-- (deferred tiles: not "unchanged" next frame even if the draw list is, so they get redrawn)
if set then
set.LastUsed = frame_no
if list_deferred then set.SnapOK = false else
Snapshot(set, draw_list, ox, oy, sw, sh)
set.SLimFree = lim_free
set.SEX0, set.SEY0, set.SEX1, set.SEY1 = ex0, ey0, ex1, ey1
end
end
if list_deferred then any_deferred = true end
z = PlaceOverlays(bd, draw_list, z, ox, oy)
end
-- Overlay frames not submitted this frame are hidden (never destroyed: the user owns them)
for frame, shown in pairs(bd.OverlayShown) do
if shown ~= frame_no then frame.Visible = false; bd.OverlayShown[frame] = nil end
end
table.clear(bd.Overlays)
-- Hide layers not drawn this frame; free long unused ones (closed windows, old tooltips)
for draw_list, set in pairs(bd.Layers) do
if set.LastUsed ~= frame_no then
set.SnapOK = false -- hidden now: must go through the full path when shown again
for kind = 1, 3 do if set[kind] then HideLayer(set[kind]) end end
if frame_no - set.LastUsed > LAYER_KEEP_FRAMES then
DestroyLayerSet(set)
bd.Layers[draw_list] = nil
end
end
end
-- drop references so old vertex tables can be collected
for k = n + 1, bd.LastPrimCount do pr_t[k], pr_vb[k] = nil, nil end
bd.LastPrimCount = n
-- Tiles left for later: if the app doesn't submit a new frame, Heartbeat finishes them from this draw data
if any_deferred then bd.Stale = draw_data; bd.StaleHB = bd.HB end
PE()
end
---------------------------------------------------------
-- CORE LIFECYCLE
---------------------------------------------------------
--- @param parent Instance? # where the ScreenGui goes, defaults to the local player's PlayerGui
--- @param render_scale number? # imgui pixels per Roblox UI point (defaults to 1). Roblox UI points are scaled by the
--- # OS display scale, so on a 150% display pass 1.5 to render at physical resolution
--- # (imgui then looks the same size as a desktop imgui app). Change later with SetRenderScale().
function ImGui_ImplRoblox_Init(parent, render_scale)
local io = ImGui.GetIO()
local gui = Instance.new("ScreenGui")
gui.Name = "ImGui"
gui.IgnoreGuiInset = true -- so GetMouseLocation() and the tiles share the same origin
gui.ResetOnSpawn = false
gui.DisplayOrder = 1000
gui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling -- layers (one Frame per draw list) stack by ZIndex
gui.Parent = parent or Players.LocalPlayer:WaitForChild("PlayerGui")
local bd = {
Time = os.clock(), Gui = gui, Connections = {},
Textures = {}, TextureCount = 0, MouseX = -1, MouseY = -1,
Layers = {}, Overlays = {}, OverlayHolders = {}, OverlayShown = {}, FrameNo = 0, Scale = render_scale or 1, TilesActive = false, RenderRate = 0, BusyRenderRate = 0, RedrawBudget = 0, HB = 0, NextRender = 0, LastPrimCount = 0, ForceRedraw = true,
}
io.BackendPlatformUserData = bd
io.BackendPlatformName = "imgui_impl_roblox"
io.BackendRendererName = "imgui_impl_roblox"
io.BackendFlags = bit32.bor(io.BackendFlags, 16 --[[ImGuiBackendFlags.RendererHasTextures]], 8 --[[ImGuiBackendFlags.RendererHasVtxOffset]])
local platform_io = ImGui.GetPlatformIO()
platform_io.Platform_SetClipboardTextFn = ImGui_ImplRoblox_PlatformSetClipboardText
platform_io.Platform_GetClipboardTextFn = ImGui_ImplRoblox_PlatformGetClipboardText
local screen = gui.AbsoluteSize
ImGui_ImplRoblox_UpdateMonitors(screen.X, screen.Y)
local function connect(signal, fn) table.insert(bd.Connections, signal:Connect(fn)) end
connect(UserInputService.InputBegan, function(input, game_processed)
local io = ImGui.GetIO()
local button = MOUSE_BUTTONS[input.UserInputType]
if button then
io:AddMouseSourceEvent(0 --[[ImGuiMouseSource.Mouse]])
io:AddMouseButtonEvent(button, true)
elseif input.UserInputType == Enum.UserInputType.Keyboard and (not game_processed or proxy_focused) then
local ctrl, shift, alt = ImGui_ImplRoblox_UpdateKeyModifiers()
local key = KEY_MAP[input.KeyCode]
if key then io:AddKeyEvent(key, true) end
if proxy_focused and (key == 523 --[[ImGuiKey.Backspace]] or key == 522 --[[ImGuiKey.Delete]]) then ImGui_ImplRoblox_GetBackendData().DeleteKeySeen = true end
-- Text: printable ASCII keys, skipped while Ctrl/Alt are held (shortcuts)
local v = input.KeyCode.Value
if v >= 32 and v < 127 and not (ctrl or alt) then
local c = string.char(v)
if shift then c = SHIFTED[c] or c:upper() end
-- While the proxy may own text input, hold the char: NewFrame uses the proxy's text if it changed
-- (correct layout/unicode), otherwise these. So typing works even if the proxy silently lost focus.
if proxy_focused then table.insert(ImGui_ImplRoblox_GetBackendData().PendingChars, string.byte(c))
else io:AddInputCharacter(string.byte(c)) end
end
end
end)
connect(UserInputService.InputEnded, function(input, game_processed)
local io = ImGui.GetIO()
local button = MOUSE_BUTTONS[input.UserInputType]
if button then
io:AddMouseButtonEvent(button, false)
elseif input.UserInputType == Enum.UserInputType.Keyboard then
ImGui_ImplRoblox_UpdateKeyModifiers()
local key = KEY_MAP[input.KeyCode]
if key then io:AddKeyEvent(key, false) end
end
end)
connect(UserInputService.InputChanged, function(input, game_processed)
if input.UserInputType == Enum.UserInputType.MouseWheel then
ImGui.GetIO():AddMouseWheelEvent(0, input.Position.Z)
end
end)
bd.PendingChars = {}
-- Render rate cap trailing edge: the last frame submitted is always drawn, even if it was skipped and the app
-- then stopped calling NewFrame (e.g. it rendered one empty frame to hide its windows)
connect(game:GetService("RunService").Heartbeat, function()
local d = bd.Pending
if d and os.clock() + 0.002 >= bd.NextRender then
bd.Pending = nil
ImGui_ImplRoblox_RenderDrawData(d)
elseif bd.Stale and bd.HB > bd.StaleHB then -- deferred tiles of a frame not followed by a new one
ImGui_ImplRoblox_RenderDrawData(bd.Stale)
end
bd.HB += 1
end)
connect(UserInputService.WindowFocused, function() ImGui.GetIO():AddFocusEvent(true) end)
connect(UserInputService.WindowFocusReleased, function() ImGui.GetIO():AddFocusEvent(false) end)
end
-- Text proxy (opt-in, ImGui_ImplRoblox.SetTextProxy(true)): a hidden TextBox holds focus while an imgui text field is
-- active, so the OS gives us typing (any layout/unicode), paste (Ctrl+V) and copy/cut (Ctrl+C/X of imgui's selection).
-- Off: typing comes from KeyCodes (printable ASCII), no OS clipboard.
function ImGui_ImplRoblox_SetTextProxy(enabled)
local bd = ImGui_ImplRoblox_GetBackendData()
if not enabled then
if bd.Proxy then bd.Proxy:Destroy(); bd.Proxy = nil end
proxy_focused = false
return
end
if bd.Proxy then return end
local proxy = Instance.new("TextBox")
proxy.Name = "TextProxy"
proxy.BackgroundTransparency = 1
proxy.TextTransparency = 0 -- kept opaque but parked off screen: a transparent TextBox may not get native paste
proxy.BorderSizePixel = 0
proxy.Size = UDim2.fromOffset(200, 20)
proxy.Position = UDim2.fromOffset(-10000, -10000)
proxy.MultiLine = true
proxy.ClearTextOnFocus = false
proxy.TextEditable = true
proxy.Text = ""
proxy.Parent = bd.Gui
bd.Proxy, bd.ProxyText = proxy, ""
-- Clicking elsewhere (or Roblox taking focus) ends the edit, like clicking outside the field in imgui.
-- (Text is polled in NewFrame instead of using Text changed signals: those are deferred in Roblox.)
proxy.FocusLost:Connect(function()
if bd.ProxyReleasing then return end
-- Roblox drops TextBox focus on any click, including clicks inside imgui (e.g. moving the cursor within the
-- same field). Those must not end the edit: just recapture next frame. Only a click outside imgui ends it.
if ImGui.GetIO().WantCaptureMouse then bd.ProxyLostId = nil; return end
bd.ProxyLostId = bd.ProxyActiveId -- don't recapture for the same field
local g = ImGui.GetCurrentContext()
if g and g.ActiveId ~= 0 and g.ActiveId == bd.ProxyActiveId then ImGui.ClearActiveID() end
end)
end
function ImGui_ImplRoblox_Shutdown()
local io = ImGui.GetIO()
local bd = ImGui_ImplRoblox_GetBackendData()
for _, c in ipairs(bd.Connections) do c:Disconnect() end
DestroyTiles(bd)
bd.Gui:Destroy()
io.BackendPlatformUserData = nil
io.BackendPlatformName = nil
io.BackendRendererName = nil
io.BackendFlags = bit32.band(io.BackendFlags, bit32.bnot(bit32.bor(
16 --[[ImGuiBackendFlags.RendererHasTextures]],
8 --[[ImGuiBackendFlags.RendererHasVtxOffset]]
)))
end
function ImGui_ImplRoblox_NewFrame()
PB("ImGui NewFrame (backend)")
local io = ImGui.GetIO()
local bd = ImGui_ImplRoblox_GetBackendData()
if bd.Pending then bd.Pending = nil; table.clear(bd.Overlays) end -- skipped frame superseded (its draw lists get reset)
bd.Stale = nil
local screen = bd.Gui.AbsoluteSize
local w, h = floor(screen.X * bd.Scale), floor(screen.Y * bd.Scale)
if w ~= bd.ScreenW or h ~= bd.ScreenH then
bd.ScreenW, bd.ScreenH = w, h
ImGui_ImplRoblox_UpdateMonitors(w, h)
end
ImVec2_CopyV(io.DisplaySize, w, h)
local current_time = os.clock()
io.DeltaTime = (current_time > bd.Time) and (current_time - bd.Time) or (1.0 / 60.0)
bd.Time = current_time
local m = UserInputService:GetMouseLocation()
if m.X ~= bd.MouseX or m.Y ~= bd.MouseY then
bd.MouseX, bd.MouseY = m.X, m.Y
io:AddMouseSourceEvent(0 --[[ImGuiMouseSource.Mouse]])
io:AddMousePosEvent(m.X * bd.Scale, m.Y * bd.Scale)
end
-- Text proxy: focused while an imgui text field is active. proxy_focused only when Roblox really gave it focus,
-- otherwise the KeyCode fallback keeps typing working.
local proxy = bd.Proxy
local g = ImGui.GetCurrentContext()
local focused_box = proxy and UserInputService:GetFocusedTextBox()
if not proxy then
-- text proxy disabled: KeyCode typing only
elseif io.WantTextInput and g.ActiveId ~= 0 then
if g.ActiveId ~= bd.ProxyActiveId or g.ActiveIdIsJustActivated or g.IO.MouseClicked[0] then bd.ProxyActiveId = g.ActiveId; bd.ProxyLostId = nil end -- (re)clicking a field recaptures
local mouse_up = not (io.MouseDown[0] or io.MouseDown[1] or io.MouseDown[2])
if focused_box ~= proxy and mouse_up and bd.ProxyLostId ~= g.ActiveId and (focused_box == nil or not focused_box:IsDescendantOf(game)) then
bd.ProxyText = proxy.Text -- what it already holds is not new input (else re-typing it would collapse imgui's selection)
proxy:CaptureFocus()
focused_box = UserInputService:GetFocusedTextBox()
end
elseif focused_box == proxy then
bd.ProxyReleasing = true
proxy:ReleaseFocus()
bd.ProxyReleasing = false
focused_box = nil
end
proxy_focused = proxy ~= nil and focused_box == proxy
local used_proxy_text = false
if proxy_focused then
-- 1) whatever the OS put in the proxy since last frame (typing, paste, cut) replaces imgui's selection
local text = proxy.Text
if text ~= bd.ProxyText and text ~= bd.ProxyLastRaw then -- (LastRaw: don't re-send if our reset hasn't applied yet)
bd.ProxyLastRaw = text
-- InputText ignores characters while Ctrl is held (upstream, so shortcuts don't type); a paste arrives with
-- Ctrl still down, so release Ctrl around the characters in the event queue.
local ctrl = io.KeyCtrl or UserInputService:IsKeyDown(Enum.KeyCode.LeftControl) or UserInputService:IsKeyDown(Enum.KeyCode.RightControl)
if ctrl then io:AddKeyEvent(4096 --[[ImGuiMod_Ctrl]], false) end
local prev = bd.ProxyText or ""
if #text < #prev and prev:sub(1, #text) == text and text:sub(-1) ~= "\n" then
-- The TextBox deleted from its own text (Backspace/Delete; Roblox can't hold a real selection in it, so it
-- may remove just 1 char). Apply the deletion with imgui's own selection instead: one Backspace deletes
-- the whole imgui selection, or one char if none. Skipped if imgui already got the key event.
if not bd.DeleteKeySeen then io:AddKeyEvent(523 --[[ImGuiKey.Backspace]], true); io:AddKeyEvent(523 --[[ImGuiKey.Backspace]], false) end
elseif text ~= "\n" and text ~= "\r" and text ~= "\t" then -- Enter/Tab arrive as key events
local prev = bd.ProxyText or ""
if #text > 0 and (prev == "" or text:sub(1, #prev) ~= prev or #text <= #prev) then
for _, cp in utf8.codes(text) do io:AddInputCharacter(cp) end
elseif #text > #prev then
for _, cp in utf8.codes(text:sub(#prev + 1)) do io:AddInputCharacter(cp) end
end
end
if ctrl then io:AddKeyEvent(4096 --[[ImGuiMod_Ctrl]], true) end
used_proxy_text = true
bd.ProxyText = nil
end
-- 2) mirror imgui's selection, fully selected, so the native Ctrl+C / Ctrl+X copy exactly that
local st = g.InputTextState
local sel = ""
if st and st.ID ~= 0 and st.ID == g.ActiveId and st.Stb and st:HasSelection() then
local a, b = math.min(st.Stb.select_start, st.Stb.select_end), math.max(st.Stb.select_start, st.Stb.select_end)
sel = ImGui._ByteArrayToString(st.TextA.Data, a, b) -- stb positions are 1-based in this port
end
if sel ~= bd.ProxyText then
proxy.Text = sel
bd.ProxyLastRaw = nil
bd.ProxyText = sel
end
-- keep imgui's selection fully selected in the proxy, so Ctrl+C copies it and typing/Ctrl+V replaces it
local want_sel, want_cur = (#sel > 0) and 1 or -1, #sel + 1
if proxy.SelectionStart ~= want_sel then proxy.SelectionStart = want_sel end
if proxy.CursorPosition ~= want_cur then proxy.CursorPosition = want_cur end
end
bd.DeleteKeySeen = false
-- Held KeyCode chars: dropped if the proxy delivered the text, otherwise typed (proxy didn't really get the keys)
local pending = bd.PendingChars
if #pending > 0 then
if not used_proxy_text then for _, c in ipairs(pending) do io:AddInputCharacter(c) end end
table.clear(pending)
end
-- While the mouse is over imgui, make the tiles Active so Roblox stops passing clicks/wheel to the game (camera zoom etc.)
local want = io.WantCaptureMouse
if want ~= bd.TilesActive then
bd.TilesActive = want
for _, set in pairs(bd.Layers) do for kind = 1, 3 do local layer = set[kind]; if layer then for _, tile in pairs(layer.Tiles) do tile.Label.Active = want end end end end
end
PE()
end
--- Opt-in: max milliseconds per frame spent redrawing changed tiles (default 0 = no limit, every change is drawn the
--- same frame). With a budget (e.g. 1.5), tiles over it keep
--- their previous pixels and are redrawn on the following frames (each one at most a few frames late), so a big change
--- (resizing a large window, opening one, scrolling a long list) is spread over frames instead of one long frame.
function ImGui_ImplRoblox_SetRedrawBudget(ms)
ImGui_ImplRoblox_GetBackendData().RedrawBudget = (ms or 0) / 1000
end
--- Max tile redraws per second (default 0 = every frame, limited by the redraw budget instead). imgui logic still runs every frame.
function ImGui_ImplRoblox_SetRenderRate(hz)
ImGui_ImplRoblox_GetBackendData().RenderRate = hz or 0
end
--- Optional: max tile redraws per second while scrolling, resizing or moving a window (0 = off, the default)
function ImGui_ImplRoblox_SetBusyRenderRate(hz)
ImGui_ImplRoblox_GetBackendData().BusyRenderRate = hz or 0
end
--- Toggle MicroProfiler labels (debug.profilebegin) for the backend
function ImGui_ImplRoblox_SetProfiling(enabled) profiling = enabled and true or false end
local function ImGui_ImplRoblox_IsProfiling() return profiling end
function ImGui_ImplRoblox_SetRenderScale(scale)
local bd = ImGui_ImplRoblox_GetBackendData()
if scale == bd.Scale then return end
bd.Scale = scale
DestroyTiles(bd)
bd.ForceRedraw = true
end
ImGui_ImplRoblox = {
Init = ImGui_ImplRoblox_Init,
Shutdown = ImGui_ImplRoblox_Shutdown,
NewFrame = ImGui_ImplRoblox_NewFrame,
RenderDrawData = ImGui_ImplRoblox_RenderDrawData,
AddFile = ImGui_ImplRoblox_AddFile,
SetRenderScale = ImGui_ImplRoblox_SetRenderScale,
SetRenderRate = ImGui_ImplRoblox_SetRenderRate,
SetRedrawBudget = ImGui_ImplRoblox_SetRedrawBudget,
SetOverlay = ImGui_ImplRoblox_SetOverlay,
SetTextProxy = ImGui_ImplRoblox_SetTextProxy,
CreateTexture = ImGui_ImplRoblox_CreateTexture,
DestroyTexture = ImGui_ImplRoblox_DestroyTexture,
GetRenderScale = function() return ImGui_ImplRoblox_GetBackendData().Scale end,
SetBusyRenderRate = ImGui_ImplRoblox_SetBusyRenderRate,
SetProfiling = ImGui_ImplRoblox_SetProfiling,
IsProfiling = ImGui_ImplRoblox_IsProfiling,
}
return true -- [Roblox] ModuleScripts must return exactly one value
end
__FILES['imgui_impl_roblox_fonts.lua'] = function()
-- [Roblox] Embedded copies of fonts/*.ttf (Roblox can't read files). Generated: hex encoded bytes.
-- Add your own fonts the same way, or at runtime with ImGui_ImplRoblox.AddFile(path, bytes).
return {
["fonts/ProggyClean.ttf"] = [[
00010000000c0080000300404f532f3288eb7490000001480000004e636d617002122375000003a0000001526376742000000000000004fc00000002676c796612af8956000007040000928068656164
d79166d3000000cc0000003668686561084201c30000010400000024686d74788a007e8000000198000002066c6f63618c73b0d800000500000002046d61787001ae00da00000128000000206e616d65
2559bb96000099840000019e706f7374a6ac83ef00009b24000005d27072657069020112000004f40000000800010000000100003c55e9d55f0f3cf50003080000000000b767778400000000bd92a6d7
0000fe80038005000000000300020000000000000001000004c0fe40000003800000000003800001000000000000000000000000000000020001000001010090002400000000000200080040000a0000
00760008000000000000038001900005000002bc028a0000008f02bc028a000001c50032020000000000040900000000000000000000000000000000000000000000416c74730040000020ac08000000
0000050001800000038000000380038003800380038003800380038003800380038003800380038003800380038003800380038003800380038003800380038003800380038003800380038003800380
0180010000000080000000800180010001000080008000800080010000800080008000800080008000800080008000800080018000800000008000800080000000800080008000800080008000800080
0100008000800080000000800080008000800080008000000080000000000080000000800100008001000080000001000080008000800080008000800080008001000080008001000000008000800080
0080008000800100008000800000008000800080008001800080000003800080038001000080010000800080008000800080008000800000038000800380038001800100010000800080008000000080
0000008000800000038000800000038001800080008000000000018000800100000000800080008003800000000000800080008000800180000000800100018000800080008000800080000000800080
0080008000800080008000000080008000800080008001000100010001000000008000800080008000800080008000800080008000800080000000800000008000800080008000800080000000800080
00800080008001000100010001000080008000800080008000800080008000800080008000800080008000800080000000000003000000000000001c000100000000004c000300010000001c00040030
00000008000800020000007f00ff20acffff00000000008120acffff00010001dfd500010000000000000000000001060000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000810000000000000000000000000000000000000000000000000000000000000000000000000000b100018db801ff8500000000
000000c600c600c600c600c600c600c600c600c600c600c600c600c600c600c600c600c600c600c600c600c600c600c600c600c600c600c600c600c600c600c600c600c600c600f4011c019e02140288
02fc03140358039c03de041404320450046204a20516056605bc0612067406d60738077e07ec084e086c089608d00910094a09880a160a800b040b560bc80c2e0c820cea0d5e0da40dea0e500e960f28
0fb01012107410e0115211b61204126e12c4134c13ac13f6145814ae14ea1540158015a615b81612167e16c61734178e17e0185618ba18ee1936199619d41a481a9c1af01b5c1bc81c041c4c1c961cea
1d2a1d921dd21e401e8e1ee01f241f761fa61fa620102010202e208a20b220c821142174219821ee22622286230c230c238023802380239823b023d82400244a2468249024ae25062560258225f825f8
265826aa26aa26d82740279a280a286828a8290e292029b829f82a362a602a602b022b2a2b5e2bac2be62c202c342c9a2d282d5c2d782daa2de82e262ea62f262fb62ff4305e30c8313e31b4321e329e
331e338233ee345c34ce353a358635d43626367236e6377637d8383a38a63912397439ae3a2e3a9a3b063b7c3be83c3a3c963d223d863dec3e563ec63f2a3f9a4012406a40d0413641a242084240427a
42b842f0436243cc442a448a44ee455845b645e2465446b44714477a47da485448c6494000240000fe800380050000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043
0047004b004f00530057005b005f00630067006b006f00730077007b007f00830087008b008f000011353315313533153135331531353315313533153135331531353315053533152135331505353315
2135331505353315213533150535331521353315053533152135331505353315213533150535331521353315053533152135331505353315213533150535331521353315053533152135331505353315
31353315313533153135331531353315313533153135331580808080808080fc8080028080fc8080028080fc8080028080fc8080028080fc8080028080fc8080028080fc8080028080fc8080028080fc
8080028080fc8080028080fc8080028080fc8080808080808080048080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080
80808080808080808080808080808080808080808080808080808080808080800007018000000200040000030007000b000f00130017001b000001353315073533150735331507353315073533150735
33150335331501808080808080808080808080808003808080808080808080808080808080808080ff0080800006010003000280048000030007000b000f001300170000013533153335331505353315
3335331505353315333533150100808080fe80808080fe8080808004008080808080808080808080808080000018000000000380040000030007000b000f00130017001b001f00230027002b002f0033
0037003b003f00430047004b004f00530057005b005f00000135331533353315053533153335331505353315313533153135331531353315313533153135331505353315333533150535331533353315
053533153135331531353315313533153135331531353315053533153335331505353315333533150180808080fe80808080fd80808080808080fd80808080fe80808080fd80808080808080fd808080
80fe808080800380808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080800000150080ff800300040000030007000b
000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f00530000013533150535331531353315313533153135331505353315333533150535331533353315053533153135
331531353315053533153335331505353315333533150535331531353315313533153135331505353315018080ff0080808080fd80808080fe80808080ff00808080ff00808080fe80808080fd808080
8080ff0080038080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080800000000014000000000380040000030007000b000f0013
0017001b001f00230027002b002f00330037003b003f00430047004b004f0000133533152135331505353315333533153335331505353315333533153335331505353315333533150735331533353315
05353315333533153335331505353315333533153335331505353315213533158080018080fd008080808080fd808080808080fe0080808080808080fe008080808080fd808080808080fd0080018080
038080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080800000000014008000000380040000030007000b000f00130017001b001f0023
0027002b002f00330037003b003f00430047004b004f00000135331531353315053533152135331505353315213533150535331531353315213533150535331521353315333533150535331521353315
05353315213533150535331531353315313533153335331501008080fe8080010080fe0080010080fe808080010080fd00800100808080fd0080018080fd8080018080fe008080808080038080808080
80808080808080808080808080808080808080808080808080808080808080808080808080808080808080000003018003000200048000030007000b0000013533150735331507353315018080808080
800400808080808080808000000b0100ff000280048000030007000b000f00130017001b001f00230027002b0000013533150535331507353315053533150735331507353315073533150735331d0135
33150735331d01353315020080ff00808080ff00808080808080808080808080800400808080808080808080808080808080808080808080808080808080808080808000000b0100ff00028004800003
0007000b000f00130017001b001f00230027002b00000135331d013533150735331d01353315073533150735331507353315073533150535331507353315053533150100808080808080808080808080
80ff00808080ff00800400808080808080808080808080808080808080808080808080808080808080808000000b008000800300030000030007000b000f00130017001b001f00230027002b00000135
331505353315333533153335331505353315313533153135331505353315333533153335331505353315018080fe808080808080fe00808080fe008080808080fe808002808080808080808080808080
808080808080808080808080808080000009008000800300030000030007000b000f00130017001b001f0023000001353315073533150535331531353315313533153135331531353315053533150735
33150180808080fe808080808080fe808080800280808080808080808080808080808080808080808080800000040080ff000180010000030007000b000f000025353315073533150735331505353315
01008080808080ff008080808080808080808080808000000005008001800300020000030007000b000f0013000013353315313533153135331531353315313533158080808080800180808080808080
80808080000201000000018001000003000700002535331507353315010080808080808080808000000a0080ff800300048000030007000b000f00130017001b001f0023002700000135331507353315
05353315073533150535331507353315053533150735331505353315073533150280808080ff00808080ff00808080ff00808080ff008080800400808080808080808080808080808080808080808080
80808080808080800014008000000300040000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f00000135331531353315313533150535331521353315
0535331521353315053533153335331533353315053533153335331533353315053533152135331505353315213533150535331531353315313533150100808080fe0080018080fd8080018080fd8080
80808080fd808080808080fd8080018080fd8080018080fe0080808003808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080000000
000e008000000300040000030007000b000f00130017001b001f00230027002b002f00330037000001353315053533153135331505353315333533150735331507353315073533150735331505353315
31353315313533153135331531353315018080ff008080fe808080808080808080808080fe80808080808003808080808080808080808080808080808080808080808080808080808080808080808080
000f008000000300040000030007000b000f00130017001b001f00230027002b002f00330037003b00000135331531353315313533150535331521353315073533150535331505353315053533150535
331507353315313533153135331531353315313533150100808080fe00800180808080ff0080ff0080ff0080ff0080808080808080038080808080808080808080808080808080808080808080808080
808080808080808080808080000f008000000300040000030007000b000f00130017001b001f00230027002b002f00330037003b00000135331531353315313533150535331521353315073533150535
33153135331d013533150735331505353315213533150535331531353315313533150100808080fe00800180808080fe808080808080fd8080018080fe00808080038080808080808080808080808080
8080808080808080808080808080808080808080808080800011008000000380040000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043000001353315053533153135
33150535331533353315053533152135331505353315213533150535331531353315313533153135331531353315313533150535331507353315028080ff008080fe80808080fe0080010080fd808001
8080fd80808080808080ff008080800380808080808080808080808080808080808080808080808080808080808080808080808080808080808000000012008000000300040000030007000b000f0013
0017001b001f00230027002b002f00330037003b003f004300470000133533153135331531353315313533153135331505353315073533150735331531353315313533153135331d0135331507353315
0535331521353315053533153135331531353315808080808080fd808080808080808080808080fd8080018080fe00808080038080808080808080808080808080808080808080808080808080808080
808080808080808080808080808080000011008000000300040000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430000013533153135331505353315053533150735
331531353315313533153135331505353315213533150535331521353315053533152135331505353315313533153135331501808080fe8080ff00808080808080fe0080018080fd8080018080fd8080
018080fe008080800380808080808080808080808080808080808080808080808080808080808080808080808080808080808000000c008000000300040000030007000b000f00130017001b001f0023
0027002b002f00001335331531353315313533153135331531353315073533150535331507353315053533150735331505353315073533158080808080808080ff00808080ff00808080ff0080808003
80808080808080808080808080808080808080808080808080808080808080800013008000000300040000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b
0000013533153135331531353315053533152135331505353315213533150535331531353315313533150535331521353315053533152135331505353315213533150535331531353315313533150100
808080fe0080018080fd8080018080fe00808080fe0080018080fd8080018080fd8080018080fe0080808003808080808080808080808080808080808080808080808080808080808080808080808080
8080808080808080808000000011008000000300040000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043000001353315313533153135331505353315213533150535
331521353315053533152135331505353315313533153135331531353315073533150535331505353315313533150100808080fe0080018080fd8080018080fd8080018080fe00808080808080ff0080
fe80808003808080808080808080808080808080808080808080808080808080808080808080808080808080808080000004018000000200030000030007000b000f0000013533150735331503353315
0735331501808080808080808002808080808080fe8080808080800000060080ff000180030000030007000b000f00130017000001353315073533150335331507353315073533150535331501008080
80808080808080ff008002808080808080fe8080808080808080808080800000000a000000800300030000030007000b000f00130017001b001f00230027000001353315313533150535331531353315
053533153135331d013533153135331d013533153135331502008080fe008080fe0080808080808002808080808080808080808080808080808080808080808080800000000c00800100038002800003
0007000b000f00130017001b001f00230027002b002f000013353315313533153135331531353315313533153135331501353315313533153135331531353315313533153135331580808080808080fd
008080808080800200808080808080808080808080ff0080808080808080808080808000000a008000800380030000030007000b000f00130017001b001f002300270000133533153135331d01353315
3135331d01353315313533150535331531353315053533153135331580808080808080fe008080fe0080800280808080808080808080808080808080808080808080808080000000000a008000000300
040000030007000b000f00130017001b001f002300270000013533153135331531353315053533152135331507353315053533150535331507353315033533150100808080fe00800180808080ff0080
ff00808080808003808080808080808080808080808080808080808080808080ff008080001a000000000380040000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043
0047004b004f00530057005b005f006300670000013533153135331531353315053533152135331505353315213533153135331533353315053533153335331533353315333533150535331533353315
3335331533353315053533152135331531353315313533150535331d013533153135331531353315313533150100808080fe0080018080fd0080010080808080fc8080808080808080fc808080808080
8080fc80800100808080fd808080808080038080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080800000
0012008000000380040000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043004700000135331531353315053533153135331505353315213533150535331521353315
0535331531353315313533153135331505353315213533150535331521353315053533152135331501808080ff008080fe8080010080fe0080010080fe0080808080fd8080020080fd0080020080fd00
800200800380808080808080808080808080808080808080808080808080808080808080808080808080808080808080800000000018008000000380040000030007000b000f00130017001b001f0023
0027002b002f00330037003b003f00430047004b004f00530057005b005f0000133533153135331531353315313533150535331521353315053533152135331505353315313533153135331531353315
3135331505353315213533150535331521353315053533152135331505353315313533153135331531353315313533158080808080fe0080018080fd8080018080fd808080808080fd8080020080fd00
80020080fd0080020080fd0080808080800380808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080800000000e0080
00000380040000030007000b000f00130017001b001f00230027002b002f00330037000001353315313533153135331505353315213533150535331507353315073533150735331d0135331521353315
0535331531353315313533150180808080fe0080018080fd008080808080808080018080fe00808080038080808080808080808080808080808080808080808080808080808080808080808080800000
0014008000000380040000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f000013353315313533153135331531353315053533152135331505353315
213533150535331521353315053533152135331505353315213533150535331521353315053533153135331531353315313533158080808080fe0080018080fd8080020080fd0080020080fd00800200
80fd0080020080fd0080018080fd808080808003808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080001300800000030004000003
0007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b0000133533153135331531353315313533153135331505353315073533150735331531353315313533153135
33150535331507353315073533150735331531353315313533153135331531353315808080808080fd808080808080808080fe0080808080808080808080800380808080808080808080808080808080
8080808080808080808080808080808080808080808080808080808080800000000f008000000300040000030007000b000f00130017001b001f00230027002b002f00330037003b0000133533153135
331531353315313533153135331505353315073533150735331531353315313533153135331505353315073533150735331507353315808080808080fd808080808080808080fe008080808080808003
808080808080808080808080808080808080808080808080808080808080808080808080808000000012008000000380040000030007000b000f00130017001b001f00230027002b002f00330037003b
003f004300470000013533153135331531353315053533152135331505353315073533150735331521353315313533153135331505353315213533150535331521353315053533153135331531353315
0180808080fe0080018080fd0080808080800100808080fd0080020080fd8080018080fe0080808003808080808080808080808080808080808080808080808080808080808080808080808080808080
80808080800000000014008000000380040000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f00001335331521353315053533152135331505353315
2135331505353315313533153135331531353315313533153135331505353315213533150535331521353315053533152135331505353315213533158080020080fd0080020080fd0080020080fd0080
8080808080fd0080020080fd0080020080fd0080020080fd0080020080038080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080800000
000c010000000280040000030007000b000f00130017001b001f00230027002b002f00000135331531353315313533150535331507353315073533150735331507353315073533150535331531353315
313533150100808080ff008080808080808080808080ff00808080038080808080808080808080808080808080808080808080808080808080808080000c008000000280040000030007000b000f0013
0017001b001f00230027002b002f000001353315313533153135331507353315073533150735331507353315073533150735331505353315313533153135331501008080808080808080808080808080
80fe00808080038080808080808080808080808080808080808080808080808080808080808080000011008000000380040000030007000b000f00130017001b001f00230027002b002f00330037003b
003f0043000013353315213533150535331521353315053533152135331505353315333533150535331531353315313533150535331521353315053533152135331505353315213533158080020080fd
0080018080fd8080010080fe00808080fe80808080fe8080010080fe0080018080fd80800200800380808080808080808080808080808080808080808080808080808080808080808080808080808080
80800000000c008000000300040000030007000b000f00130017001b001f00230027002b002f000013353315073533150735331507353315073533150735331507353315073533153135331531353315
31353315313533158080808080808080808080808080808080808080038080808080808080808080808080808080808080808080808080808080808080000000001a000000000380040000030007000b
000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f00530057005b005f0063006700001135331531353315213533153135331505353315313533152135331531353315
053533153335331533353315333533150535331533353315333533153335331505353315213533152135331505353315213533152135331505353315213533150535331521353315808001808080fc80
808001808080fc8080808080808080fc8080808080808080fc8080010080010080fc8080010080010080fc8080028080fc80800280800380808080808080808080808080808080808080808080808080
8080808080808080808080808080808080808080808080808080808080808080808080000018008000000380040000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043
0047004b004f00530057005b005f000013353315313533152135331505353315313533152135331505353315333533152135331505353315333533152135331505353315213533153335331505353315
2135331533353315053533152135331531353315053533152135331531353315808080018080fd008080018080fd00808080010080fd00808080010080fd00800100808080fd00800100808080fd0080
01808080fd008001808080038080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080001000800000038004000003
0007000b000f00130017001b001f00230027002b002f00330037003b003f0000013533153135331505353315213533150535331521353315053533152135331505353315213533150535331521353315
0535331521353315053533153135331501808080fe8080010080fd8080020080fd0080020080fd0080020080fd0080020080fd8080010080fe8080800380808080808080808080808080808080808080
8080808080808080808080808080808080808080800000000011008000000300040000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043000013353315313533153135
331531353315053533152135331505353315213533150535331521353315053533153135331531353315313533150535331507353315073533158080808080fe0080018080fd8080018080fd80800180
80fd8080808080fe0080808080800380808080808080808080808080808080808080808080808080808080808080808080808080808080808000000000120080ff800380040000030007000b000f0013
0017001b001f00230027002b002f00330037003b003f00430047000001353315313533150535331521353315053533152135331505353315213533150535331521353315053533152135331505353315
213533150535331531353315333533150735331501808080fe8080010080fd8080020080fd0080020080fd0080020080fd0080020080fd8080010080fe80808080808080038080808080808080808080
8080808080808080808080808080808080808080808080808080808080808080808000000014008000000380040000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043
0047004b004f0000133533153135331531353315313533150535331521353315053533152135331505353315213533150535331531353315313533153135331505353315213533150535331521353315
05353315213533158080808080fe0080018080fd8080018080fd8080018080fd8080808080fe0080010080fe0080018080fd808002008003808080808080808080808080808080808080808080808080
8080808080808080808080808080808080808080808080800012008000000380040000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043004700000135331531353315
313533153135331505353315213533150535331d013533153135331d013533153135331d01353315053533152135331505353315313533153135331531353315010080808080fd8080020080fd008080
80808080fd0080020080fd8080808080038080808080808080808080808080808080808080808080808080808080808080808080808080808080808080000000000e000000000380040000030007000b
000f00130017001b001f00230027002b002f0033003700001135331531353315313533153135331531353315313533153135331505353315073533150735331507353315073533150735331507353315
80808080808080fe008080808080808080808080808003808080808080808080808080808080808080808080808080808080808080808080808080000012008000000380040000030007000b000f0013
0017001b001f00230027002b002f00330037003b003f00430047000013353315213533150535331521353315053533152135331505353315213533150535331521353315053533152135331505353315
21353315053533153135331531353315313533158080020080fd0080020080fd0080020080fd0080020080fd0080020080fd0080020080fd0080020080fd808080808003808080808080808080808080
8080808080808080808080808080808080808080808080808080808080808080000e000000000380040000030007000b000f00130017001b001f00230027002b002f0033003700001135331521353315
05353315213533150535331521353315053533152135331505353315333533150535331533353315053533150735331580028080fc8080028080fd0080018080fd8080018080fe00808080fe80808080
ff008080800380808080808080808080808080808080808080808080808080808080808080808080808000000018000000000380040000030007000b000f00130017001b001f00230027002b002f0033
0037003b003f00430047004b004f00530057005b005f00001135331521353315053533152135331521353315053533152135331521353315053533153335331533353315333533150535331533353315
3335331533353315053533153135331533353315313533150535331521353315053533152135331580028080fc8080010080010080fc8080010080010080fc8080808080808080fc8080808080808080
fd008080808080fd8080018080fd808001808003808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808000100080
00000380040000030007000b000f00130017001b001f00230027002b002f00330037003b003f000013353315213533150535331521353315053533152135331505353315313533150535331531353315
0535331521353315053533152135331505353315213533158080020080fd0080020080fd8080010080fe808080ff008080fe8080010080fd8080020080fd008002008003808080808080808080808080
80808080808080808080808080808080808080808080808080808080000c000000000380040000030007000b000f00130017001b001f00230027002b002f000011353315213533150535331521353315
053533152135331505353315333533150535331507353315073533150735331580028080fc8080028080fd0080018080fe00808080ff0080808080808080038080808080808080808080808080808080
808080808080808080808080808080000012008000000380040000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047000013353315313533153135331531353315
3135331531353315073533150535331505353315053533150535331505353315073533153135331531353315313533153135331531353315808080808080808080ff0080ff0080ff0080ff0080ff0080
80808080808080038080808080808080808080808080808080808080808080808080808080808080808080808080808080808080000f0100ff000280048000030007000b000f00130017001b001f0023
0027002b002f00330037003b00000135331531353315313533150535331507353315073533150735331507353315073533150735331507353315073533150735331531353315313533150100808080fe
80808080808080808080808080808080808080808080040080808080808080808080808080808080808080808080808080808080808080808080808080808080000a0080ff800300048000030007000b
000f00130017001b001f002300270000133533150735331d013533150735331d013533150735331d013533150735331d0135331507353315808080808080808080808080808080800400808080808080
808080808080808080808080808080808080808080808000000f0100ff000280048000030007000b000f00130017001b001f00230027002b002f00330037003b00000135331531353315313533150735
331507353315073533150735331507353315073533150735331507353315073533150535331531353315313533150100808080808080808080808080808080808080808080fe80808080040080808080
808080808080808080808080808080808080808080808080808080808080808080808080000a008001800300048000030007000b000f00130017001b001f002300270000013533150735331505353315
333533150535331533353315053533152135331505353315213533150180808080ff00808080fe80808080fe0080018080fd808001808004008080808080808080808080808080808080808080808080
8080000000070000ff800380000000030007000b000f00130017001b00001535331531353315313533153135331531353315313533153135331580808080808080808080808080808080808080808080
000201000380020004800003000700000135331d013533150100808004008080808080000010008000000300030000030007000b000f00130017001b001f00230027002b002f00330037003b003f0000
01353315313533153135331d01353315053533153135331531353315313533150535331521353315053533152135331505353315313533153135331531353315010080808080fe0080808080fd808001
8080fd8080018080fe00808080800280808080808080808080808080808080808080808080808080808080808080808080808080800000000013008000000300048000030007000b000f00130017001b
001f00230027002b002f00330037003b003f00430047004b0000133533150735331507353315073533153135331531353315313533150535331521353315053533152135331505353315213533150535
331521353315053533153135331531353315313533158080808080808080808080fe0080018080fd8080018080fd8080018080fd8080018080fd80808080800400808080808080808080808080808080
8080808080808080808080808080808080808080808080808080808080808000000c008000000300030000030007000b000f00130017001b001f00230027002b002f0000013533153135331531353315
0535331521353315053533150735331507353315213533150535331531353315313533150100808080fe0080018080fd808080808080018080fe00808080028080808080808080808080808080808080
808080808080808080808080800000000013008000000300048000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b00000135331507353315073533150535
331531353315313533153135331505353315213533150535331521353315053533152135331505353315213533150535331531353315313533153135331502808080808080fe0080808080fd80800180
80fd8080018080fd8080018080fd8080018080fe008080808004008080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808000000000100080
00000300030000030007000b000f00130017001b001f00230027002b002f00330037003b003f000001353315313533153135331505353315213533150535331531353315313533153135331531353315
0535331507353315213533150535331531353315313533150100808080fe0080018080fd808080808080fd80808080018080fe0080808002808080808080808080808080808080808080808080808080
80808080808080808080808080800000000e008000000300048000030007000b000f00130017001b001f00230027002b002f003300370000013533153135331531353315053533150735331505353315
31353315313533153135331505353315073533150735331507353315073533150180808080fe00808080ff0080808080fe80808080808080808080040080808080808080808080808080808080808080
808080808080808080808080808080808000000000150080fe800300030000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f00530000013533153135
3315313533153135331505353315213533150535331521353315053533152135331505353315213533150535331531353315313533153135331507353315073533150535331531353315313533150100
80808080fd8080018080fd8080018080fd8080018080fd8080018080fe008080808080808080fe0080808002808080808080808080808080808080808080808080808080808080808080808080808080
808080808080808080808080808080000011008000000300048000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430000133533150735331507353315073533153135
33153135331531353315053533152135331505353315213533150535331521353315053533152135331505353315213533158080808080808080808080fe0080018080fd8080018080fd8080018080fd
8080018080fd80800180800400808080808080808080808080808080808080808080808080808080808080808080808080808080808080000008010000000200048000030007000b000f00130017001b
001f00000135331501353315313533150735331507353315073533150735331507353315018080ff0080808080808080808080808004008080fe80808080808080808080808080808080808080800000
000c0080ff000280048000030007000b000f00130017001b001f00230027002b002f00000135331501353315313533150735331507353315073533150735331507353315073533150535331531353315
31353315020080ff008080808080808080808080808080fe0080808004008080fe808080808080808080808080808080808080808080808080808080808080000010008000000300048000030007000b
000f00130017001b001f00230027002b002f00330037003b003f000013353315073533150735331507353315213533150535331521353315053533153335331505353315313533153135331505353315
2135331505353315213533158080808080808080018080fd8080010080fe00808080fe80808080fe8080010080fe00800180800400808080808080808080808080808080808080808080808080808080
80808080808080808080808080000000000a010000000200048000030007000b000f00130017001b001f0023002700000135331531353315073533150735331507353315073533150735331507353315
0735331507353315010080808080808080808080808080808080808004008080808080808080808080808080808080808080808080808080808000000014000000000380030000030007000b000f0013
0017001b001f00230027002b002f00330037003b003f00430047004b004f0000113533153135331531353315333533153135331505353315213533152135331505353315213533152135331505353315
2135331521353315053533152135331521353315053533152135331521353315808080808080fd0080010080010080fc8080010080010080fc8080010080010080fc8080010080010080fc8080010080
01008002808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080800000000e008000000300030000030007000b000f00130017001b001f0023
0027002b002f00330037000013353315313533153135331531353315053533152135331505353315213533150535331521353315053533152135331505353315213533158080808080fe0080018080fd
8080018080fd8080018080fd8080018080fd808001808002808080808080808080808080808080808080808080808080808080808080808080800000000e008000000300030000030007000b000f0013
0017001b001f00230027002b002f003300370000013533153135331531353315053533152135331505353315213533150535331521353315053533152135331505353315313533153135331501008080
80fe0080018080fd8080018080fd8080018080fd8080018080fe00808080028080808080808080808080808080808080808080808080808080808080808080808000000000130080fe80030003000003
0007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b0000133533153135331531353315313533150535331521353315053533152135331505353315213533150535
331521353315053533153135331531353315313533150535331507353315073533158080808080fe0080018080fd8080018080fd8080018080fd8080018080fd8080808080fe00808080808002808080
808080808080808080808080808080808080808080808080808080808080808080808080808080808080808000130080fe800300030000030007000b000f00130017001b001f00230027002b002f0033
0037003b003f00430047004b0000013533153135331531353315313533150535331521353315053533152135331505353315213533150535331521353315053533153135331531353315313533150735
33150735331507353315010080808080fd8080018080fd8080018080fd8080018080fd8080018080fe008080808080808080808002808080808080808080808080808080808080808080808080808080
8080808080808080808080808080808080808080000a008000000300030000030007000b000f00130017001b001f00230027000013353315333533153135331505353315313533152135331505353315
0735331507353315073533158080808080fe008080010080fd8080808080808080028080808080808080808080808080808080808080808080808080000d008000000300030000030007000b000f0013
0017001b001f00230027002b002f00330000013533153135331531353315313533150535331d013533153135331d0135331d0135331505353315313533153135331531353315010080808080fd808080
808080fd80808080800280808080808080808080808080808080808080808080808080808080808080800000000d010000000300040000030007000b000f00130017001b001f00230027002b002f0033
00000135331507353315073533153135331531353315313533150535331507353315073533150735331d01353315313533153135331501008080808080808080fe008080808080808080808003808080
8080808080808080808080808080808080808080808080808080808080808000000e008000000300030000030007000b000f00130017001b001f00230027002b002f0033003700001335331521353315
0535331521353315053533152135331505353315213533150535331521353315053533153135331531353315313533158080018080fd8080018080fd8080018080fd8080018080fd8080018080fe0080
80808002808080808080808080808080808080808080808080808080808080808080808080800000000a008000000300030000030007000b000f00130017001b001f0023002700001335331521353315
05353315213533150535331533353315053533153335331505353315073533158080018080fd8080018080fe00808080fe80808080ff0080808002808080808080808080808080808080808080808080
80808080800000000012000000000380030000030007000b000f00130017001b001f00230027002b002f00330037003b003f004300470000113533152135331505353315213533152135331505353315
21353315213533150535331533353315333533153335331505353315313533153335331531353315053533152135331580028080fc8080010080010080fc8080010080010080fc8080808080808080fd
008080808080fd80800180800280808080808080808080808080808080808080808080808080808080808080808080808080808080808000000a008000000300030000030007000b000f00130017001b
001f002300270000133533152135331505353315333533150535331507353315053533153335331505353315213533158080018080fe00808080ff00808080ff00808080fe0080018080028080808080
80808080808080808080808080808080808080808000000000130080fe800300030000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b0000133533152135
331505353315213533150535331521353315053533152135331505353315213533150535331531353315313533153135331507353315073533150535331531353315313533158080018080fd80800180
80fd8080018080fd8080018080fd8080018080fe008080808080808080fe0080808002808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080
80800000000e008000000300030000030007000b000f00130017001b001f00230027002b002f003300370000133533153135331531353315313533153135331507353315053533150535331505353315
05353315313533153135331531353315313533158080808080808080ff0080ff0080ff0080ff008080808080028080808080808080808080808080808080808080808080808080808080808080808000
000e0080ff000300048000030007000b000f00130017001b001f00230027002b002f003300370000013533153135331505353315073533150735331507353315053533153135331d0135331507353315
073533150735331d013533153135331502008080fe8080808080808080fe8080808080808080808080800400808080808080808080808080808080808080808080808080808080808080808080808080
80800000000b0180ff000200048000030007000b000f00130017001b001f00230027002b0000013533150735331507353315073533150735331507353315073533150735331507353315073533150735
331501808080808080808080808080808080808080808080800400808080808080808080808080808080808080808080808080808080808080808000000e0080ff000300048000030007000b000f0013
0017001b001f00230027002b002f003300370000133533153135331d0135331507353315073533150735331d013533153135331505353315073533150735331507353315053533153135331580808080
8080808080808080fe8080808080808080fe808080040080808080808080808080808080808080808080808080808080808080808080808080808080800000000008000001800380028000030007000b
000f00130017001b001f0000133533153135331531353315213533150535331521353315313533153135331580808080010080fc80800100808080020080808080808080808080808080808080800000
0013008000000380038000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b0000013533153135331531353315053533152135331505353315313533153135
331531353315053533150535331531353315313533153135331505353315213533150535331531353315313533150180808080fe0080018080fd0080808080fe8080ff0080808080fe8080018080fe00
8080800300808080808080808080808080808080808080808080808080808080808080808080808080808080808080808000000000040100ff000200010000030007000b000f00002535331507353315
073533150535331501808080808080ff0080808080808080808080808080000000100080ff000300048000030007000b000f00130017001b001f00230027002b002f00330037003b003f000001353315
31353315053533150735331505353315313533153135331531353315053533150735331507353315073533150735331507353315053533153135331502008080fe80808080ff0080808080fe80808080
8080808080808080fe808080040080808080808080808080808080808080808080808080808080808080808080808080808080808080808000060100ff800280010000030007000b000f001300170000
2535331533353315053533153335331505353315333533150100808080fe80808080fe8080808080808080808080808080808080808000000003008000000300008000030007000b0000333533153335
3315333533158080808080808080808080800000000d008000000300048000030007000b000f00130017001b001f00230027002b002f0033000001353315073533150535331531353315313533153135
3315313533150535331507353315073533150735331507353315073533150180808080fe808080808080fe80808080808080808080808004008080808080808080808080808080808080808080808080
8080808080808080808080000011008000000300048000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043000001353315073533150535331531353315313533153135
331531353315053533150535331531353315313533153135331531353315053533150735331507353315073533150180808080fe808080808080fe8080fe808080808080fe8080808080808080040080
80808080808080808080808080808080808080808080808080808080808080808080808080808080800000000005008003000300048000030007000b000f001300000135331505353315333533150535
331521353315018080ff00808080fe008001808004008080808080808080808080800000000e008000000380040000030007000b000f00130017001b001f00230027002b002f00330037000001353315
05353315213533150535331533353315073533150535331507353315333533153335331505353315213533153335331505353315020080fe0080010080fe008080808080ff0080808080808080fd0080
0100808080fd0080038080808080808080808080808080808080808080808080808080808080808080808080800000000015008000000380050000030007000b000f00130017001b001f00230027002b
002f00330037003b003f00430047004b004f005300000135331533353315053533150535331531353315313533153135331505353315213533150535331d013533153135331d013533153135331d0135
33150535331521353315053533153135331531353315313533150100808080ff0080ff0080808080fd8080020080fd00808080808080fd0080020080fd80808080800480808080808080808080808080
808080808080808080808080808080808080808080808080808080808080808080808080808080000005008000800200030000030007000b000f0013000001353315053533150535331d0135331d0135
3315018080ff0080ff00808080028080808080808080808080808080800000000018000000000380040000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b
004f00530057005b005f00001335331531353315333533153135331531353315053533152135331505353315213533150535331521353315313533153135331505353315213533150535331521353315
0535331521353315053533153135331533353315313533153135331580808080808080fc8080010080fe0080010080fe00800100808080fd0080010080fe0080010080fe0080010080fe808080808080
8003808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808000000015008000000380050000030007000b000f0013
0017001b001f00230027002b002f00330037003b003f00430047004b004f0053000001353315333533150535331505353315313533153135331531353315313533153135331507353315053533150535
33150535331505353315053533150735331531353315313533153135331531353315313533150100808080ff0080fe808080808080808080ff0080ff0080ff0080ff0080ff0080808080808080800480
808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080000003018003000280048000030007000b0000013533150735331d0135
33150180808080800400808080808080808000000003010003000200048000030007000b00000135331507353315053533150180808080ff008004008080808080808080000601000300030004800003
0007000b000f0013001700000135331533353315053533153335331505353315333533150100808080fe80808080ff008080800400808080808080808080808080808000000600800300028004800003
0007000b000f0013001700000135331533353315053533153335331505353315333533150100808080fe80808080fe008080800400808080808080808080808080808000000d00800080030003000003
0007000b000f00130017001b001f00230027002b002f00330000013533150535331531353315313533150535331531353315313533153135331531353315053533153135331531353315053533150180
80ff00808080fe008080808080fe00808080ff008002808080808080808080808080808080808080808080808080808080808080800000000005008001800300020000030007000b000f001300001335
3315313533153135331531353315313533158080808080800180808080808080808080800007000001800380020000030007000b000f00130017001b0000113533153135331531353315313533153135
3315313533153135331580808080808080018080808080808080808080808080800000000004008003000280040000030007000b000f0000013533153335331505353315333533150100808080fe0080
808003808080808080808080800000000010000002000380040000030007000b000f00130017001b001f00230027002b002f00330037003b003f00001135331531353315313533153335331531353315
313533150535331521353315313533153135331505353315213533153335331505353315213533153335331580808080808080fd00800100808080fd00800100808080fd008001008080800380808080
80808080808080808080808080808080808080808080808080808080808080800010008000000300048000030007000b000f00130017001b001f00230027002b002f00330037003b003f000001353315
3335331505353315013533153135331531353315313533150535331d013533153135331d0135331d01353315053533153135331531353315313533150100808080ff0080ff0080808080fd8080808080
80fd8080808080040080808080808080ff00808080808080808080808080808080808080808080808080808080808080800000000005008000800200030000030007000b000f001300001335331d0135
331d01353315053533150535331580808080ff0080ff0080028080808080808080808080808080800015000000000380030000030007000b000f00130017001b001f00230027002b002f00330037003b
003f00430047004b004f00530000133533153135331533353315313533150535331521353315213533150535331521353315313533153135331531353315053533152135331505353315213533152135
331505353315313533153335331531353315808080808080fd0080010080010080fc8080010080808080fc8080010080fe0080010080010080fd00808080808002808080808080808080808080808080
8080808080808080808080808080808080808080808080808080808080808080800000000011008000000300048000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043
000001353315333533150535331501353315313533153135331531353315313533150735331505353315053533150535331505353315313533153135331531353315313533150100808080ff0080fe80
80808080808080ff0080ff0080ff0080ff008080808080040080808080808080ff0080808080808080808080808080808080808080808080808080808080808080808000000d00000000038004800003
0007000b000f00130017001b001f00230027002b002f00330000013533153335331501353315213533150535331521353315053533152135331505353315333533150535331507353315073533150100
808080fd8080028080fc8080028080fd0080018080fe00808080ff008080808080040080808080ff00808080808080808080808080808080808080808080808080808080800000000007018000000200
040000030007000b000f00130017001b00000135331503353315073533150735331507353315073533150735331501808080808080808080808080808003808080ff0080808080808080808080808080
8080808000120080ff800300038000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047000001353315053533153135331531353315053533153335331533353315
0535331533353315053533153335331505353315333533153335331505353315313533153135331505353315018080ff00808080fe008080808080fd80808080fe80808080fe808080808080fe008080
80ff00800300808080808080808080808080808080808080808080808080808080808080808080808080808080808080800000000010008000000300040000030007000b000f00130017001b001f0023
0027002b002f00330037003b003f000001353315313533150535331507353315053533153135331531353315313533150535331507353315053533150735331531353315313533153135331531353315
01808080fe80808080ff0080808080fe80808080ff0080808080808080038080808080808080808080808080808080808080808080808080808080808080808080808080808000000014000000000380
038000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f0000113533152135331505353315313533153135331531353315313533150535331521353315
053533152135331505353315213533150535331531353315313533153135331531353315053533152135331580028080fd008080808080fd8080018080fd8080018080fd8080018080fd808080808080
fd008002808003008080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808000000010000000000380040000030007000b000f00130017001b
001f00230027002b002f00330037003b003f0000113533152135331505353315213533150535331521353315053533153335331505353315053533153135331531353315313533153135331505353315
0735331580028080fc8080028080fd0080018080fe00808080ff0080fe808080808080fe8080808003808080808080808080808080808080808080808080808080808080808080808080808080808080
80000000000a0180ff000200048000030007000b000f00130017001b001f0023002700000135331507353315073533150735331507353315033533150735331507353315073533150735331501808080
808080808080808080808080808080808004008080808080808080808080808080ff0080808080808080808080808080800000000012008000000300048000030007000b000f00130017001b001f0023
0027002b002f00330037003b003f00430047000001353315313533153135331505353315213533150535331d01353315313533150535331533353315053533153135331d013533150535331521353315
0535331531353315313533150100808080fe0080018080fd80808080ff00808080ff00808080fd8080018080fe0080808004008080808080808080808080808080808080808080808080808080808080
80808080808080808080808080808000000201000400028004800003000700000135331533353315010080808004008080808000001c000000000380040000030007000b000f00130017001b001f0023
0027002b002f00330037003b003f00430047004b004f00530057005b005f00630067006b006f000013353315313533153135331531353315313533150535331521353315053533152135331531353315
333533150535331533353315213533150535331533353315213533150535331521353315313533153335331505353315213533150535331531353315313533153135331531353315808080808080fd00
80028080fc8080010080808080fc80808080018080fc80808080018080fc8080010080808080fc8080028080fd0080808080800380808080808080808080808080808080808080808080808080808080
808080808080808080808080808080808080808080808080808080808080808080808080000b008001800280040000030007000b000f00130017001b001f00230027002b0000013533153135331d0135
331505353315313533153135331505353315213533150535331531353315313533150100808080fe80808080fe0080010080fe8080808003808080808080808080808080808080808080808080808080
80808000000a008000800300030000030007000b000f00130017001b001f0023002700000135331533353315053533153335331505353315333533150535331533353315053533153335331501808080
80fe00808080fe00808080ff00808080ff008080800280808080808080808080808080808080808080808080808080000007008000000280020000030007000b000f00130017001b0000133533153135
33153135331531353315073533150735331507353315808080808080808080808001808080808080808080808080808080808080001e000000000380040000030007000b000f00130017001b001f0023
0027002b002f00330037003b003f00430047004b004f00530057005b005f00630067006b006f007300770000133533153135331531353315313533153135331505353315213533150535331533353315
3135331521353315053533153335331533353315333533150535331533353315313533152135331505353315333533153335331533353315053533152135331505353315313533153135331531353315
31353315808080808080fd0080028080fc8080808080010080fc8080808080808080fc8080808080010080fc8080808080808080fc8080028080fd008080808080038080808080808080808080808080
80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808000000007000004800380050000030007000b000f00130017001b
00001135331531353315313533153135331531353315313533153135331580808080808080048080808080808080808080808080800000000008008002800280048000030007000b000f00130017001b
001f0000013533153135331505353315213533150535331521353315053533153135331501008080fe8080010080fe0080010080fe808080040080808080808080808080808080808080808080000000
000e008000000300038000030007000b000f00130017001b001f00230027002b002f00330037000001353315073533150535331531353315313533153135331531353315053533150735331501353315
313533153135331531353315313533150180808080fe808080808080fe80808080fe808080808080030080808080808080808080808080808080808080808080ff0080808080808080808080000a0080
02000280048000030007000b000f00130017001b001f00230027000013353315313533153135331d013533150535331505353315053533153135331531353315313533158080808080ff0080ff0080ff
0080808080040080808080808080808080808080808080808080808080808000000a008002000280048000030007000b000f00130017001b001f00230027000013353315313533153135331d01353315
053533153135331d013533150535331531353315313533158080808080fe80808080fe008080800400808080808080808080808080808080808080808080808080000000000201800380028004800003
000700000135331505353315020080ff00800400808080808000000000110000ff000380030000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430000133533152135
33150535331521353315053533152135331505353315213533150535331531353315213533150535331533353315313533153335331505353315053533158080018080fd8080018080fd8080018080fd
8080018080fd808080010080fd80808080808080fd0080ff008002808080808080808080808080808080808080808080808080808080808080808080808080808080808080000000001a0080ff800380
040000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f00530057005b005f006300670000013533153135331531353315313533153135331505353315
3135331531353315333533150535331531353315313533153335331505353315313533153335331505353315333533150535331533353315053533153335331505353315333533150535331533353315
01008080808080fd008080808080fd808080808080fe0080808080fe80808080fe80808080fe80808080fe80808080fe8080808003808080808080808080808080808080808080808080808080808080
8080808080808080808080808080808080808080808080808080808080808080808000000009010001000280028000030007000b000f00130017001b001f002300000135331531353315313533150535
331531353315313533150535331531353315313533150100808080fe80808080fe80808080020080808080808080808080808080808080808080800000040180fe800280000000030007000b000f0000
05353315313533150735331505353315018080808080ff008080808080808080808080800008008002000200048000030007000b000f00130017001b001f000001353315053533153135331507353315
07353315053533153135331531353315010080ff00808080808080ff00808080040080808080808080808080808080808080808080800000000a008002000280048000030007000b000f00130017001b
001f0023002700000135331531353315053533152135331505353315213533150535331521353315053533153135331501008080fe8080010080fe0080010080fe0080010080fe808080040080808080
8080808080808080808080808080808080808080000a008000800300030000030007000b000f00130017001b001f00230027000013353315333533150535331533353315053533153335331505353315
33353315053533153335331580808080ff00808080ff00808080fe00808080fe00808080028080808080808080808080808080808080808080808080808000000016008000000380050000030007000b
000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f00530057000001353315053533152135331505353315213533150535331521353315053533153335331507353315
053533152135331505353315333533153135331505353315333533153135331531353315313533150535331521353315028080fd8080018080fd8080010080fe0080010080fe008080808080ff008001
0080fe0080808080fd80808080808080fd008001808004808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080000000
0016008000000380050000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f005300570000013533150535331521353315053533152135331505353315
21353315053533153335331507353315313533153135331505353315213533150535331521353315053533152135331505353315213533153135331531353315028080fd8080018080fd8080010080fe
0080010080fe0080808080808080fe0080018080fd8080010080fd8080010080fe0080010080808004808080808080808080808080808080808080808080808080808080808080808080808080808080
80808080808080808080808080808000001a000000000380050000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f00530057005b005f006300670000
0135331505353315313533152135331505353315333533150535331531353315333533150535331531353315053533153135331533353315053533152135331505353315333533153135331505353315
333533153135331531353315313533150535331521353315028080fd008080018080fe00808080fe0080808080fe808080fe0080808080ff0080010080fe0080808080fd80808080808080fd00800180
80048080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080000a008000000300040000030007000b
000f00130017001b001f0023002700000135331503353315073533150535331505353315073533152135331505353315313533153135331501808080808080ff0080ff00808080018080fe0080808003
808080ff0080808080808080808080808080808080808080808080800012008000000380050000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043004700000135331d
013533150135331531353315053533152135331505353315213533150535331531353315313533153135331505353315213533150535331521353315053533152135331501808080ff008080fe808001
0080fe0080010080fe0080808080fd8080020080fd0080020080fd008002008004808080808080ff00808080808080808080808080808080808080808080808080808080808080808080808080808000
0012008000000380050000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043004700000135331505353315033533153135331505353315213533150535331521353315
05353315313533153135331531353315053533152135331505353315213533150535331521353315020080ff0080808080fe8080010080fe0080010080fe0080808080fd8080020080fd0080020080fd
008002008004808080808080ff0080808080808080808080808080808080808080808080808080808080808080808080808080800014008000000380050000030007000b000f00130017001b001f0023
0027002b002f00330037003b003f00430047004b004f00000135331531353315053533152135331501353315313533150535331521353315053533152135331505353315313533153135331531353315
05353315213533150535331521353315053533152135331501808080fe8080010080fe808080fe8080010080fe0080010080fe0080808080fd8080020080fd0080020080fd0080020080048080808080
8080808080ff0080808080808080808080808080808080808080808080808080808080808080808080808080800000000014008000000380050000030007000b000f00130017001b001f00230027002b
002f00330037003b003f00430047004b004f0000013533153335331505353315333533150135331531353315053533152135331505353315213533150535331531353315313533153135331505353315
21353315053533152135331505353315213533150180808080fe00808080ff008080fe8080010080fe0080010080fe0080808080fd8080020080fd0080020080fd008002008004808080808080808080
80ff0080808080808080808080808080808080808080808080808080808080808080808080808080800000000012008000000380048000030007000b000f00130017001b001f00230027002b002f0033
0037003b003f0043004700000135331521353315013533153135331505353315213533150535331521353315053533153135331531353315313533150535331521353315053533152135331505353315
21353315010080010080fe808080fe8080010080fe0080010080fe0080808080fd8080020080fd0080020080fd0080020080040080808080ff0080808080808080808080808080808080808080808080
808080808080808080808080808080800016008000000380050000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f0053005700000135331531353315
0535331521353315053533152135331505353315313533150535331521353315053533152135331505353315313533153135331531353315053533152135331505353315213533150535331521353315
01808080fe8080010080fe0080010080fe808080fe8080010080fe0080010080fe0080808080fd8080020080fd0080020080fd0080020080048080808080808080808080808080808080808080808080
80808080808080808080808080808080808080808080808080808080808080000017000000000380040000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b
004f00530057005b000001353315313533153135331531353315053533153335331505353315333533150535331521353315313533150535331531353315313533153135331505353315213533150535
33152135331505353315213533153135331531353315018080808080fd80808080fe80808080fe008001008080fd8080808080fd8080018080fd8080018080fd80800180808080038080808080808080
80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080000000110080fe800380040000030007000b000f00130017001b001f00230027002b
002f00330037003b003f0043000001353315313533153135331505353315213533150535331507353315073533150735331d013533152135331505353315313533153135331505353315073533150535
33150180808080fe0080018080fd008080808080808080018080fe00808080ff00808080ff00800380808080808080808080808080808080808080808080808080808080808080808080808080808080
80808080800000000014008000000300050000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f00000135331d01353315013533153135331531353315
31353315313533150535331507353315073533153135331531353315313533150535331507353315073533153135331531353315313533153135331501008080fe808080808080fd8080808080808080
80fe0080808080808080808004808080808080ff00808080808080808080808080808080808080808080808080808080808080808080808080808080808080000014008000000300050000030007000b
000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f00000135331505353315013533153135331531353315313533153135331505353315073533150735331531353315
313533153135331505353315073533150735331531353315313533153135331531353315020080ff0080fe808080808080fd808080808080808080fe0080808080808080808004808080808080ff0080
80808080808080808080808080808080808080808080808080808080808080808080808080808080800000000015008000000300050000030007000b000f00130017001b001f00230027002b002f0033
0037003b003f00430047004b004f005300000135331505353315333533150135331531353315313533153135331531353315053533150735331507353315313533153135331531353315053533150735
33150735331531353315313533153135331531353315018080ff00808080fe008080808080fd808080808080808080fe00808080808080808080048080808080808080ff008080808080808080808080
80808080808080808080808080808080808080808080808080808080808080000014008000000300048000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b
004f000001353315333533150135331531353315313533153135331531353315053533150735331507353315313533153135331531353315053533150735331507353315313533153135331531353315
313533150100808080fe008080808080fd808080808080808080fe00808080808080808080040080808080ff008080808080808080808080808080808080808080808080808080808080808080808080
8080808080808000000d010000000280050000030007000b000f00130017001b001f00230027002b002f003300000135331d013533150135331531353315313533150535331507353315073533150735
33150735331505353315313533153135331501808080fe80808080ff00808080808080808080ff0080808004808080808080ff0080808080808080808080808080808080808080808080808080808080
000d010000000280050000030007000b000f00130017001b001f00230027002b002f00330000013533150535331501353315313533153135331505353315073533150735331507353315073533150535
33153135331531353315020080ff0080ff00808080ff00808080808080808080ff0080808004808080808080ff00808080808080808080808080808080808080808080808080808080800000000e0100
00000280050000030007000b000f00130017001b001f00230027002b002f0033003700000135331505353315333533150135331531353315313533150535331507353315073533150735331507353315
053533153135331531353315018080ff00808080fe80808080ff00808080808080808080ff00808080048080808080808080ff0080808080808080808080808080808080808080808080808080808080
000d010000000280048000030007000b000f00130017001b001f00230027002b002f00330000013533153335331501353315313533153135331505353315073533150735331507353315073533150535
331531353315313533150100808080fe80808080ff00808080808080808080ff00808080040080808080ff00808080808080808080808080808080808080808080808080808080800015000000000380
038000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f0053000013353315313533153135331531353315053533152135331505353315213533150535
33153135331531353315313533152135331505353315213533150535331521353315053533153135331531353315313533158080808080fe0080018080fd8080020080fc8080808080010080fd008002
0080fd0080018080fd808080808003008080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080800019008000000380050000030007000b
000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f00530057005b005f0063000001353315333533150535331533353315013533153135331521353315053533153335
3315213533150535331533353315213533150535331521353315333533150535331521353315333533150535331521353315313533150535331521353315313533150180808080fe00808080fe008080
018080fd00808080010080fd00808080010080fd00800100808080fd00800100808080fd008001808080fd0080018080800480808080808080808080ff00808080808080808080808080808080808080
80808080808080808080808080808080808080808080808080808080808000000010008000000380050000030007000b000f00130017001b001f00230027002b002f00330037003b003f00000135331d
01353315013533153135331505353315213533150535331521353315053533152135331505353315213533150535331521353315053533153135331501808080ff008080fe8080010080fd8080020080
fd0080020080fd0080020080fd8080010080fe80808004808080808080ff0080808080808080808080808080808080808080808080808080808080808080808080000000001000800000038005000003
0007000b000f00130017001b001f00230027002b002f00330037003b003f0000013533150535331503353315313533150535331521353315053533152135331505353315213533150535331521353315
05353315213533150535331531353315020080ff0080808080fe8080010080fd8080020080fd0080020080fd0080020080fd8080010080fe80808004808080808080ff00808080808080808080808080
8080808080808080808080808080808080808080808000000012008000000380050000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043004700000135331531353315
0535331521353315013533153135331505353315213533150535331521353315053533152135331505353315213533150535331521353315053533153135331501808080fe8080010080fe808080fe80
80010080fd8080020080fd0080020080fd0080020080fd8080010080fe8080800480808080808080808080ff008080808080808080808080808080808080808080808080808080808080808080808000
0012008000000380050000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043004700000135331533353315053533153335331501353315313533150535331521353315
053533152135331505353315213533150535331521353315053533152135331505353315313533150180808080fe00808080ff008080fe8080010080fd8080020080fd0080020080fd0080020080fd80
80010080fe8080800480808080808080808080ff0080808080808080808080808080808080808080808080808080808080808080808080000010008000000380048000030007000b000f00130017001b
001f00230027002b002f00330037003b003f0000013533152135331501353315313533150535331521353315053533152135331505353315213533150535331521353315053533152135331505353315
31353315010080010080fe808080fe8080010080fd8080020080fd0080020080fd0080020080fd8080010080fe808080040080808080ff00808080808080808080808080808080808080808080808080
8080808080808080808000000009008000800300030000030007000b000f00130017001b001f002300001335331521353315053533153335331505353315053533153335331505353315213533158080
018080fe00808080ff0080ff00808080fe008001808002808080808080808080808080808080808080808080808000000016008000000380040000030007000b000f00130017001b001f00230027002b
002f00330037003b003f00430047004b004f0053005700000135331531353315333533150535331521353315053533152135331533353315053533152135331533353315053533153335331521353315
0535331533353315213533150535331521353315053533153335331531353315018080808080fd8080010080fd80800100808080fd00800100808080fd00808080010080fd00808080010080fd808001
0080fd808080808003808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080800000000012008000000380050000030007000b
000f00130017001b001f00230027002b002f00330037003b003f0043004700000135331d0135331501353315213533150535331521353315053533152135331505353315213533150535331521353315
05353315213533150535331531353315313533153135331501808080fe0080020080fd0080020080fd0080020080fd0080020080fd0080020080fd0080020080fd808080808004808080808080ff0080
808080808080808080808080808080808080808080808080808080808080808080808080800000000012008000000380050000030007000b000f00130017001b001f00230027002b002f00330037003b
003f004300470000013533150535331501353315213533150535331521353315053533152135331505353315213533150535331521353315053533152135331505353315313533153135331531353315
020080ff0080fe8080020080fd0080020080fd0080020080fd0080020080fd0080020080fd0080020080fd808080808004808080808080ff008080808080808080808080808080808080808080808080
808080808080808080808080808080000014008000000380050000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f0000013533153135331505353315
213533150135331521353315053533152135331505353315213533150535331521353315053533152135331505353315213533150535331531353315313533153135331501808080fe8080010080fd80
80020080fd0080020080fd0080020080fd0080020080fd0080020080fd0080020080fd80808080800480808080808080808080ff00808080808080808080808080808080808080808080808080808080
8080808080808080808080000012008000000380048000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043004700000135331521353315013533152135331505353315
21353315053533152135331505353315213533150535331521353315053533152135331505353315313533153135331531353315010080010080fd8080020080fd0080020080fd0080020080fd008002
0080fd0080020080fd0080020080fd8080808080040080808080ff0080808080808080808080808080808080808080808080808080808080808080808080808080800000000d00000000038005000003
0007000b000f00130017001b001f00230027002b002f00330000013533150535331501353315213533150535331521353315053533152135331505353315333533150535331507353315073533150200
80ff0080fe0080028080fc8080028080fd0080018080fe00808080ff00808080808004808080808080ff0080808080808080808080808080808080808080808080808080808080000010008000000300
040000030007000b000f00130017001b001f00230027002b002f00330037003b003f00001335331507353315073533153135331531353315313533150535331521353315053533152135331505353315
3135331531353315313533150535331507353315808080808080808080fe0080018080fd8080018080fd8080808080fe0080808003808080808080808080808080808080808080808080808080808080
8080808080808080808080808000000000190000ff800380048000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f00530057005b005f006300000135
3315313533153135331505353315213533150535331521353315053533153135331531353315313533150535331521353315053533152135331505353315213533150535331521353315053533153135
3315313533153135331531353315053533150100808080fe0080018080fd8080018080fd8080808080fe0080018080fd8080020080fd0080020080fd0080020080fd008080808080fd00800400808080
80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080800012008000000300048000030007000b000f00130017001b
001f00230027002b002f00330037003b003f0043004700000135331d0135331501353315313533153135331d013533150535331531353315313533153135331505353315213533150535331521353315
0535331531353315313533153135331501808080fe8080808080fe0080808080fd8080018080fd8080018080fe008080808004008080808080ff00808080808080808080808080808080808080808080
808080808080808080808080808080800012008000000300048000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047000001353315053533150135331531353315
3135331d01353315053533153135331531353315313533150535331521353315053533152135331505353315313533153135331531353315020080ff0080ff0080808080fe0080808080fd8080018080
fd8080018080fe008080808004008080808080ff008080808080808080808080808080808080808080808080808080808080808080808080808000000013008000000300048000030007000b000f0013
0017001b001f00230027002b002f00330037003b003f00430047004b000001353315053533153335331501353315313533153135331d0135331505353315313533153135331531353315053533152135
3315053533152135331505353315313533153135331531353315018080ff00808080fe8080808080fe0080808080fd8080018080fd8080018080fe0080808080040080808080808080ff008080808080
80808080808080808080808080808080808080808080808080808080808080800014008000000300048000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b
004f00000135331533353315053533153335331501353315313533153135331d013533150535331531353315313533153135331505353315213533150535331521353315053533153135331531353315
313533150180808080fe00808080fe8080808080fe0080808080fd8080018080fd8080018080fe00808080800400808080808080808080ff008080808080808080808080808080808080808080808080
808080808080808080808080808000000012008000000300040000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047000001353315333533150135331531353315
3135331d013533150535331531353315313533153135331505353315213533150535331521353315053533153135331531353315313533150100808080fe8080808080fe0080808080fd8080018080fd
8080018080fe0080808080038080808080ff00808080808080808080808080808080808080808080808080808080808080808080808080800014008000000300050000030007000b000f00130017001b
001f00230027002b002f00330037003b003f00430047004b004f00000135331505353315333533150535331501353315313533153135331d013533150535331531353315313533153135331505353315
21353315053533152135331505353315313533153135331531353315018080ff00808080ff0080ff0080808080fe0080808080fd8080018080fd8080018080fe00808080800480808080808080808080
80ff00808080808080808080808080808080808080808080808080808080808080808080808080800016000000000380030000030007000b000f00130017001b001f00230027002b002f00330037003b
003f00430047004b004f00530057000013353315313533153335331531353315053533152135331505353315313533153135331531353315313533153135331505353315213533150535331521353315
213533150535331531353315313533153135331531353315808080808080fe8080010080fd00808080808080fc8080010080fe0080010080010080fd0080808080800280808080808080808080808080
80808080808080808080808080808080808080808080808080808080808080808080808080000000000f0080fe800300030000030007000b000f00130017001b001f00230027002b002f00330037003b
00000135331531353315313533150535331521353315053533150735331507353315213533150535331531353315313533150535331507353315053533150100808080fe0080018080fd808080808080
018080fe00808080ff00808080ff0080028080808080808080808080808080808080808080808080808080808080808080808080808080800012008000000300048000030007000b000f00130017001b
001f00230027002b002f00330037003b003f0043004700000135331d01353315013533153135331531353315053533152135331505353315313533153135331531353315313533150535331507353315
2135331505353315313533153135331501008080ff00808080fe0080018080fd808080808080fd80808080018080fe0080808004008080808080ff008080808080808080808080808080808080808080
80808080808080808080808080808080800000000012008000000300048000030007000b000f00130017001b001f00230027002b002f00330037003b003f004300470000013533150535331503353315
313533153135331505353315213533150535331531353315313533153135331531353315053533150735331521353315053533153135331531353315018080ff008080808080fe0080018080fd808080
808080fd80808080018080fe0080808004008080808080ff008080808080808080808080808080808080808080808080808080808080808080808080808000000013008000000300048000030007000b
000f00130017001b001f00230027002b002f00330037003b003f00430047004b000001353315053533153335331501353315313533153135331505353315213533150535331531353315313533153135
331531353315053533150735331521353315053533153135331531353315018080ff00808080fe80808080fe0080018080fd808080808080fd80808080018080fe00808080040080808080808080ff00
808080808080808080808080808080808080808080808080808080808080808080808080800000000012008000000300040000030007000b000f00130017001b001f00230027002b002f00330037003b
003f004300470000013533153335331501353315313533153135331505353315213533150535331531353315313533153135331531353315053533150735331521353315053533153135331531353315
0100808080fe80808080fe0080018080fd808080808080fd80808080018080fe00808080038080808080ff00808080808080808080808080808080808080808080808080808080808080808080808080
800000000009010000000200048000030007000b000f00130017001b001f002300000135331d013533150135331531353315073533150735331507353315073533150735331501008080ff0080808080
808080808080808004008080808080ff00808080808080808080808080808080808080800009010000000200048000030007000b000f00130017001b001f002300000135331505353315033533153135
33150735331507353315073533150735331507353315018080ff00808080808080808080808080808004008080808080ff0080808080808080808080808080808080808080000000000a010000000280
048000030007000b000f00130017001b001f00230027000001353315053533153335331501353315313533150735331507353315073533150735331507353315018080ff00808080fe80808080808080
808080808080040080808080808080ff00808080808080808080808080808080808080800009010000000280048000030007000b000f00130017001b001f002300000135331533353315013533153135
331507353315073533150735331507353315073533150100808080fe80808080808080808080808080040080808080fe8080808080808080808080808080808080808080001400800000030004800003
0007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f000001353315313533153335331505353315053533153335331507353315053533153135331531353315
31353315053533152135331505353315213533150535331521353315053533153135331531353315010080808080ff0080ff008080808080fe0080808080fd8080018080fd8080018080fd8080018080
fe008080800400808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080000012008000000300048000030007000b000f00130017001b
001f00230027002b002f00330037003b003f0043004700000135331533353315053533153335331501353315313533153135331531353315053533152135331505353315213533150535331521353315
053533152135331505353315213533150100808080fe00808080fe8080808080fe0080018080fd8080018080fd8080018080fd8080018080fd80800180800400808080808080808080ff008080808080
808080808080808080808080808080808080808080808080808080800010008000000300048000030007000b000f00130017001b001f00230027002b002f00330037003b003f00000135331d01353315
013533153135331531353315053533152135331505353315213533150535331521353315053533152135331505353315313533153135331501008080ff00808080fe0080018080fd8080018080fd8080
018080fd8080018080fe0080808004008080808080ff008080808080808080808080808080808080808080808080808080808080808080800010008000000300048000030007000b000f00130017001b
001f00230027002b002f00330037003b003f0000013533150535331501353315313533153135331505353315213533150535331521353315053533152135331505353315213533150535331531353315
31353315020080ff0080ff00808080fe0080018080fd8080018080fd8080018080fd8080018080fe0080808004008080808080ff00808080808080808080808080808080808080808080808080808080
80808080808000000011008000000300048000030007000b000f00130017001b001f00230027002b002f00330037003b003f004300000135331505353315333533150135331531353315313533150535
331521353315053533152135331505353315213533150535331521353315053533153135331531353315018080ff00808080fe80808080fe0080018080fd8080018080fd8080018080fd8080018080fe
00808080040080808080808080ff008080808080808080808080808080808080808080808080808080808080808080800012008000000300048000030007000b000f00130017001b001f00230027002b
002f00330037003b003f00430047000001353315333533150535331533353315013533153135331531353315053533152135331505353315213533150535331521353315053533152135331505353315
31353315313533150180808080fe00808080fe80808080fe0080018080fd8080018080fd8080018080fd8080018080fe008080800400808080808080808080ff00808080808080808080808080808080
80808080808080808080808080808080808000000010008000000300040000030007000b000f00130017001b001f00230027002b002f00330037003b003f000001353315333533150135331531353315
3135331505353315213533150535331521353315053533152135331505353315213533150535331531353315313533150100808080fe80808080fe0080018080fd8080018080fd8080018080fd808001
8080fe00808080038080808080ff008080808080808080808080808080808080808080808080808080808080808080800007008000800300030000030007000b000f00130017001b0000013533150135
33153135331531353315313533153135331501353315018080fe808080808080fe808002808080ff0080808080808080808080ff0080800000140080ff800300038000030007000b000f00130017001b
001f00230027002b002f00330037003b003f00430047004b004f000001353315053533153135331531353315053533152135331531353315053533153335331533353315053533153335331533353315
05353315313533152135331505353315313533153135331505353315028080fe00808080fe008001008080fd808080808080fd808080808080fd808080010080fe00808080fe00800300808080808080
80808080808080808080808080808080808080808080808080808080808080808080808080808080800000000010008000000300048000030007000b000f00130017001b001f00230027002b002f0033
0037003b003f00000135331d01353315013533152135331505353315213533150535331521353315053533152135331505353315213533150535331531353315313533153135331501008080fe808001
8080fd8080018080fd8080018080fd8080018080fd8080018080fe008080808004008080808080ff00808080808080808080808080808080808080808080808080808080808080808080000000100080
00000300048000030007000b000f00130017001b001f00230027002b002f00330037003b003f000001353315053533150135331521353315053533152135331505353315213533150535331521353315
053533152135331505353315313533153135331531353315018080ff0080ff0080018080fd8080018080fd8080018080fd8080018080fd8080018080fe008080808004008080808080ff008080808080
808080808080808080808080808080808080808080808080808080800011008000000300048000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430000013533150535
3315333533150135331521353315053533152135331505353315213533150535331521353315053533152135331505353315313533153135331531353315018080ff00808080fe0080018080fd808001
8080fd8080018080fd8080018080fd8080018080fe0080808080040080808080808080ff0080808080808080808080808080808080808080808080808080808080808080808000000010008000000300
040000030007000b000f00130017001b001f00230027002b002f00330037003b003f00000135331533353315013533152135331505353315213533150535331521353315053533152135331505353315
21353315053533153135331531353315313533150100808080fe0080018080fd8080018080fd8080018080fd8080018080fd8080018080fe0080808080038080808080ff008080808080808080808080
80808080808080808080808080808080808080808080000000150080fe800300048000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f005300000135
3315053533150135331521353315053533152135331505353315213533150535331521353315053533152135331505353315313533153135331531353315073533150735331505353315313533153135
3315020080ff0080fe8080018080fd8080018080fd8080018080fd8080018080fd8080018080fe008080808080808080fe0080808004008080808080ff00808080808080808080808080808080808080
8080808080808080808080808080808080808080808080808080808000140080ff000300040000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f0000
1335331507353315073533153135331531353315313533150535331521353315053533152135331505353315213533150535331521353315053533153135331531353315313533150535331507353315
808080808080808080fe0080018080fd8080018080fd8080018080fd8080018080fd8080808080fe00808080038080808080808080808080808080808080808080808080808080808080808080808080
8080808080808080808080808080800000150080fe800300040000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f0053000001353315333533150135
33152135331505353315213533150535331521353315053533152135331505353315213533150535331531353315313533153135331507353315073533150535331531353315313533150100808080fe
0080018080fd8080018080fd8080018080fd8080018080fd8080018080fe008080808080808080fe00808080038080808080ff0080808080808080808080808080808080808080808080808080808080
80808080808080808080808080808080808000000000001501020000000000000000002400480000000000000001001a00820000000000000002000e006c0000000000000003001a0082000000000000
0004001a00820000000000000005001400000000000000000006001a00820001000000000000001200140001000000000001000d003100010000000000020007002600010000000000030011002d0001
000000000004000d00310001000000000005000a003e0001000000000006000d00310003000104090000002400480003000104090001001a00820003000104090002000e006c00030001040900030022
007a0003000104090004001a00820003000104090005001400000003000104090006001a00820032003000300034002f00300034002f003100356279205472697374616e204772696d6d657252656775
6c61725454582050726f676779436c65616e5454323030342f30342f3135006200790020005400720069007300740061006e0020004700720069006d006d006500720052006500670075006c00610072
005400540058002000500072006f0067006700790043006c00650061006e0054005400000002000000000000000000140000000100000000000000000000000000000000010100000001010201030104
01050106010701080109010a010b010c010d010e010f0110011101120113011401150116011701180119011a011b011c011d011e011f01200003000400050006000700080009000a000b000c000d000e
000f0010001100120013001400150016001700180019001a001b001c001d001e001f0020002100220023002400250026002700280029002a002b002c002d002e002f0030003100320033003400350036
003700380039003a003b003c003d003e003f0040004100420043004400450046004700480049004a004b004c004d004e004f0050005100520053005400550056005700580059005a005b005c005d005e
005f00600061012101220123012401250126012701280129012a012b012c012d012e012f0130013101320133013401350136013701380139013a013b013c013d013e013f0140014100ac00a300840085
00bd009600e80086008e008b009d00a900a400ef008a00da0083009300f200f3008d0097008800c300de00f1009e00aa00f500f400f600a200ad00c900c700ae006200630090006400cb006500c800ca
00cf00cc00cd00ce00e9006600d300d000d100af006700f0009100d600d400d5006800eb00ed0089006a0069006b006d006c006e00a0006f0071007000720073007500740076007700ea0078007a0079
007b007d007c00b800a1007f007e0080008100ec00ee00ba0e756e69636f6465233078303030310e756e69636f6465233078303030320e756e69636f6465233078303030330e756e69636f6465233078
303030340e756e69636f6465233078303030350e756e69636f6465233078303030360e756e69636f6465233078303030370e756e69636f6465233078303030380e756e69636f6465233078303030390e
756e69636f6465233078303030610e756e69636f6465233078303030620e756e69636f6465233078303030630e756e69636f6465233078303030640e756e69636f6465233078303030650e756e69636f
6465233078303030660e756e69636f6465233078303031300e756e69636f6465233078303031310e756e69636f6465233078303031320e756e69636f6465233078303031330e756e69636f6465233078
303031340e756e69636f6465233078303031350e756e69636f6465233078303031360e756e69636f6465233078303031370e756e69636f6465233078303031380e756e69636f6465233078303031390e
756e69636f6465233078303031610e756e69636f6465233078303031620e756e69636f6465233078303031630e756e69636f6465233078303031640e756e69636f6465233078303031650e756e69636f
6465233078303031660664656c657465044575726f0e756e69636f6465233078303038310e756e69636f6465233078303038320e756e69636f6465233078303038330e756e69636f6465233078303038
340e756e69636f6465233078303038350e756e69636f6465233078303038360e756e69636f6465233078303038370e756e69636f6465233078303038380e756e69636f6465233078303038390e756e69
636f6465233078303038610e756e69636f6465233078303038620e756e69636f6465233078303038630e756e69636f6465233078303038640e756e69636f6465233078303038650e756e69636f646523
3078303038660e756e69636f6465233078303039300e756e69636f6465233078303039310e756e69636f6465233078303039320e756e69636f6465233078303039330e756e69636f6465233078303039
340e756e69636f6465233078303039350e756e69636f6465233078303039360e756e69636f6465233078303039370e756e69636f6465233078303039380e756e69636f6465233078303039390e756e69
636f6465233078303039610e756e69636f6465233078303039620e756e69636f6465233078303039630e756e69636f6465233078303039640e756e69636f6465233078303039650e756e69636f646523
3078303039660000
]],
["fonts/ProggyForever.ttf"] = [[
00010000000d0080000300504646544db071471100004e180000001c474445460027001800004df8000000204f532f326861dc280000015800000060636d61709393e3be000003440000018a67617370
ffff000300004df000000008676c7966789d738700000658000041a8686561642c3ad262000000dc0000003668686561043e00e60000011400000024686d747817821441000001b80000018c6c6f6361
99df8a52000004d0000001866d617870010900e400000138000000206e616d65b5c86e22000048000000041a706f7374a844032500004c1c000001d10001000000010000e47302ad5f0f3cf5000b03e8
00000000e5afbb4200000000e5b3d43bffe1ff4401d702bc000000080002000000000000000100000283ff47000001b8ffe1ffe201d70001000000000000000000000000000000040001000000c200e1
000500000000000200000001000100000040000000000000000401b8019000050000028a02bb0000008c028a02bb000001df003101020000000000000000000000008000000710000002000000000000
00005858585800400020e9120283ff47000002af00bb00000003000000000185020600000020000101b800060000000001b8000001b8000000a60074fff900310005000400b900870087001b0016009a
003500a600270029004f0032002d001600310029002e0027002900a6008f001800180018002fffe100050026002f00270034003a0023002a0042004100130031001300290023002c000d0026002a0009
0025000dfffa0000000500210068002700680025fff2007c00300032003b0032002500280032003e008b0064002900850014003e002a003200320045004500570036001dfff90014002f0040003f00bc
003f0016009f003f00280037000500bc0040006dfff9005c0032fff9006d006500180073006d008300250030009f008f00720053003200130013001200490005000500050005000500050002002f0034
00340034003400420042004200420011002a0023002300230023002e0001002e002e002e002f0005002a002a0026002600260026002600260005003b00250025002500250033004400440044002a003e
002b002b002b002b002b0018000c003d003d003d003d001a0032001a00260015001e001800000003000000030000001c0001000000000084000300010000001c0004006800000016001000030006007e
00ab00d200d300ff01010178202620ace912ffff0000002000a100ae00d300d401010178202620ace912ffffffe3ffc1ffbfff5fffbeffbdfec4e099e01417af00010000000000000000000000000000
00000000000000000000010600000100000000000000010200000002000000000000000000000000000000010000030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324
25262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f6061008384868890949a9f9ea0a2a1a3a5a7a6a8a9
abaaacadafb1b0b2b4b3b8b7b9ba006f63646800759d6d6a00736900859600700000667400000000006b7900a4b67e6200000000006c7abf007f82930000000000000000b500bd3c00c0000000000076
0000008189808a878c8d8e8b329200919899970000006e0000007700000000000000002800280028002800440056008600d4010e016a0176019601b601d601ea02080216022802380266027c02ae02ea
030603400378038a03d80410042c0456046a047e049204d4052e0548058405ac05c805e005f40628063c0654066e0686069606b006c606f00750078207aa07f60808082e0840085c0874088808a008b2
08c208d408e608f409020950098009a209d209fa0a1a0a540a720a900ab60ace0ade0b0e0b2e0b4c0b7c0bac0bce0c0a0c260c480c5c0c780c900d0c0d260d5e0d6a0da20dd60df20e220e4a0e800ea6
0eba0f260f480f800fd20fee10321040105e107a10a210dc10ea1120113e1150116a117e11a411c011ee122c127e12b812d812f8131c1350138413ba13de141814361456147814aa14c814e61508153a
156e15a015d016041648168c16a616e6170a172e1756178e17aa17cc1812186818be1918198819f21a581b161b4e1b7c1bac1bde1c221c401c601c821cb41cfc1d3c1d621d881db21df01e2a1e5e1e96
1ec21eee1f1c1f5c1f881fae1fee2044208420c420d4000000050006000001b202bc000300060009000c000f0000132111211b020b01111303210313110601acfe541cbabac6b4c0b80172aeb602bcfd
4402a8fecb0135feb6012cfda70118fecc0149fed1025d00000200a6fffe011202230005000d00003723033733150232161406222634fb3b0e0153402c20202c20c401045b5bfea11f2c20202c000000
000200740146014402070003000700001323353317233533b541418e40400146c0c0c0000002fff8000301bf0203001b001f000001230733152307233723072337233533372335333733073337330733
0737230701bf681f5c6a263b265c263a26606e1f6270263b265c263a265ac11e5c1e013c73368f8f8f8f36733690909090a9737300030031fff1018702160028002d00340000131e031514071523352e
012f01351e0217352e03353436373533151e011f0115262715322735061514173e0135342627fe16282e1c8d3c24430f0f061545251f2c2b164b413c19370f0f2b43023e4884202924250121040f1c32
21720f2c2b010e06063f040b1603a2060f1a2a1d3543092d2a020b05053e1d04960c870c3d2cee07251f1e200a00000000050005000b01b201fa0007000f0017001f0023000012140622263436320614
1632363426221632161406222634163236342622061413330123d13c543b3b545c1e291d1d29cb543c3c543b51291e1e291d2f47fede4701c14f38384f384c271b1b271bfd384f38384f561b271b1b27
01a1fe1500030004fff601b3021500220035003f000025163123262706232226353436372e0235343633321615140e0207161736353714011c021e05173e013534262322061332372e01270615141601
7043480d153c594868342c16130f3f3c3d330f201918593a243dfec301020306080c0725251f171823603425196e1841504f4f0f18316042324a21191928182945373114242214116b454148016c010d
01070307050a090d1009192818151920fe6c241c831c363b303e0000000100b9014600ff02080003000013233533fe44440147c100010087ffc10131023e00110000010e01141617232e04343e023f01
01312b37372b3c040f261d181720210b0b023d3cb1a1b13c061544466761664b3d10100000010087ffc10131023e00110000131e04140e020f01233e01342627c3040f261d181720210b0b3c2b37372b
023d061644476761654b3c10103cb1a1b13c00000001001b0045019d01c700110000010717072715233507273727371735331537019d83832180408120838320814080014d474732468e8e4632474732
468e8e4600010016004a01a201b9000b0000133315231523352335333533fba6a63ea6a63e011f3b9a9a3b9a0001009aff81011e006b0010000036321615140e020f012736372e013534d12d1f151f1e
0b0b1a370a11196b2016203d28200707253029011f1516000001003500e30183012000030000252135210183feb2014ee43c0000000100a6ffff01120069000700003632161406222634c62c20202c20
691f2c1f1f2c000000010027ffde0190022700030000090123010190fedd4501220227fdb802480000030029fff7018f020e00050010001b000013321023221017220e03151417372603323e03353427
0716dcb2b2b2b2111c1f140e04bd1a39111c1e150e04be1b020efdea02163a081a2c4e352721e534fe5f071a2c4e352822e634000001004f000001680207000a0000132737331133152137331151016a
4469fee901690167514efe373d3d017a000100320000018602110020000013273e0233321e0215140607063121152135343736373e01353426232206073701081d5125304824112c3b930101fead0d01
a525283a2d1a4b18019f4105101b1e302e15284e3a923d2e0e0c01a5265021212f1b0d000001002dfff6018b02100029000001161514062322262f01351e023332363534262b01353332363534262322
060f013536333216151406011a706b55224e1617071950293941413140402e38372d214814144a444e623301161e6d41540d070645040c1333312e343a2d26252b0e070740174a3b3237000000020016
000001a20207000a000d00002533152315233523351333031103014a585843f1d0643ab3b53a7b7b490142feaf0112feee00000000010031fff601870207002800001311211523153633321e0315140e
022322262f01351e0233323e0235342e0223220607480121dd18291930332618233d43262b460e0e07184c261527271717282c191c380e010201043b7f0b0a19263f28314826120b060547040b130b19
30212130190c0d0700020029fff8018f0208001b0025000013321e0215140623222635343e023332161f011526232206153613323534232206151416ea1f38311d5b525762243f4f30182e0b0a1f3b45
59244e6565293b3b01571428442b5a597288496e3f1f08040342195f6248fed9777939403f3800000001002e0000018a020700060000133521150323132e015bc54ebe01cb3b1dfe1701ca0000030027
fff6019102110014002400340000011e0115140623222635343726353436333216151426220e02141e02323e02342e0102323e02342e02220e02141e01012c3034684c4d6863545f47466093251d1d10
111d1c241c1e10101d442a2020121221202820211212200115104834454e4d466b211e5b3e45463d5aa606112535251006061025352511fe5907142a3f2a130707132a3f2a140000000200290000018f
0210001b0025000037222e0235343633321615140e022322262f013516333236350603221514333236353426cd1e39301d5b52566324404f2f182e0b0b203b4459234f64642a3b3cb11427442c595a72
88496e3f1f08030441185f62480127787838404038000000000200a60051011201ab0007000f000036321614062226343622263436321614c62c20202c204c2c20202c20bb1f2c1f1f2ca51f2c1f1f2c
0002008fffd1011201ab00100018000036321615140e020f012736372e0135343622263436321614c62c20161e1f0b0a1b370a11194d2d1f1f2d1fbb2016213c291f0707253029011f1516a61f2c1f1f
2c00000000010018005401a001b0000600001325150d011525180187fec80138fe79011e913e6f703e910000000200180098019f01720003000700000121352115213521019ffe790187fe7901870137
3bd93b0000010018005401a001b00006000013051505352d01180187fe790138fec801b09139913e6f7000000002002f00000187021f0007002e0000363216140622263413321615140e031d01233534
3e0435342623220e020f012e03173e04c22d1f1f2d1f3f53521d2a2a1d461521262116342b192d1d160405030c170f01020a22263d6a1f2c1f1f2c01d444341c2f211e2111292916271c1d171f101e21
10171708080207100a01040e231a16000002ffe1ffe901d702200039004100002535170e0423222e0335343633321615140e02071523350623222634363332161f013235342623220615141e0233323e
0237263236342622061401b026020b262b47253f67432e148d6969800f18150a37242b374f50371c310a0b2d54465176273d4b272340281e07d33c2b2b3c2b67012e040b1b151121374a4f2a78a34e43
1823110701c6131f5d835e1a0d0d1c22397c683557371e0d14140747384f39394f00000000020005000001b202070007000a000001132327230723130333030109a94d29c1284da9229e4f0206fdfa88
880206feba010900000300260000019202070015001e00270000011e0415140e022b011133321e021514062715163736353426231332363534262b011501210c172219121e32391fc2b12b3e200f31d2
472754312a0841343e2d7901160307141b301f2639200e0205192b2e182a37b29c0102044c2a21fe6d2f342f2bbd00000001002ffff801890212001a000025150623222e02353436333217152623220e
0215141e0133320188314f3856321870684d33334d2639200f1a42324d5d491c2a4a5f3a759720472d213b4a2e3e5b3b000200270000019002070007000e00001332111023063111133235342b0111c7
c9c8a09f7d7d540206fefdfefe010206fe34cac9fe6d000000010034000001830207000b000013211521173315231521152135014dfefd01e3e40104feb202063b993bbc3b0000000001003a0000017e
020700090000132115231f01152315233a0143f901b5b64a02063b9e013bf10000010023fff801940212002400002527233533150e0423222e02353436333217152623220e0215141e0133323637014e
0163aa02071b20361e3855331870684e32334d2639200e1942321b2907438d3adf0207120d0b2a4a5f3a759720472d213b4a2e3e5b3b0a050001002a0000018d0207000b000033113315333533112335
23152b4bcc4b4bcc0206d4d4fdfaf7f600010042000001750207000b00001335211523113315053537114301327474fece7301cb3b3bfe703a0139010191000000010041fff901760207000e00001333
111406230631353332363511239ed8424ea49330258c0206fe9e505a0139303a012f000000010013000001a50207000b0000331133153733071323030715134be955d4dd5ab4390206e6e6d4fece0104
39ca00000001003100000187020700050000371133112115314b010a010205fe353a000000010013000001a40207000c0000331133173733110711032303111464636564416b396a0206fefefdfb0101
c5feef0113fe3a00000100290000018e020700090000330333131133112303112d0362ba485fba0206fe5a01a6fdfa01a6fe5a0000020023fffa0195021200050019000013321023221012323e03342e
03220e03141e02dcb8b8b8a7221b1e140d0d141e1b221b1e140e0e141f0211fdea0216fe25081a2b4e6a4e2c1a08081a2c4e6a4e2b1a00000002002cffff018b02070010008100001332161514062b01
1c011e011c0131231117323e0235342e022b0115390a3339023339013331333133313331333133313b02313b41db555b55546c01014ba1202e160a0a152a1e5d01010101010101010101010101010101
0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010102065046475905153730321d0206fc131f1f11
111f1f12c30000000002000dffb801ab020f000c002000001332111407171523270623221012323e03342e03220e03141e02c6b828543052283ab8a7221a1f140d0d141e1b221b1e140e0e141e020efe
f57b43612c5b1b0216fe25081a2b4e6a4e2c1a08081a2c4e6a4e2b1a00020026000001910207000d00170000370723113332161514060717232737231516373e01353426720348b34f53392c7a4f6f02
634427223335dbda020559452d4510e5d9f3b80102022e2d2b2f00000001002afff6018d02110036000001152e0223220615141e07171e031514062322262f01351e02333236353426272e0335343633
321e0217016d061543233e3d060e0b190c1f092101172a301d65592b4e1212071b562d2d423d4522302f1867571228201b0801f547040d1529300b120f0b0a05070305010411213a274e4a1009084905
101b2e2d2e250e06111f3222485205080902000000010009000001af02070007000013352115231107110a01a4ac4b01cb3b3bfe360101cb00010025fff6019302070018000037113311141e02333236
35113311140e03222e03254c06132b2043304b061626445f46261607a60160fe991a2322123643015ffead21303521161520322c0001000d000001ab0207000600003303331b013303aea149868648a1
0206fe3a01c6fdfa0001fff9000001be0207000c00003303331b01331b013303230b014b513f3a3f53403940514948490206fe63019dfe62019efdfa01b8fe480001ffff000001b90207000b00001327
33173733071323270723b8a050797b51a5b1508d8d500113f3c3c3f3feeddada00010005000001b2020700080000370333173733031523b6b04f878650b14bea011ce4e4fee3e9000001002100000197
02070009000013352115012115053501290166fee00128fe8a011801cb3b35fe6a3a01380193000000010068ffbd01500249000700000127113315231133014fa2a2e7e7021201fde238028c00010027
ffde0190022700030000130123016d012346fede0227fdb80248000000010068ffbd0150024900070000133311233533110768e7e7a3a30249fd7438021e010000010025013f01930207000600001317
2327072337fc9652646552960206c68d8dc600000001fff1ffbe01c7fff7000300000521352101c6fe2c01d4423800000001007c01a0013c02390003000013172327cf6c526d02399999000000020030
fff60188018f0026003800001315321e02150631233627062322263534373e011f01303c012e0623063135323613323e03352223260e04151416d6263a341d013d02022663404f6817551e1f02030709
0f121a107a123a182030170d020a15182d2720160d33018f010c21412ff1191b3d3f3e5d1d060501010e07110a100a0c0705023602fe9d1a21311b110102030a121c14252300000000020032fff70186
02310014001e00001332161514062322262f0107231133153e0402323635342622061514e84757583b203e100f073d44010512172b247032336e34018e6665626a1d0f0f31022fdd030814100cfe9f52
44435353434400000001003bfff7017d018f00140000012206151416333237150623222634363332171526010d414848413e312f4e556e6e554e2f3101594a4c4c4a2b41206ac46a20412b0000020032
fff7018602310016001e000001331123270e0423222635343633321e021f0102323534262206150142443e0603081a1b29143b585746192a18110304c8c83068300230fdd1310307150f0d6a6265660c
11110607feda96435353430000020025fff70193018f0014001a00003714163332363715062322263534363332161514312722061533346c3e432a3e2e4652596d694f5b5aaf2f45e0b4384f1215401d
6d5f5d6e614930a54430740000010028000301900224001300000122061d013315231123112335333534363b01150128231f7d7d447a7a41406c01ed19254b39fed9012739344e3e3700000000020032
ff460186018f001d0027000001331114062b013533323e023d010e04232226353436333216170332353423220615141601483d5a5182881c2a170a010512182a19475b5b3c30341c6b63633533330185
fe6e56563a162425123b0206100c0a65646265141dfed78e965244414d0000000001003e0001017a0231001200001332161511233534262322061d012311331536fd35484428252f3844442501924e43
ff00fa2d303d3bdf022fda3c0002008b0001012d021e0005001100001335331123113723223d01343b01321d01148ba1443b380a0a3809014b3bfe7b014a7f0a400909400a00000000020064ff460154
021e000b001800000123223d01343b01321d011407353311142b01353332363511014a380a0a3809aeae7f70691d2501ca0a400909400a7e3afe62a13b34320164000000000100290002018e021e000b
0000371723270715231133113733d2bc519a334646b550f5f3c92d9c021bfec9a1000000000100850001013302080005000013331123112385ad45680207fdfa01ca000000010014fffe01a4018f001e
000001321615112311342322060711231134232206071123113315363332173e0101442b343d341a1a033d341a1a033e3e1f2a341a0d30018f4535feea0105542225feee0105542124feec0185121d31
161b00000001003e0001017a0193001200001332161511233534262322061d012311331736fd35484428252f384440042501924e43ff00fa2d303d3bdf0185303c0000000002002afff7018d01900007
0011000012321615142035341632363534262206151489a65efe9e835c3a3b5a3b01906666cdcd66fe4850504646505000020032ff4e018601880016001e000013321615140623222e022f0115231133
173e040232363534221514eb42595747192918120303443d070106141929236730c70188696365670c12110606dd023031020814100dfe9e524394944300000000020032ff4e018601880016001e0000
01331123350e0423222635343633321e021f01023236353422151401483e44010512182a194657594217291a130404a26830c8017efdd0dd0208150f0d676563690c12110606fed95243949443000000
0001004500000173018e001300000132161f01152e022322061d01231133073e01010c1d340b0b0410381f38474444021145018e0f070841050c143c23f50185351b230000010045fffe017301970029
0000371e011514062322262f01351e02333e013534272e0135343e023332161f01152e022322151416e8464558491f461414081c491d2e315b53390e20412c213e0e0f06133f215a24ea0b2e3739420c
060641040c1501241a310f0f352c162624140b06063e040b1139191d00010057000601610204001200003732311523222e03351133153315231514d8887d192129181044a2a2413a030f1a3222017c76
3acc460000010036fff601820185001600003735331514333236353631331323350e042322263644562c3e03440140010311172d1b4f4991f4f1663527fbfe7c2a0207130d0c4a000001001d0000019b
0185000600000133032303331301544694569347780185fe7b0185feba0000000001fff8ffff01c00185000c000001330323270723033313373317017c43613f444141614444354c3c0185fe7bd7d701
85fed0c0c000000000010014ffff01a30185000b00002517232707233727331737330101a24e7a784ea2954c6e6d4ccbcb9b9bccb98d8d000001002fff440189018600ae0000011215140e022b013533
323e04353435390323390223390123312331233123312331233123312b01312b02312b6d222635123133151416331631030186030c182f20c3b010170d08030101010101010101010101010101010101
0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101
0101010101010101010101010101010101010101010101010101010a474801441d2989010185fe8730112f34243b0c181622110e04024e310106e5313201014900010040ffff01770186000e00003733
1721353e0327223135211589ed01feca0f32694501e5012d34343b113a7a5201323c00000001003fffb201780254002a000001151407161d0114163b01152223222e033d0134262b01353332363d0134
3e0333323115232206010b4848262a1d1f0d26361b0e03262b33332b26030e1b36262c1d2a2601d7645f11116063221d3e141a2c1e17562b2042202a57171e2c1a133e1d000100bcffc800fc023f0003
000017231133fc40403702750001003fffb201780254002a0000133534262b01353233321e031d0114163b01152322061d01140e03232231353332363d01343726ac252a1d1e0e25361b0f02262c3333
2c26020f1b36252c1d2a254848017364211d3e131a2c1e17572a2042202b56181d2c1a143e1d2164601110000001001600b101a1015d0021000001170e0423222e03070e010f01273e0433321e03373e
013701673a050511152819192b1e1c1f10141903033a050511152819192b1e1c1f1014190301420d15152d18141a23241502032d15150d15162c18151a24231603032d140002009fffeb011902090005
000d00001333170723351222263436321614c42e1d01654c3223233223014bc49b9b010a23312323310000000002003fff93017901f20016001c0000011136371506071523352e013534363735331516
17152603110e011514011939272d33264e666450263d23285e323a0158fed702193b15036465066b5c5f6804616205143c19fede012606494386000000010028ffff01900210001b0000373315213533
352335333534363332161f0115262322061d01331523cac5fe99574949524f2134090a302a323288883b3b3ba1325258580a0505401e353f5832000000020037003f0180017d0017001f000000140717
072706222707273726343727371736321737170706323634262206140159163c223e1f491f3e223c16163c223e1f491f3e223c88412e2e412e0103491e39233c13133c23391e491e39233c13133c2339
8d2c3d2b2b3d000000010005000001b20207001800000107331523071533152315233523353335272335332733173701b2785b732194944b949421735b774f87860206c227350b27b6b6270b3527c2e4
e4000000000200bcffce00fc02390003000700001323113311231133fc4040404001320107fd95010800000000020040ffbc01770210003b004b0000251e011514062322262f01351633323635342627
26272e04353436372e013534363332161f0115262322061514161716171e04151406273e013534262726270e011514161716013019154548193910103a33242a252f08031c1927110e26211915444a18
360e0e33332528242f08031d1827110e2647151c302e1916161b302d19751222172c420a0505391a2019141e1a0402100e1c1722131c32101222182b3b0a0505391b1a19141e1a04020f0f1c1721141c
32090c230f1a2c190e0d0c240f1a2c190e0000000002006d01d4014b021b000b001700001323223d01343b01321d01143323223d01343b01321d0114b03a09093a088a3a09093a0901d5093409093409
09340909340900000003fff8002801c001da0014001c0024000036343633321715262322061514163332371506232202321614062226341232363426220614544e3e391e2b222f34342f2e1f24333d25
bc8585bc86969c6f6f9c6fc37e420f26153031312f122411015b7fb37f7fb3fef4699369699300000003005c00a2015b021200280037003b00003722263534373e011f013c012e022322060f01353e06
333216151431232e031506372223220e0415141633323617233533c72f3b4d113d1615080f20161a320b0c021207110c11120939492d01020302282805081918251314082416312935f7f7f22c2b4314
05030101080a160e0b0b06062c0106020602030139409f0208110c012d8e0103060b120d18183db62b000000000200320031018601700006000d00001307171527353733150717152735df6f6fadada7
7070ae012d5d5d42911c92435d5d42911c0000000004fff8002801c001da0007000f0023002d0000123216140622263412323634262206143716171631232627262322311523353332151406273e0135
34262b0115167ebc8585bc86969c6f6f9c6fe6111a2a341e1115181b2f5c67223d131b1d15311f01da7fb37f7fb3fef469936969933e0726412c1a2167f24a15201201141313144f010000000001006d
01b7014b01eb0003000001233533014adcdc01b733000000000200650132015202100007000f000012321614062226341632363426220614ab6245456245583b2a2a3b29020f415b41415b7027372727
3700000000020018ffff019f01970003000f000005213521273315231523352335333533019ffe790187a4a4a43ea5a53e013bea3b73733b72000000000100730185014502ad00190000133315233536
37363534262322060f0135363332161514060706ad98d24d202a251c112a0c0d2733393a1b245601ac2726481d282412190b06062914331c162d224d0001006d0182014b02b000270000011406232226
2f0135163332363534262b01353332363534262322060f0135363332161514060716014b443715310f0e322a1a302b1f1f1f1d241e1b152c0c0c2f2c313f211e4701d6252f0704042a121a19191b2616
1512160804042a0d2a211c1d070d00000001008301b5013502390003000013330723eb49783802398300000000010025ff6b01920184002500002532363f0115062322262f010e042322262715231133
151416333236353631331015140178070c04031b1d131a0403010410162a1b1a2f0b3c402d252b3b034131040202340f1a0d0d0207120e0b1c1ac00218f130353526fbfee2181d0000010030ffb90188
020700100000133311231123101506222f011122263436d8af3446011a0d0d4563630206fdb60220fde5050201010127557955000001009f00c60119013f000700001232161406222634c33223233223
013f2332232332000001008fff7701290001000e0000211615142322262f013516333227300100285b0e1f0808181b5b4929253b0402022e0b5e0000000100720184014602a7000a0000133315233533
3507353733fa4bcc4c53553301ab2626d40e280d0003005300a2016502100007001100150000242035343632161526220615141632363534132335330164feef497f4966452e2d472d2af9f9f38f4747
4747652f363631313636feeb2b000000000200320031018601700006000d00001317150735372717150735372735d8aeae707007ad6f6f0170921c91425d5d4f1c91425d5d43000000040013ffa201a5
0240000a00150018001c000013073537331533152335331715331523152335233537073335371705277b5257304bcb4bf72b2b3290865d665c09fe7809021a0e270dfc272780bc2541412ab7bb909625
5b25000000030013ffa201a50240000a0024002800001307353733153315233533130631331523353637363534262322060f012736333216151406371705277c5257304bcb4be05698d24d202a251c11
290d0c012733393b1c1b09fe7809021a0e270dfc2727fed24d2727471e272413190c06062a13331b172df7255b25000000040012ffa201a50246002600310034003800000114062322262f0135163332
3634262b01353332363534262322060f0135363332161514060716171533152315233523353707332737170527010c483715300d0d292e242a2622211e1f252119182d0a0a2e2c323e211e48692b2b31
91865c66015909fe7809016c242f07040429121d2f1c25171514140804042a0c29201d1d080ee9bc2541412ab7bb9096255b250000020049fff7016f021b00070026000000222634363216140332363f
01150e0223222635343e043d013315140e0315141601092c20202c203e1d42121307184522524e131d221d13471a25261a2f01b11f2c1f1f2cfe60180b0c41050c154136182b1e1e181f11453d1e3022
1f27151f2400000000030005000001b302830007000a000e00000113232723072313033303372327330109a94d29c1284da9229e4f2d3854440206fdfa87870206feb9010a5d5c0000030005000001b3
02830007000a000e00000113232723072313033303370723370109a94d28c1294da9229e4f5f5438480206fdfa87870206feb9010ab95c5c00030005ffff01b302830007000a00110000011323272307
2313033303350723373317230109a94d28c1294da9229e4f3d334e454e330205fdfa87870206feb9010a9d3e5c5c000000030005fffe01b3027b0007000a001f00000113232723072313033303272206
1523343633321633323635373314062322260109a94d28c1294da9229e4f330c0c2f24221a3e0f0b0d012e22241c3a0205fdfa87870206feb9010a891310212b221109081933230000040005ffff01b3
0283000b0017001f002200001323223d01343b01321d01143323223d01343b01321d01140713232723072313033303b03a09093a09893a09093a0942a94d28c1294da9229e4f023d0934090934090934
0909340938fdfa87870206feb9010a0000030005fffe01b30295000f001c001f0000011323272307231326353436321615142733141617333e01353426220603330301139f4d28c1294d9f2f3c543c9f
0112102c1013212f22169e4f01e6fe19878701e71c33283737283334111a06061a11151f1ffe73010a00000000020002ffff01b60207000f001300002515331523352307231325072317370f01112303
013580ca7f2544960117017a016f01b9254bf6bc3b86850205023d99013c38010dfef4000001002fff77018902100028000001220e0215141e0133323715060716151406232227351633323534272e03
353436333217152601082639200e1942324d332538212d262d19181f2e1a3651301770684d333301d5203c4a2e3e5b3b2e49150524201f1e092d0b1f1620032a4a5d38759720472d0002003400030184
02870003000f00000123273303211521112115231f011523010d395444450104feb20147fd01f1f2022a5dfdb83b02063b99013b000200340004018402870003000f0000010723370321152111211523
1f011523012c543848690104feb20147fd01f1f202875d5dfdb93b02063b99013b000000000200340004018402880006001200001307233733172303211521112115231f011523d53d344e464e349301
04feb20147fd01f1f202693e5d5dfe153b02063b99013b0000030034000001840285000b0017002300003721152111211523173315231323223d01343b01321d01143323223d01343b01321d01147f01
04feb20147fd01f1f2313a09093a09893a09093a093b3b02063b993b0148083409093408083409093408000000020042fffe017602830003000f000001232733172311171521353311233521010b3854
44b27373fece7474013202265cb8fe6f01393b01903b0000000200420003017602870003000f0000010723371723113315213533112735210139543848807474fece7373013202875d5db8fe703b3901
91013b000002004200030176028800060012000013072337331723172311331521353311273521e13d344e464e34577474fece7373013202693e5d5d5cfe703b390191013b00000000030042fffe0176
0283000b0017002300000123113315213537112335212723223d01343b01321d01143323223d01343b01321d011401757474fece73730132c53a09093a09893a09093a0901cafe703b390101913b3809
34090934090934090934090000020011000401a7020b00120024000013321e03140e03232231352335333513323e03342e032b011533152315c91f383d2d1d1d2d3d381e8b2e2e8913242a1e14141e2a
24133e6363020b0c23375f7d6036230bf535dcfe33091b2a4b624b2a1b08a235bc0000000002002a0000018e02820009001e000001331123031123033313032206152334363332163332363537331406
2322260146485fba470462ba9d0c0c2f24221a3e0f0b0d012e22241c3a0206fdfa01a6fe5a0206fe5a01f81310212b22110908193323000000030023fffa0195028700030009001d0000012327331732
1023221012323e03342e03220e03141e02011239544413b8b8b8a8211b1e140d0d141d1c221b1e140d0d141f022a5d76fdea0216fe25081a2b4e6a4e2c1a08081a2c4e6a4e2b1a0000030023fffa0195
02880006000c002000001307233733172307321023221012323e03342e03220e03141e02dc3d344e464e343db8b8b8a8211b1e140d0d141d1c221b1e140d0d141f02693e5d5d1afdea0216fe25081a2b
4e6a4e2c1a08081a2c4e6a4e2b1a000000030023fffb0195028100050019002e000013321023221012323e03342e03220e03141e02032206152334363332163332363735331406232226dcb8b8b8a821
1b1e140d0d141d1c221b1e140d0d141f080b0d2f2423193e0f0b0d012e22231d3a0211fdea0216fe25081a2c4e694e2c1b07071b2c4e694e2c1a02191310212b221109081933230000040023fffa0195
0288000b0017001d003100001323223d01343b01321d01143323223d01343b01321d011407321023221012323e03342e03220e03141e02b03a09093a09893a09093a096fb8b8b8a8211b1e140d0d141d
1c221b1e140d0d141f024209340909340909340909340931fdea0216fe25081a2b4e6a4e2c1a08081a2c4e6a4e2b1a000001002e004101890189000b0000010717072707273727371737018981812d80
812d81812d8180015e79792a79792a79792a797900030001ffea01b702220011001c00270000010716151023222707273726351033321737011417372623220e0313323e03353427071601b7411eb853
2e3624411eb8532e36fede07b91a3b111b1e140d6b111b1e140d07b81a0208584169fef5384819584169010b3848fee5382af93a081a2c4efefb081a2b4e35382af83a000002002efff6018a02830003
001500000123273317331114232235113311141633323e02350113395445734baab04b273d1e28130702265c7cfea0b0bd0153fea0433412212419000002002efff6018a028300030015000001072337
17331114232235113311141633323e02350132543948514baab04b273d1e28130702825c5c7cfea0b0bd0153fea04334122124190002002efff6018a0283000600180000130723373317231733111423
2235113311141633323e0235dc3d344e464e34254baab04b273d1e28130702653e5c5c21fea0b0bd0153fea043341122241900000003002ffff6018a0283000b0017002900001323223d01343b01321d
01143323223d01343b01321d011407331114232235113311141633323e0235b13a09093a09893a09093a090e4cabb04b283d1e271307023d09340909340909340909340937fea0b0bd0153fea0433411
2224190000020005ffff01b302820003000c00000107233717330315233703331701365439497050b14b01b14f8702825c5c7cfee3e9ea011ce500000002002affff018e0206000b0014000013321615
142b0115231133151732363534262b0115dc535fb2674b4b5d313d3f2f5d01aa4b4a967f02065cf02b302f2ab40000000001002afff2018e021c0030000013141e0315140e012627351633323635342e
0335343e023f0126362e022322061511231134363332152206ea22303021334e58263133292d212f2f211823220c0c01030a0e261b2e294449529b2f48013214211b2035232b391405103815241d1825
191b2d1e1c2c180f0202010f1716103032fe7b01854d4a982c00000000030026fff501920245002b003a003e0000251514161f012326270e012322263534363332313c022e052322060f01353e063332
03323e0227302a01230e0115141613232733017e0a0505440907154c2641505b5b5c0206080f141e122647101103190b1811181a0da8c124341a0a01242f0b3c34336f397648e27419370f0f16252025
403c3e46010f09120d100b0a05120a093f01090308030402fe9b1d2f321a02202c2624019783000000030026fff501920245002b003a003e0000251514161f012326270e012322263534363332313c02
2e052322060f01353e06333203323e02272a02230e0115141613072337017e0a0505440907154c2641505b5b5c0206080f141e122647101103190b1811181a0da8c225341a0a0101232f0b3c3433b776
3967e27419370f0f16252025403c3e46010f09120d100b0a05120a093f01090308030402fe9b1d2f321a02202c2624021a83830000030026fff501920245002b003a00410000251514161f012326270e
012322263534363332313c022e052322060f01353e06333203323e0227302a01230e0115141613072337331723017e0a0505440907154c2641505b5b5c0206080f141e122647101103190b1811181a0d
a8c124341a0a01242f0b3c34333f42345a385a34e27419370f0f16252025403c3f45010f09120d100b0a05120a093f01090308030402fe9b1d2f321a02202c262401ef588383000000030026fff50192
0233002b003a00540000251514161f012326270e012322263534363332313c022e052322060f01353e06333203323e0227302a01230e011514161333221523343633321e013332363d0133140e022322
2e02017e0a0505440907154c2641505b5b5c0206080f141e122647101103190b1811181a0da8c124341a0a01242f0b3c343309011a2d242515221e0d0c0d2e050d1e15151f1116e27419370f0f162520
25403c3e46010f09120d100b0a05120a093f01090308030402fe9b1d2f321a02202c262401dc362b371b1a1a0d0e0f1d211511141100000000040026fff50192021d002b003a00460052000025151416
1f012326270e012322263534363332313c022e052322060f01353e06333203323e02272a02230e011514161323223d01343b01321d01143323223d01343b01321d0114017e0a0505440907154c264150
5b5b5c0206080f141e122647101103190b1811181a0da8c225341a0a0101232f0b3c3433103a09093a09893a09093a09e27419370f0f16252025403c3e46010f09120d100b0a05120a093f0109030803
0402fe9b1d2f321a02202c262401ab0934090934090934090934090000040026fff501920272002b003a0042004a0000251514161f012326270e012322263534363332313c022e052322060f01353e06
333203323e02272a02230e0115141612222634363216142622061416323634017e0a0505440907154c2641505b5b5c0206080f141e122647101103190b1811181a0da8c225341a0a0101232f0b3c3433
66543c3c543c4f2e21212e21e27419370f0f16252025403c3e46010f09120d100b0a05120a093f01090308030402fe9b1d2f321a02202c2624018638503838505d1f2c1f1f2c000000030005fff501b3
018f00ca00d600e000002515303901233901233123312b7b1c021e053316363f01150e0223222e022f010e042322263534363b013534262322060f01353633321e021f013e04333216171e0115073631
2322061514163332363714313336272e01232201b20101010101010101010101010101010101010101010101010101010101010102010101010101010101020101010102010101020101010201010201
01020101020102010102010201020102010201020102010201020102010202010201020201020201020201020201020202010202020102020201020202020102020202090204060a0e150c2234090a05
0f3019192a16100203010410152416393c4644322a1e132e0d0e2c41131e120c020201030e1324163a30080201f4011c352c2517231b41790102031b1f39b301011c0a1c0d170b0e060111090a3c030a
100b111006050207130f0c3c3839411c32270e08073a18090d0d040502060f0b0a3b4c122a0c42362026231f24b01b17162326000001003bff77017d018e00250000012206151416333237150e010f01
16151406232227351633323534272e013534363332171526010d414848413e310e2b0f0f212d262d19181f2e1a526a6e554e2f3101584a4c4c4a2b410b0f020224201f1e082e0c20161f036960626a20
412b000000030025fff7019302450014001a001e0000371416333236371506232226353436333216151631272206153334272327336c3e432a3e2e4652596d694f5c5901b02e46e03c397648b4384f12
15401d6d5f5d6e614930a5443074698300030025fff7019302450014001a001e0000371416333236371506232226353436333216151631272206153334370723376c3e432a3e2e4652596d694f5c5901
b02e46e00e773866b4384f1215401d6d5f5d6e614930a5443074ec838300000000030025fff7019302450014001a00210000371416333236371506232226353436333216151631272206153334270723
373317236c3e432a3e2e4652596d694f5c5901b02e46e06e43345a395a34b4384f1215401d6d5f5d6e614930a5443074c158838300040025fff70193021d0014001a0026003200003714163332363715
062322263534363332161516312722061533342723223d01343b01321d01143323223d01343b01321d01146c3e432a3e2e4652596d694f5c5901b02e46e09c3a09093a09893909093909b4384f121540
1d6d5f5d6e614930a54430747d09340909340909340909340900000000020033fffc0175023b0003001200001323273313153315222322263d012335331514e3397749a950184736415a9e01b883fdf9
01364e3fc932fb5600020044fffc0174023b0003001200000107233713153315222322263d012335331514014c773967224f174835425a9e023b8383fdf901364e3fc932fb56000000020044fffc0174
0245000e0015000025153315222322263d0123353315140307233733172301254f174835425a9e2842345a385a333401364e3fc932fb5601e658838300030044fffc0174021a000d0019002500002533
15222322263d0123353315140323223d01343b01321d01143323223d01343b01321d011401254f174835425a9e48390909390989390909390933364e3fc932fb5701a109340909340909340909340900
0002002afff4018e021b0020002f000001141e06151406232226353436172e012f01072737273317371707160332363534262f01262322061514160140140613080d0605595858596b64051b0a0b670c
5843512f6a0d5d24492d3c140a0a12322b3b3c0183011b0a1d13211d2613566b6b5e5d6608081f0c0b1f221b453120221c22fe81464726430f0e0945494945000002003e0000017a02200019002d0000
1333221523343633321e013332363d0133140e0223222e02172332161511233534262322061d012311331736a9011a2d242514231e0d0c0d2e050e1d15151f1116490135484427262f384440042501f4
362c361b1a1a0e0d0f1d2115111411634e43ff00fa2d303d3bdf01842f3c00000003002bfff4018d024500070011001500001232161514203534163236353426220615141323273389a65efe9e835c3a
3b5a3b9b387749018d6666cccc66fd475050464650500151830000000003002bfff4018d024500070011001500001232161514203534163236353426220615141307233789a65efe9e835c3a3b5a3be2
763967018d6666cccc66fd4750504646505001d4838300000003002bfff4018d024500070011001800001232161514203534163236353426220615141307233733172389a65efe9e835c3a3b5a3b6842
345a385a34018d6666cccc66fd4750504646505001a95883830000000003002bfff4018d022000070011002a000012321615142035341632363534262206151413221523343633321e013332363d0133
140e0223222e0289a65efe9e835c3a3b5a3b35192e242614231d0e0c0d2e050e1e1514201016018d6666cccc66fd475050464650500183362c361b1a1a0e0d0f1d211511141100000004002bfff4018d
021a00070011001d002900001232161514203534163236353426220615141323223d01343b01321d01143323223d01343b01321d011489a65efe9e835c3a3b5a3b3c3a09093a09893a09093a09018d66
66cccc66fd4750504646505001630934090934090934090934090000000300180031019f0188000f001f00230000372322263d0134363b0132161d011406032322263d0134363b0132161d0114061721
3521fe460407070446050606054604070704460506069cfe7901873206053f040707043f0506010106053f050606053f0506733b0003000cffde01ac01a60012001a0022000001071615142322270727
3726353436333217370514173726232206173236353427071601ac3d21b24b2c39223e1d5e53472e37feec09a81b2e2d3b682f390aa91b018b443451cc283f1a4632516666273fe32a1fbd2146e74850
2a20bd250002003dfff6017b023b0017001b000001331123270e042322263d01331514163332363536272327330136443f060205141726154d3d43262b2c3503293977490185fe7c39020a17120e4852
f4f034343b32eb33830000000002003dfff6017b023b0017001b000001331123270e042322263d01331514163332363536370723370136443f060205141726154d3d43262b2c35031d7738660185fe7c
39020a17120e4852f4f034343b32ebb6838300000002003dfff6017b02450017001e0000011123270e042322263d013315141633323635363127072337331723017a3f060205141726154d3d43262a2d
35025942345a385a340185fe7c39020a17120e4852f4f034343b32eb955883830003003dfff6017b021b00170023002f000001331123270e042322263d013315141633323635362723223d01343b0132
1d01143323223d01343b01321d01140136443f060205141726154d3d43262a2d3503893a09093a09893a08083a090185fe7c39020a17120e4852f4f034343b32eb500934090934090934090934090000
0002001aff6b019e023b0003001a000001072337173302070e032b01353033323e02373631033313014677386659488c270e20241912362e0f17120a060ca0487b023b8383b6feaf62232c1205360c18
13101d017ffed00000020032ff6b01860220000d00170000133216151423222715231133153612323635342622061514da505bab432044441f185937395638018d6666cc41ca02b4d03dfe9d47505046
465050000003001aff6b019e0211000b0017002e00001323223d01343b01321d01143323223d01343b01321d0114173302070e032b01353033323e02373631033313af3a09093a098939090939090c48
8c270e20241912362e0f17120a060ca0487b01cb09340909340909340909340946feaf62232c1205360c1813101d017ffed0000000030026fff501920208002b003a003e0000251514161f012326270e
012322263534363332313c022e052322060f01353e06333203323e02272a02230e0115141613233533017e0a0505440907154c2641505b5b5c0206080f141e122647101103190b1811181a0da8c22534
1a0a0101232f0b3c3433aadddde27419370f0f16252025403c3e46010f09120d100b0a05120a093f01090308030402fe9b1d2f321a02202c262401a93400000000030015000001a3006a000f001f002f
00002532161d0114062b0122263d013436332332161d0114062b0122263d013436332332161d0114062b0122263d01343633019605070705460507070551050707054605070705510507070547040808
04690705510507070551050707055105070705510507070551050707055105070001001efff901990214002b0000012207330723061514153307231e0333323637150623222627233733263437233733
3e01333217152e01012c6e14b212a30184116f061b262517143d1b2f3d58670e41122c02023e122f0e675341301c3d01dc90241e04110f252c3c1d0a1318471c685f251d091c245f691d481815000000
00010018008101a0012c00050000011523352135019f3efeb7012cab703b000000000016010e0001000000000000004700900001000000000001000d00f4000100000000000200070112000100000000
0003001a01500001000000000004000d01870001000000000005002301dd00010000000000060014022b000100000000000900210284000100000000000d000b02be0001000000000010000d02e60001
000000000011000703040003000104090000008e00000003000104090001001a00d80003000104090002000e010200030001040900030034011a0003000104090004001a016b00030001040900050046
0195000300010409000600280201000300010409000900420240000300010409000d001602a60003000104090010001a02ca0003000104090011000e02f40043006f0070007900720069006700680074
00200028006300290020003200300032003600200044006900730063006f002000480065006c006c006f002c00200043006f007000790072006900670068007400200028006300290020003200300031
0039002c00320030003200330020005400720069007300740061006e0020004700720069006d006d006500720000436f7079726967687420286329203230323620446973636f2048656c6c6f2c20436f
707972696768742028632920323031392c32303233205472697374616e204772696d6d65720000500072006f0067006700790046006f00720065007600650072000050726f676779466f726576657200
0052006500670075006c006100720000526567756c6172000031002e003000310032003b00500072006f0067006700790046006f007200650076006500720052006500670075006c006100720000312e
3031323b50726f676779466f7265766572526567756c61720000500072006f0067006700790046006f00720065007600650072000050726f676779466f72657665720000560065007200730069006f00
6e00200031002e003000310032003b0046006f006e007400730065006c00660020004d0061006b0065007200200033002e0035002e00310037000056657273696f6e20312e3031323b466f6e7473656c
66204d616b657220332e352e31370000500072006f0067006700790046006f007200650076006500720052006500670075006c00610072000050726f676779466f7265766572526567756c6172000054
00720069007300740061006e0020004700720069006d006d00650072002c00200054006800690065006200610075006c007400200043006f00750072006f007400005472697374616e204772696d6d65
722c20546869656261756c7420436f75726f7400004d004900540020004c006900630065006e0073006500004d4954204c6963656e73650000500072006f0067006700790046006f0072006500760065
0072000050726f676779466f7265766572000052006500670075006c006100720000526567756c6172000000000200000000000000000000000000010000000000000000000000000000000000c20000
000100020003000400050006000700080009000a000b000c000d000e000f0010001100120013001400150016001700180019001a001b001c001d001e001f002000210022002300240025002600270028
0029002a002b002c002d002e002f0030003100320033003400350036003700380039003a003b003c003d003e003f0040004100420043004400450046004700480049004a004b004c004d004e004f0050
005100520053005400550056005700580059005a005b005c005d005e01020060006100a30084008500bd009600e80086008e008b009d00a9008a01030083009300f200f3008d0097008800c300de00f1
009e00aa00f500f400f600a200ad00c900c700ae006200630090006400cb006500c800ca00cf00cc00cd00ce00e9006600d300d100af006700f0009100d600d400d5006800eb00ed0089006a0069006b
006d006c006e00a0006f0071007000720073007500740076007700ea0078007a0079007b007d007c00b800a1007f007e0080008100ec00ee00ba010400ab010501060b766572746963616c626172096f
76657273636f726507616d6163726f6e044575726f07756e696539313200000000000001ffff000200010000000c00000010001800020000000400000002000000010000000100000000000100000000
e50db79300000000e5afbb4200000000e5b3d43b
]],
["fonts/Cousine-Regular.ttf"] = [[
00010000001101000004001044534947000000010000ab44000000084646544d4ee467c80000ab6c0000001c47444546010500040000ab4c000000204f532f32f9269e4f0000019800000060636d6170
5d08ad9f000003b00000027a637674204d165eb5000011a00000023c6670676d73d323b00000062c0000070567617370001800090000ab3400000010676c796639545d7f000015900000894468656164
f6dda5880000011c00000036686865610b7802620000015400000024686d74784f0a46ca000001f8000001b86c6f63616c1a4dd2000013dc000001b26d617870047c026700000178000000206e616d65
11e538f900009ed400000a32706f7374ec7cffc70000a9080000022a70726570d998f79400000d340000046a0001000000010000bd3316a55f0f3cf5001f080000000000c96f2dd500000000c96f2dd5
fff6fe3904d606a80000000800020000000000000001000006a9fd99000004cdfff6fff704d60001000000000000000000000000000000040001000000d800790005006a000400020010002f005a0000
032d015200030001000304cd019000050008059a05330000011d059a05330000036100660212080502070409020205020404a00002af400078fb00000000000000003141534300400020221904e7fe7e
022606a902676000009fdfd70000043a054500000020000104cd00440000000004cd000004cd000002030115003600420000002b01f60156015000f800740103014e01f00072007c0094009000800067
00800097009e0085008d01f00160007400740074005e002c000000a2007100a200a200c2007100a200ca00b000a200ed008100a2006600a2006600a2004f004c008e000a0000002400240049019a0073
010e0085fffb0192008000b30082008a0085008a008f00b9008f007500ec0086006300b3008200b3008a00f200a700be00b900450015005e0042009300e3021300a7006c020300850038009e00340213
009f014f001f00ce007f0074001ffffb014700740138013201920090007f01f00077011d00e2006b001b0011000b00990000000000000000000000000001007100a200a200a200a200ca00ca00ca00ca
001b00a200660066006600660066009d0038008e008e008e008e002400a2008e008000800080008000800080001400820085008500850085008f008f008f008f007d00b3008200820082008200820074
006e00b900b900b900b9004200b90042008f0014001c011b017400ff00aafff6018d018f015d00b100b200b20150015a016e00cc005b01f0012500fa00ea00c500000003000000030000001c00010000
00000174000300010000001c0004015800000022002000040002007e00ff0131015302c602da02dc2014201a201e2022203a204420ac22152219ffff0000002000a00131015202c602da02dc20132018
201c20222039204420ac22152219ffffffe30000ff8fff6ffdfdfdeafde9e0b3e0b0e0afe0ace096e08de026debcdeba0001000000200000000000000000000000000000000000000000000000000000
00000000000300620063006400650066006700680069006a006b006c006d0010006e006f0070007100720073007400750076007700780079007a007b007c007d007e007f008000810082008300840085
0086008700880089008a008b008c008d008e008f0090009100920093009400950096009700980099009a009b009c009d009e009f00a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad
00ae00af00b000b100b200b300b400b500b600b700b800b900ba00bb00bc00bd00be00bf0000010600000100000000000000010200000002000000000000000000000000000000010000030405060708
090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758
595a5b5c5d5e5f6061008485878991969ca1a0a2a4a3a5a7a9a8aaabadacaeafb1b3b2b4b6b5bab9bbbc0070636468ce769f6e6a00746900869800710000667500000000006b7a00a6b87f626d000000
006c7b0003808395c1c2c6c7cbccc8c9b700bf00d1d2cfd000000077cacd00828a818b888d8e8f8c939400929a9b99c0c3c56f0000c4780000000000404559585554535251504f4e4d4c4b4a49484746
4544434241403f3e3d3c3b3a393837363531302f2e2d2c28272625242322211f181411100f0e0d0b0a090807060504030201002c4523466020b02660b004262348482d2c452346236120b02661b00426
2348482d2c45234660b0206120b04660b004262348482d2c4523462361b0206020b02661b02061b004262348482d2c45234660b0406120b06660b004262348482d2c4523462361b0406020b02661b040
61b004262348482d2c0110203c003c2d2c20452320b0cd442320b8015a51582320b08d44235920b0ed51582320b04d44235920b0042651582320b00d44235921212d2c20204518684420b001602045b0
4676688a4560442d2c01b10b0a432343650a2d2c00b10a0b4323430b2d2c00b0282370b101283e01b0282370b10228453ab10200080d2d2c2045b00325456164b050515845441b2121592d2c49b00e23
442d2c2045b0004360442d2c01b00643b00743650a2d2c2069b04061b0008b20b12cc08a8cb8100062602b0c642364615c58b00361592d2c8a03458a8a87b0112bb0292344b0297ae4182d2c4565b02c
234445b02b23442d2c4b525845441b2121592d2c4b515845441b2121592d2c01b005251023208af500b0016023edec2d2c01b005251023208af500b0016123edec2d2c01b0062510f500edec2d2c4623
46608a8a462320468a608a61b8ff8062232010238ab10c0c8a70456020b0005058b00161b8ffba8b1bb0468c59b0106068013a2d2c2045b0032546524bb013515b58b0022546206861b00325b003253f
2321381b2111592d2c2045b00325465058b0022546206861b00325b003253f2321381b2111592d2c00b00743b006430b2d2c21210c6423648bb84000622d2c21b08051580c6423648bb82000621bb200
402f2b59b002602d2c21b0c051580c6423648bb81555621bb200802f2b59b002602d2c0c6423648bb84000626023212d2c4b53588ab004254964234569b0408b61b08062b020616ab00e23442310b00e
f61b21238a121120392f592d2c4b535820b0032549646920b00526b0062549642361b08062b020616ab00e2344b0042610b00ef68a10b00e2344b00ef6b00e2344b00eed1b8ab0042611122039232039
2f2f592d2c4523456023456023456023766818b08062202d2cb0482b2d2c2045b0005458b040442045b04061441b2121592d2c45b1302f4523456160b0016069442d2c4b5158b02f2370b01423421b21
21592d2c4b515820b0032545695358441b2121591b2121592d2c45b01443b0006063b0016069442d2cb02f45442d2c452320458a60442d2c45234560442d2c4b235158b90033ffe0b134201bb3330034
005944442d2cb0164358b00326458a586466b01f601b64b020606620581b21b04059b001615923586559b02923442310b029e01b2121212121592d2cb0024354584b53234b515a58381b2121591b2121
2121592d2cb0164358b004254564b020606620581b21b04059b0016123581b6559b0292344b00525b00825082058021b0359b0042510b005252046b0042523423cb00425b0072508b0072510b0062520
46b00425b0016023423c2058011b0059b0042510b00525b029e0b02920456544b0072510b00625b029e0b00525b00825082058021b0359b00525b003254348b00425b0072508b00625b00325b0016043
481b2159212121212121212d2c02b00425202046b004252342b0052508b003254548212121212d2c02b0032520b0042508b0022543482121212d2c452320451820b00050205823652359236820b04050
5821b04059235865598a60442d2c4b53234b515a5820458a60441b2121592d2c4b545820458a60441b2121592d2c4b53234b515a58381b2121592d2cb000214b5458381b2121592d2cb002435458b046
2b1b21212121592d2cb002435458b0472b1b212121592d2cb002435458b0482b1b21212121592d2cb002435458b0492b1b212121592d2c208a08234b538a4b515a5823381b2121592d2c00b0022549b0
00535820b04038111b21592d2c014623466023466123201020468a61b8ff80628ab140408a704560683a2d2c208a2349648a2353583c1b21592d2c4b52587d1b7a592d2cb012004b014b54422d2cb102
0042b123018851b1400188535a58b910000020885458b202010243604259b12401885158b920000040885458b2020202436042b12401885458b2022002436042004b014b5258b2020802436042591bb9
40000080885458b202040243604259b94000008063b80100885458b202080243604259b94000010063b80200885458b202100243604259b94000020063b80400885458b202400243604259595959592d
2c451868234b51582320452064b04050587c59688a6059442d2cb00016b00225b0022501b001233e00b002233eb10102060cb00a236542b00b234201b001233f00b002233fb10102060cb006236542b0
072342b00116012d2c7a8a104523f5182d0000004064090304fd01f550281ff246281ff1462a1ff046351f8bee9beeabee036bef8bef02bbef01a4ef011bef5bef6bef0304ec44ec020aeb46ff1fe7e4
261fe6e43d1fe5e41e1fe3e2461f0be20140e246161fe1e0461fbbe0cbe0dbe00340e0333646e046181fbc0114003e011200550113400c3d0355df3ddd55de3ddc55bb41090116000100540116006401
160002ffc00116b30c164620b80116b2010200bc01160010011600020115b2dc3d1fb80111406303ff1f10dd20dd40dd50dd80ddb0dd0620dc50dc80dcb0dc040fdc01d015331f5fc86fc87fc8035fc3
6fc37fc303bfc201c150261f70be0120be30bec0be0370be80be020fbc1fbc022fbc3fbc6fbcafbcdfbc05b9ad261f20b830b850b870b880b805b8ffc0401cb813294610b70120b750b780b7b0b70480
b5b0b5020fb33fb3efb303b8010d402baa481f80b090b002b0b0c0b0d0b0032faf3faf02a0adb0ad02c0add0ad022fac3fac029fab01c0aad0aa02410f010f0032010e00550000010e0010010e002001
0e0070010e0004000f0110401501509c609c709c039996261f9846261f3097409702b8ffc0b396161c46b8ffc0402a960e1146951bff1f0f94af94bf940340941d31464094161b4640940c0f460f932f
933f937f93ef93050fba010c006f010c402002928d261f9153ff1fdf90013090011f902f90026f907f9002008f108f208f03b8ffc040208f181c46208e308e024f8d5f8d6f8d03308c010f8c1f8c2f8c
03408c10134610bf010b0020010b0030010b0003ffc0010bb2172046b9ffc0010b40501014468b82261f894a3c1f88873d1f87843c1f864aff1f9f8501108420843084033083017f82014082090c4673
50261f6f46351f6e46351f1a01185519331855073303550603ff1f6050261f5f50261fb9ffe00107b21f2646b9ffe00107401f131c465e5a481f5c46311f5b5a481f5a46311f13321255050103550432
0355b80108b51b3c1f0f0301b901190118b2351f40b80118b2172846b8010a402550261f52501b1fef51ff510240513538464051252846cf5001df4c014c461d1f4b46481f50b8011b405a014a46261f
4946351f4846351f4746351faf4601df46ef46028046011632155511010f5510320f55020100550100011f1f0f3f0f5f0f7f0f040f0f2f0f4f0f6f0f8f0fdf0fff0f073f0f7f0fef0f036f00014f0001
a016010501b80190b154532b2b4bb807ff524bb009505bb00188b02553b00188b040515ab00688b000555a5b58b101018e59858d8d00421d4bb0325358b0601d594bb0645358b0401d594bb0805358b0
101db11600425973747374752b2b2b2b2b017374752b2b2b2b73002b2b74742b2b732b2b2b2b732b2b2b2b012b2b2b002b2b2b2b2b2b2b2b2b012b2b002b012b737474732b2b2b2b2b2b2b73002b7374
74742b75737474742b2b73732b2b2b732b012b2b732b2b007374732b017373740073747473742b7301730073742b732b737401737475002b73017400742b7373732b2b735f732b74742b2b2b2b012b2b
742b2b5e732b002b2b2b012b5e73007373737473012b2b2b002b73185f5e000005cc05cc007d054500150060054500150000000000000000000000000000043a001400770000ffec00000000ffec0000
0000ffec0000fe57000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000008000000000000b400bd00aa00a000c800b400850000000000000088007e00a500c000000000000000000000000000bf00c900ab008c00bc009b008d0000000000000000000000000000
00000000000000000000000000b900b4007c000000000094005f000000000000000000000000000000000000000000000000000001a8006f007800a400c80083008d00bb005e018a0103006000740081
00b800c0005004b104c3fe4c00eb01b200c30109007b012b029600c9009600000000000000000000000000000000000000000000009200a8006b009200b7006b009b0000000002f20092023e006e02a5
0345008900a00060024c000000c30000014e00a4015b005e00800069006f0000005e00680000000000000000000000000026034a0087007b0000007500680000000000000000000005820233000bfff4
00830089008f009600690071005b005200000000000000b202030000008d031f00bb00ae00b500000000008b000000000000000006a9037002bc0208fd9900910091004d004d006400640181009f00a8
fe68fe9c009b014501450090fef60017007a0545024d000cffee0598028b006e004c0062004600e8004405110000002c002c002c002c0054007e01300230030604080424045c0492050c054a05760592
05ac05da065c06a8072207e8083208bc0964099e0a6a0b0e0b340b4c0b9c0bd60c260c9c0ddc0e3e0ec20f540fae0ff2102c10b010f4113a118411ec120e12a41306137813d0145e14da158a15c2161a
167e177417f8185a18ac18d8190819301978198c19aa1a581ad61b5c1bde1c681cc81d761dc61e061e5a1ece1efe1f941fea204e20d6215c21b02242229a22f4235e244e24ca255625982618263226b2
2714273e27c4285e292829ac29d42adc2b082c0a2c9c2cec2d0e2de42dee2e4e2ea22efe2f862fa430123054305c30b030f0314c319a31c031de3200327a329232aa32c232da32f4330c337c339033a8
33c033d833f2340a3422343a345434d034e83500351835303548356235cc367a369236aa36c236dc36f43752380a3822383838523870388c38a43984399839b039c639dc39f43a103a263a443a683b1a
3b303b483b5e3b743b8a3ba23bee3cac3cc43cda3cf23d0c3d223da63dbe3dee3e683f203f483fc8402040344048406e409840c6410a415641a441e0420e423c425e43004322436443bc444a44a20000
0002004400000264055500030007002eb101002f3cb207041ced32b10605dc3cb203021ced3200b103002f3cb205041ced32b207061dfc3cb201021ced323311211125211121440220fe240198fe6805
55faab4404cd000000020203000002c90544000300070029401503079802000410040204040809010102059d040203003f2fed11392f111201392f5d33ed323130012303330335331502b19418c4c6c2
018d03b7fabcc9c900020115034d03b605cc0003000700274014039602020907962f060100060106050000060200003f33332f32012f5d5ded12392fed31300123033301230333038c8e28e0fe158d29
e0034d027ffd81027f000000000200360000048b0545001b001f00f0b90004fff040990d1148061d161d261d0306011601260103120f0e0b040a13081d1c1504140904010019041805161f1e07040617
13ac1405ac0617ac181a2f033f0302031a0318300640065006031018201802e018f01802140618180614030a2111110c0c09ac000a010a08040caf0d1d010d1c0010af111915114f11014f115f11ef11
0311401f22487f0d8f0d9f0d034f0d5f0d020d110d11051713030a05002f333f331239392f2f5d712b5d7111333310ed323211333310ed3232012f5ded332f332f111217392f2f2f5d71711133332f5d
2f10ed10ed10ed1112173911121739111217391112173931305d5d2b010321152103231321032313233533132335211333032113330333152103211303a2430104fee5586e56fe95546e54c9e143f101
07596e58016b586e58d3fd4045016a430341fec36cfe680198fe6801986c013d6c0198fe680198fe686cfec3013d000000030042ff73047e05c400390044004f011940c7443d543d02654e016e470165
3c016a2f01752a852a0276298629027622862202650e01790a890a02790989090279038903027a1f8a1f022c6f2d2d454b3327063f241107071f7500850002006f004510452045604570450510452045
304570459045a045b045074545510c6f0b401220488e0b010b0b3a6f001f401f02001f101ff01f031f324073244a1273333f3f242d2d272425402027482540121748252524064b117308000c100c400c
500c600c05400c500ca00cb00cf00c050c0c089007010707050818003f33332f5d11332f5d7110ed323f332f2b2b1133332f11392f33ed3210ed32012f5d71ed332f5d2bed11332f5d71ed5d12392f33
3333cd32323211332fed5d3130005d5d5d5d5d5d5d5d5d5d5d015d01140e02071523352e0127371e0317112627262726232e0535343e02373533151e0317072e0327111e0501141e0217110e0301342e
0227113e03047e3872af7780d7f322aa0b2d4d704d0807040603023162584c381f40729d5d806691664014ae0a273f583b3a7267584124fcb028455d3647623c1b02a430506a3a416b4d2b017c4b805f
3905a1a107b9a725365b44290401e902020102010c1d273749613f52774f28038282042c50754c21304f392104fe560e1f2b3a516c023636472f1e0e019d03213545fd5e3f5134210ffe29041e385200
00050000fff404ce055100030017002b003f005300c1b9003effd8400e090e483828090c483428090c482eb8ffd8b3090e4816b8ffd8400e090e481028090c480c28090c4806b8ffd84016090e487902
8902020203560076008600030001031001bafff00003ffc0403a0a0e481f012f013f01030301030113314ab43bb531b470408040024040135518b409b522b41345b62cb74fb6361927b60eb71db60407
02060018003f3f3fedf4ed3fedf4ed012fedf4ed1112392f5dfdf4ed111239392f2f5d2b383811335d11335d3130002b2b2b2b2b2b2b2b3323013325321e0215140e0223222e0235343e0213342e0223
220e0215141e0233323e0201321e0215140e0223222e0235343e0213342e0223220e0215141e0233323e02dd9103a493fca03a674d2d2d4e683c3c694e2c2d4e6ac61223332123362412132435222033
241302083a674d2d2d4e683c3c694e2c2d4e6ac6122333212336241213243522203324130549081f4d816360825023234f836063814d1ffeb0425835151635574240573617173657fed81f4d81636082
5023234f836063814d1ffeb0425835151635574240573617173657000003002bffec04a1054d003d004f005f00f44018734e834e0279518951026558755885580369530139250120b8ffe8b3090e481c
b8ffe8b3090c482eb8fff0403d090c48251201061216120248491923493e303e01303b013b3b3149306b50010c141c146c1403284b5314043e2d5034050419703e803e903e033e3e3019b8ffc0403f10
13483f3001193019306156480f400b17480f602d702d802d036550012950010550342d040a30454b554b654b034b14302853055b43511e03375b51000a16003f33ed323fed1217395d2f1217395d5d5d
012f2bed1239392f2f5d2b12392f5d1117391217395d5d10ed322f5d5d10ed10ed3130005d5d2b2b2b5d015d005d015d005d05222e02270e0323222e0235343e02372e0335343e0233321e0215140e02
071e03173e0137170e01071e0133323637150e0101342e0223220e02151416173e03032e01270e0115141e0233323e020430294c433a161c4756673b6d9e66312d4e6b3e0f1b150c2b57845945765630
4472934e1f3e444a2a2a3f14911a57312b61281a30151838febb182d40272c49351e2617407155314a59a142555e2040614127473c310c15232f1a193226183a668a504d7e6751201c44494c253e6d51
2e23446440476c5646213664605d2e57c6792b8be55e36330709870b0b044922382716192e432a41742b1b323a47fccb64e1742e9565345c4427131e26000000000101f6034d02d605cc0003001c400d
03960002010202040500000200003f332f111201392f5ded31300123033302ac8d29e0034d027f0000010156fe57037b05cc0015002840161006f0110f051f050205051700f0000b010b1000061b003f
3f012f5ded12392f5d33ed32313001141e0217232e0335343e0237330e03021129588960be5c87592b2b59875cbe6089582902128af4e6e17671dbe6fb9090f9e5d97176e1e6f40000010150fe570375
05cc00150024401300f00b0b171105f0100006100602061000051b003f3f012f5d33ed3212392fed313001140e0207233e0335342e0227331e0303752a58875cc0608a582a2a588a60c05c87582a0214
90fbe6db7176e1e6f48a89f4e6e17671d9e5f900000100f802fc03d405cb000e00af40175b066b0602000350036003030f095f096f09030b030708b8ffc0404c0b114808080a0d0504400b1148040402
0e0e0a0d0e700a800a020a060e000d100d020d0d0f10020a0a010b000c750c850c02030904087f0b8f0b020b0806090c0b050d057007800790070307b8ffc0b6090c4807070d00003f332f2b5d331217
392f5d113311335d11331133331133111201392f5dcd39c45d2b0118104de4392f2b331112392f2b3331305f5e5d5d005d0125170517070b0127372537050333029f01082dfee6b977969c77bdfee82d
010b0c8804a4678449fa480102ff0048f849866b012900000001007400b40457049e000b004940250b0e0104020e040901aa40060002010a0302020c0d020e0407050e300701070004ad0905b3003f33
ed32c65d2b0018104de6111201392f5f5e5d331aed32c62b0118104de6313001112311213521113311211502af93fe5801a89301a80260fe5401ac9201acfe549200000000010103fe9502b5012b0003
00394027090019000200100e1148090219020202100e114801990297039a2f006f007f000300009c019b040010f6ed012f5dedfded31302b5d2b5d011321010103a90109fec9fe950296fd6a0001014e
01d0037e02700003001d400f03030500001000020000ba2f010101002f5ded012f5d12392f313001352115014e023001d0a0a000000101f0000002db012b0003001a400c039600000100000405019b00
002fed111201392f5ded31302111331101f0eb012bfed50000010072ffec045905cc0003003d401c8a010139016901790103010210020205850301360366037603030300b8fff0b70000010003190100
003f3f012f5d38325d5d12392f38335d5d313017013301720335b2fccf1405e0fa2000000003007cffec044f055a00130027002b007940523620014611561102460d560d024907590702490359030210
2a202a02402a502a020029012a292a291e006e401450140200140114142d1e6f000a100a020a4f28014f285f28022829292319730f0723730519003fed3fed12392fcd5d71012f5ded12392f5d71ed12
39392f2f5d5d713130005d5d5d5d5d0114020e0123222e01023534123e0133321e011207342e0223220e0215141e0233323e0225353315044f4d85b46667b2834b4b84b46a65b1844cb7284e71484c74
4f28294f724947724f2bfe58eb02a3c3fef7a44747a3010ac3cc010c9f40409ffef4cc9fd47e34357ed39f9ad280383880d222f9f9000000000100940000046a05450012005940390011011111106f02
08081f01010f013f014f015f018f019f01af010701010e100201100270028002030202131408730909030e061001730018003fed323f33332fed111201392f5d7133332f5d71332f10ed322f5d313033
3521110e032335323e0237331121159d01b713607d8e424a8f7c601aa601619103fd2847341f94223d5432fb4c910000000100900000043c055a00260087405c7506850602392001741b841b02761a86
1a0277158715027716871602690a016b1001591001051a0126261d6e1f083f08026f08bf080208080028126e131300602470248024032424cf000100001000020012120d7318070124730018003fed32
3fed332f012f5d5d332f5d11332fed1112392f5d71ed322f3130005d5d5d5d5d5d5d5d5d5d33353e0535342e0223220e0207273e0333321e0215140e04072115902c90a3a585542747613b365d462c07
b809406fa26a69a7753e5687a49c802002d37561a4918481854a3c57391b1e3c593b114c86653a305d8a595ea19082808146920000010080ffec044b055a003b00e64063753985390263390140395039
0278348834026c34014b345b3402742f842f02062e762e862e0377298729026c25015a2501631201640d01550d017908890802790789070279038903027b028b0202361a316e3f205f20022020006e13
40130100130113b8ffc0403c1518481a131a130a3d266e27270b6ecf0adf0aef0a03000a100a020a3619731a1a102c262623732c07107305400b900b02400b900bf00b030b0b0519003f332f5d7110ed
3fed332f1112392fed39012f5d5ded332fed111239392f2f2b5d7110ed332f71ed11393130005d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d01140e0223222e0227371e0333323635342e022b01353332
3e0235342623220607273e0333321e0215140e0207151e03044b3f79b27482b4743a09ba082b4a6d49889b456779336662336e5b3b858377930cb50b507b9e5977a96c3325496f4a527d542b01725b91
6436416b894911385c42247e79445a35169c1937593f6a777a6f0e5d8a5b2d38608149376756400f04093c576a00000000020067000004650545000a000d00554035690679068906037f090109090801
6f0d069002a0020202020f0c6f1004010400047308050f0c1f0c2f0c6f0c040c0c010b06060118003f3f3312392f5d3333ed32012f5ded12392f5d3333ed32322f5d31305d0111231121350133113315
09012103a9b4fd72027bc7bcfe90fe0c01f4013ffec1013f8c037afc888e0343fd4b000000010080ffec044b0545002c008e405e69087908890803690779078907034a175a1702451355130222400d11
482222006e152524211f6f20200a4015010015c015d0150315152e0b6ef00a01cf0adf0a02000a100a020a257320201a736f2801282810247321061073050b0b0519003f332f10ed3fed12392f5ded33
2fed012f5d5d5ded12392f5d7112392fed33323210ed322f2b3130005d5d5d5d01140e0223222e0227371e0333323e0235342e0223220e02072313211521033e0133321e02044b407ebb7b6fa572430e
b60b284565484672512c2a4e71482d4c413517b02f0321fd851d30906369a8764001bc64ab7b46345b7a4615284b3b232b5075493c674c2b101c251402d891fe57253540719c000000020097ffec0448
055a0024003800ab407d7a038a03027a028a0202680378038803036723772387230367227722872203453755370243315331024a2d5a2d024b275b27021c1b2c1b3c1b03156e1414006eaf2501402501
0025c025d0250325253a1d2f6e900aa00a02000a100ae00af00a040a2a736f2001102020200220203418730f8f150115150f0734730519003fed3f332f5d10ed12392f5d5ded012f5d71ed3212392f5d
7171ed322fed3130005d5d5d5d5d5d5d5d5d5d01140e0223222e023534123e0133321e0217072e0123220e02153e0133321e0207342e0223220e0215141e0233323e0204483b73aa6f7bb87a3d4582bb
76487e674e17ac1c7b514a78542d31b273609d6f3cb724486a4631645133284b6a424167482601be64ab7d465dacf598b10110b85f1e436e501f5b514586c5815b5f3e72a06b4370502d214162414881
62392d52740000000001009e0000042d0545000e00354010056e06060c006f0b0b10000c100c020cb8ffc0400b1418480c000c730d060518003f3fed32012f2b5d12392fed12392fed313001060a0215
23341a0237213521042d60ab824cbc5086b061fd1f038f04ba8afedafecffec69f9e013b013201248591000000030085ffec0446055a0029003d005100b6407e7528852802760c860c02780288020278
08880802771d871d02792289220278128812026a46016a4001655001654a01653c0165360124101f346e151f6e2a152a152a0a006e403e01003ec03ed03e033e3e53486ee00af00a02cf0adf0a02000a
100a020a241010437339394d761a861a022f731a0779058905024d730519003fed5d3fed5d12392fed391133012f5d5d5ded12392f5d71ed1239392f2f10ed10ed1239393130005d5d5d5d5d5d5d5d5d
5d5d5d5d01140e0223222e0235343e0237352e0335343e0233321e0215140e0207151e0303342e0223220e0215141e0233323e0213342e0223220e0215141e0233323e0204463975b57d7cb577392f4f
65363b5d3f213970a66d73a96f36213f5d3d3d684d2bde1b3e644946633f1c163a665056663711231c4473564f6f452020467251536f441d017a54916c3d3d6b91534773543409040e3e546435447d60
393a607e443464543d0c040a35547102332f523c23233c522f2552452d2e4552fdbb2d59472c2c485a2f3b654a2a294b670000000002008dffec043f055a0024003800a540777a088a08027a078a0702
67207720872003681a781a881a03453755370245315531024a2d5a2d024b275b27021311231133110313006e3f258f259f25af2504c025d0250225251d3a0b6e0a0a2f6ee01df01d02cf1d01001d101d
021d347313184013164818180e2a7322070e7305700b800b020b0b0519003f332f5d10ed3fed12392f2b33ed012f5d5d5ded332fed1112392f5d71ed333130005d5d5d5d5d5d5d5d5d0114020e012322
2e0227371e0133323e02370e0323222e0235343e0233321207342e0223220e0215141e0233323e02043f4684bd775182664816ac1c775b4a7855310115495d6b38609b6c3b3f78af6fecf1c425496b46
41684827234668453267533502bfb1fef2b75d2146704f1b5b554484c4812f4a331b447aa96568a97842feb4b04983633b2d53734642735732224365000201f0000002db043a00030007002440120605
039600000100000908049b050f019b00002fed3fed111201392f5ded33323130211133110311331101f0ebebeb012bfed5030f012bfed50000020160fe950312043a0003000700000111331101132101
01f0ebfe85a90109fec9030f012bfed5fb860296fd6a000000010074009a045704aa00060077b5000152050405b8010d401006001406000506010201520304010403b8010d4028020114020103020608
040000100002000100040402053f066f067f0603064012154806030f020102002f5d332f2b5d3312393d2f333301182f5d3310ded4c187042b1001c187042b10c41001c18704182b872bc43130133501
150901157403e3fca6035a023bcd01a29afe92fe91990000000200740158045703ec00030007004c40370702090400011001020105ad1f047f049f04af04bf040504401216480400ad30015001600170
0104500170018001b001d001050f010101002f5d5d71ed2f2b71ed012f5d3310ce32313013352115013521157403e3fc1d03e303589494fe0094940000010074009a045704aa00060077b50601520102
01b8010d401000061400060100050401520302050203b8010d4028040514040503040206080400001000020006050202030f040104013f006f007f0003004012154800002f2b5d322f5d33393d2f3333
01182f5dc410ce3210c187042b1001c187042b10c41001c18704182b872bc431303735090135011574035afca603e39a99016f016e9afe5ecd0000000002005e00000431055a0027002b006d40496906
7906890603690479048904034a1b5a1b02072517250266007600860003092b980a28281e009800130113132d1d981e0a400f14480a0a23299d28662376238623031d1d189e2304003fed332f5d2fed11
392f2b012fed12392f5ded12392f33ed325d3130005d5d5d5d01140e0607233e0735342e0223220e0207273e0333321e02013533150431253e4f524f3f2701af02273e4e504d3c25294d6d44446f5232
07b80c4a7eb0706cb17e44fd8fc303ea456852423b3b445337436651413b3b4453353754391e2647653e0c5a976c3d305d89fbbcc9c900000002002cfee504a505cc005f0078013b4091014411440235
3f553f753f03013e113e026c347c348c340317180b0e486c087c088c08036c667c66024a665a6602265d016a5301654975498549037f3f8f3f023f400b0e487e3901036f2a7f2a8f2a03024f2a5f2a02
17180b0e486a377a378a37036951014b0f4c1f4c024c4c5600d337292fd30a60280a6ad21a37401418481037012f1a3f1a02370a1a1a0a37037a41b8ffe8b50d114841d356b8ffc0402617204856326f
d6150f051563d621282819260126210f151f152f159f15af15059021a0210221b8ffc04017090c484b152121154b03463c100d11483cd55b0046d551b8010f003fed3fed2b1217392f2f2f2b5d5d1133
5d332f10ed11333310ed32012f2bed2b1217392f2f2f5d5d2b10ed11333310ed3210ed11392f5d33005d015d3130002b5d5f5d5f5d2b5d5d5d5d5d5d015d2b5d5d5d5d01140e0223222e02353c013e01
35230e0323222e0235343e0433321e0217333733030e0315141633323e0235342e0223220e010215141e0233323e0237170e0323222e010235341a013633321e011205342623220e0415141e0233323e
02373e0304a5294e72481e3427160101060f2e3b492a3f563517172c415162382a3e2b1c0905207463080f0b061d1428412f193565946078bb7f423b6fa2663b66595126472b616f7e497fcc8e4d54a1
e99683c3803ffe62463b254034281b0e0b1b2c2027473a2e0d0308070402f390efac6010253c2c020b0c0a032747361f416b8b4a4c96887555301b2f3f2497fe052a51493d1534245091c97981e0a660
86e7fec8b198fab362192a3a21572544351f71d00126b6d1016301049276c8fef8766a6d2c4b647177393c674b2b37648f5713343631000000020000000004cc05450007001400724042130203050152
030d035e04051404050304140100060152000d005e07061407060d041007010707167f048f0402040701025f1413135a0d014b0d010d060503040012003f323f33335d5d392f33ed322f012f5d11332f
5d1239872b872bc410c0c01000c18705182b872bc410c0c031302103210323013309012e03270e03070321040a8afdcd89c401fed901f5fdf10d1c171104041017190d9701cf017efe820545fabb03ba
264e46350c0c36464e25fe59000300a200000480054500160021002e0077404f652d016a24016620017b028b0202740b840b940b03070b016f12014b125b120212290d5a17400d1448171706005a0022
012222301c295a00061006a006b006040612285f1c1c291b5f0703295f0612003fed3fed12392fed39012f5ded3212392f5ded12392f2bed11395d5d3130005d5d5d5d5d5d01140e0223211121321e02
15140e02071e030134262b011133323e0213342e022b011121323e020480548ebc68fe28019c75b88043214364445583582efeee9797dfe15172482151335d8250fc010a497d5b33017c67905b2a0545
264f7c563865513b0e0938577102286b5bfe601f3950fdd93f58391afe2217385e00000000010071ffec0476055a002900a4402d392701461d561d661d03661c01360301201f301f02601f701f02a01f
011f1f20401d244820200a0b0bb00a010ab8ffc04045090c480a0a2b7600860002005b30154015501503001510150215461a561a02255f1a20201a04100a200a300a600a700a05300a800ad00ae00a04
0a0a4910591002055f1013003fed5d332f5d713f332f10ed5d012f5d71ed5d12392f2b5d332f11332f2b332f5d71723130005d5d5d5d01141e0233323e0237170e0323222e01023534123e0133321e02
17072e0323220e02013a2854835c3f654f3a159f1c52749b668dcd87414084ca8a65986f4d19a80f3449613c597f522602a983cb8c48304e6636414685694065b801029ea60101af5b375d7942412e58
442a4487c7000000000200a2000004650545000c00170061404575028502027a108a10027017801702141724176417037f0f8f0f021b0f2b0f6b0f03340b01340a013b0301005a000da00d020d0d1914
5a000610060206135f0703145f0612003fed3fed012f5ded12392f5ded3130005d5d5d5d5d5d5d5d5d0114020e0123211121321e0207342e022b011133321204655098dd8cfe8e01379ef3a655c03872
ac7579a2d4ce02b0a8feffae5905454da2faac87c07939fbf3010a00000100a2000004620545000b005a403b07400a14480707000a0303000a010a0a0d05095a000010000200085f0f053f057f058f05
046f058f059f05bf05df0505050509045f0103095f0012003fed3fed12392f7172ed012f5ded3212392f5d332f1112392f2b3130331121152111211521112115a20397fd28029cfd64030105459cfe5a
9afe339c000100c20000043d054500090047402d02020608080b01055a000610060206045f3f017f018f01033f016f019f01030140152948010105005f07030512003f3fed12392f2b5d72ed012f5ded
3212392f12392f313001112115211123112115018102a4fd5cbf037b04a9fe129efde305459c000000010071ffec044f055a00290088404a080201151e251e751e851e0455120127270a25af15011515
1414005ac025d025027f258f250200250125252b361d761d861db61d041d5b300a400a500a03000a100a020a275f2828200fb8fff0400e0d1148185f0f15150f04205f0513003fed3f332f10ed2b1239
2fed012f5d71ed5d12392f5d5d5ded322f332f5d1112392f3130005d5d5d250e0323222e01023534123e0133321e0217072e0123220e0215101233323e023711213521044d245f74894e86c682403e82
c88964976f4b18ab268b6f5a7e4f23a9ac305343310dfedb01df8518362d1e69ba010199a70101af5a34597945386e794587c782fef4feea111719090150a000000100a20000042a0545000b0057403c
0b5a081f002f003f006f007f00056f007f008f00bf000400000d07035a70040100041004a004b0040404025f500701a007b007020707090503040012003f323f33392f5d71ed012f5d71ed3212392f5d
7133ed3130211121112311331121113311036bfdf6bfbf020abf026ffd910545fdca0236fabb0000000100ca000004010545000b005940380606010e0407070000090e70000100045a0f093f098f099f
09044f095f097f098f09cf09df090610090109090c0d04085f0712030b5f0003003fed323fed32111201392f5d5d71edc45d2b110133182f104de4322f3130132115211121152135211121ca0337fec4
013cfcc9013cfec405459cfbf39c9c040d000000000100b0ffec03da05450015004bb7651075108510030bb8ffe840220d1148131308005a111108178009010909000810080208135f14030e5f050909
0513003f332f10ed3fed012f5d332f5d1112392fed12392f3130002b5d01140e0223222627371e03333236351121352103da33659662abcc23bb0a2a3a49296869fec901f501a464a3733eb2c01f405d
3c1d8f8a03099c00000100a2000004cb0545000b00974034740a840a02460a560a660a03640774078407038d00016b007b00024900590002390a01140a0101200b11480a010b720982090209b8ffe040
2d0b0f480809100909000b10000b100b020b0b0d07035a00041004020402070a0176078607020701080503040012003f323f3339395d11331133012f5ded3211332f5d3833332f38332b5d113939002b
31305d5d015d5d5d005d015d5d21010711231133110133090103ebfe19a3bfbf023ce1fe080245027ea9fe2b0545fd5f02a1fdc4fcf70000000100ed0000044c0545000500264016040407035a2f003f
00020000100002000103035f0012003fed3f012f5d5ded12392f3130331133112115edbf02a00545fb579c00000100810000044b0545002c0099b9001effe840130d1148261e361e022a180d1148292a
392a020cb8fff040190b11480d100b11480d0c24241c2c5c2a0040191c4800002e1eb8fff0400d0a11004c1e1b5c001c101c021cb8ffc0b3282d481cb8ffc04009181e481c0615151d24b8ffc0400c0e
1148240d0d2a1d031c0012003f323f33392f332b11331133012f2b2b5ded322b12392f2b33ed11391133332b2b31305d2b5d2b2111343637363706070e01070323032e0327262716171e011511231133
131e0117161736373e013713331103a90102020111110e210ea489a6060e0f1008121301010101a0edb909170b0c0d0e0c0b1809b9e10364336a2c333137352d6426fe4a01b60f292f3118373c30342d
6933fc9c0545fe17174d262c312f2b254e1901eafabb0000000100a20000042a0545001300784024125c00180911480080119011d011e011043f1101c011d011026f117f118f11031111150ab8ffe840
2d0b11480a075c70080100081008a008b00804088c01017a010169010111010903830b01750b01660b010b000812003f33335d5d5d3f33335d5d5d012f5d71ed322b12392f5d5d7171332bed31302101
16171e0115112311330126272e01351133110344fdf804040305aade021005040405ac046a2b2d265927fc940545fb8e282e2762330360fabb00000000020066ffec0466055a0011001d00794056761d
861d02341d01761986190234190179178917023b170179138913023b1301561001451001460d560d0249075907024903590302005b5f12011240191c4800120112121f185b500a01100a010a155f0f04
1b5f0513003fed3fed012f5d71ed12392f5d2b71ed3130005d5d5d5d5d5d5d5d5d5d5d5d5d0114020e0123222e01023534123e0133320003100223220211101233321204664684bf787fc080404482bf
7cf70108c99e989c9c9e99a39402a9adfefab1595eb30105a7ad0102ac56fea5feaa010e0107fef9fef2fef2feec0118000200a20000045f0545000e0019005b403e6617017600860002490f590f690f
03005a7f0f8f0f020f40191c48000f010f0f1b14075a00081008a008b0080408065f1440091148141407135f09030712003f3fed12392f2bed012f5ded3212392f5d2b5ded5d5d3130005d01140e0223
2111231121321e020734262b011133323e02045f3b79b67bfee7bf01cc7dba7c3ec0a4a4f6fe52794f2603b1549b7848fdfe05453a6a955e7d81fded2b4a660000020066fe7d0471055a0020002c008b
4062352c752c852c03352875288528033a267a268a26033a227a228a2203691179118911034a045a0402461f561f02461c561c02055a140b0b005b215f21012140191c48002101142114212e275b5019
0110190119245f1e04052a5f141308605f0f010f002f5ded3fed333fed012f5d71ed1239392f2f5d2b7110ed322f10ed3130005d5d5d5d5d5d5d5d01140e02071e0133323637150e0123222e02272e03
3534123e01333200031002232202111012333212046633628d5a2986651c4017265b31558161461b70a871384482bf7cf70108c99e989c9c9e99a39402a993ebac6d167e72080586090d335f8a570b67
b2f99cad0102ac56fea5feaa010e0107fef9fef2fef2feec01180000000200a20000049905450011001b0097b9001afff0b30f004d10b8ffe040230f11004c8901010b000100200e1148001110111101
041001101003760b860b020b5a12b8ffc040361518487f128f120200120112121d16035a00041004a004b004040410025f1f167f16023f166f169f16ef1604161600155f0503040012003f323fed1239
2f5d71ed32012f5ded3212392f5d5d2bed5d11392f5d33322f38332b5d5d31302b002b21012311231121321e0215140e020701033429011121323e0203bdfe92eebf01e278b3773b2754825b0190f8fe
d0fef001185371451f023ffdc1054533618a573e7b674c0ffdab03cddffe2a28435a00000001004fffec0468055a003f00b04079343d016d2d014b1d5b1d026b1001600c01743e843e02743d01403d50
3d02863c01443c543c027637863702742784270204217421842103540b018907017a07018b02017902012a5a2929005a00130113131f41095a7f088f080208345a001f101f021f3413052f60242a2a24
040e5f05400950090209090513003f332f5d10ed3f332f10ed123939012f5dedd65ded1112392f5ded322fed3130005d5d5d5d5d5d5d5d5d5d5d5d5d015d5d5d5d5d01140e0223222427371e0333323e
0235342e02272e0535343e0233321e0217072e0323220e0215141e02171e0504683e7fc386eafefb24b90c30547a5548775530416a854432665e513d22487eac6474a3714414bc0b2b476444506c421d
355b79453770675942260172558f683ab8ae25375a41241b3a5a3f46583823110d1d27354b65436089572829527950213350361c21394e2d3c4b3120120e1f2a39526e000001004c0000048005450007
004a4031070e0104020e04015a0f023f028f029f02bf02cf02064f025f027f028f02cf02df02061002010202080900045f05030112003f3fed32111201392f5d5d71edc62b0118104de6313001112311
2135211502c5befe45043404a9fb5704a99c9c000001008effec043d05450019005e404465157515851503650f750f850f03005a80179017d017e017f017053f1701c017d017027f178f170217171b0d
5a8f0a9f0aaf0a03cf0a01000a100a020a180b03125f0513003fed3f33012f5d5d71ed12392f5d5d7171ed3130005d5d01140e0223222e0235113311141e0233323e02351133043d3a77b57a74ae733a
bf193d6950536f441dbe01ea89c27b383575bb87036dfcad618a582828599067034600000001000a000004c205450010008240550f200e1148290f390f022b0f01760386030254036403023603460302
2403010001520f090f5e1000141010000101520309035e0201140201030209021010011010127f028f0202020f020355090144090109000112003f33335d5d3f33012f5d11332f5d12391000c187052b
872bc48718102b872bc43130015d5d5d5d5d5d2b21230133011e0117161736373e0137013302c9c6fe07c90140101d0c0e0d0c0e0c1e10013ec90545fc7a2d612a302f2d3029622f0386000000010000
000004cc05450032014c401121080c004d021810004d02180b0c004c10b8ffe8b40b0c004c1ab8fff040101011004c311811004d31180b0c004c14b8fff0b40b0c004c23b8ffe0401c0b0f004c22180b
0f004c22100a004d01200b11004c00201011004c00b8ffe0b40d0e004c00b8ffe0b4090a004c12b8fff040111011004c12280d0e004c1228090a004c11b8ffe040130b11004c22093209020204090101
0152232b23b80107400c090114090123110152221a22b801074048091114090911000152312b315e320014323200120152141a145e131214131214132b091a031310320132323413092f224f225f2203
22221a3113032b1a0001241a011a1101031212003f17335d113311333f3312392f5d33012f11332f5d1217391000c187052b872bc48718102b872bc48718102b872bc400c18705182b872bc4005d5f5d
012b2b2b2b2b2b2b2b2b2b2b2b2b2b2b31302b2b2b2b2123032e0327262706070e030703230333131e0117161736373e03371333131e03171617323e0237133303fad068050e0f0f0711111211070f0f
0e056ad0d3bd6d080b0505041010070e0f0e066daf6d060e0f0e07101001060a0c0668bd018313343b3f1e464c4d461e3f3b3313fe7d0545fcbd417c31393345431d3d3c3717018ffe7116383b3d1d43
4644667a3603430000010024000004a90545000b00c8405c0909010b060109035903022901010401010302070001080b040b0602070901080a050a06030207020900010702015201080708015e020714
020207050a01520b0405040b5e0a05140a050b0a100a200a02020a020a03060009040804b8ffc040180a1c4804040d08400b1b48080009030604010804120a0103003f333f33121739012f2b11332f2b
12173939392f2f711000c187052b1000c187052b10c48718102b1000c187052b10c410c0c01087c0c0108708c008c0108708c008c03130015d5d005d5d5d0901330901230901230901330266014dcdfe
4d01dccdfe8afe8bcd01dcfe4dcd03440201fd7cfd3f023dfdc302c10284000000010024000004a80545000800964068340644065406035c07013b074b07020907190729070353050134054405020300
0510052005030c0507080e0104020e052f04010406015a0f023f028f029f02bf02cf02064f025f027f028f020410020102020a090603400050006000033f00010000010704030112003f3f3312392f5d
5d3333111201392f5d5d71ed39c65d322b0118104de63231305f5e5d5f5d5d5d5d5d005d01112311013309013302c4bcfe1ccd01760174cd0248fdb8024802fdfd9d0263000100490000048305450009
0075402689080169080157087708022908390849080304081408020801520302035e0708140708030704b8ffc0b30f144800b8ffc0401d090e48040700000704030b2f013f014f01030107045f050302
085f0112003fed323fed32012f5d1217392f2f2f2b2b1000c187052b872bc43130015d5d5d5d5d290135012135211501210483fbc60332fd1703c2fcce03618f041a9c8bfbe200000001019afe5703bd
05cc0007002f401c0207070905f27f008f00023f006f00020000010004f5010005f5001b003fed3fed012f5d5d5ded12392f3331300111211521112115019a0223fe91016ffe5707758bf9a18b000000
00010073ffec045a05cc0003003f401d8b00017a00013900690002000310030305850201360266027602030201b8fff0b70001010102000019003f3f012f5d38335d5d12392f38335d5d5d3130050133
0103a4fccfb203351405e0fa200000000001010efe57033105cc00070029401707f26002010202090400003000020004f5050001f5001b003fed3fed012f5d3312392f5ded3130013521112135211101
0e016ffe910223fe578b065f8bf88b000001008501ba0446054500060063402f85050179048904020504018c0001290069007900030001030610000610067006800604060608660276028602030203b8
fff0400f000301030001100102020000010403003f33332f325d012f5d38335d12392f5d381239335d5d1133335d5d313009022301330103acfeb7feba98017acb017c01ba0323fcdd038bfc75000000
0001fffbff2404d1ff840003000eb4030000bb01002fed012f2f3130073521150504d6dc606000000001019204b1033a05b40005001a400e74038403028004010401038c0093003fed012fcd5d31305d
01253533171502befed4c5e304b1e61def14000000020080ffec0488044e0032004100a5403f8a24017a1e8a1e02693d793d893d0325363536021320090c4806171617021d092d093d0903052d152d02
000301030330461b0c4f385f380238381543264727b8fff8402dc0270127273f470015011539511b1b332c0f261f26022f268f26ff2603262621502c10335012005107070c1216003f33332fed10ed3f
ed332f5d711112392fed012f5ded332f5d38ed1112392f5d3333ed322f5d3130005d5d5d2b5d5d5d5d25323637150e0123222e0227230e0323222635343e023f0135342e0223220e0207273e03333216
1511141625323e023d01070e03151416044d0f1e0e22432633492e1803061c4458724a9ea04f80a354ec1b37543832513a2506bc0a3764976bc8c92afdda537a5128be376f5938596f04037008081b37
513634543b20ac966b894e1f01043b435e3a1b0f27433311406b4e2bbbb1fe2e5051063f6074355904010f305b4c5264000200b3ffec044205cc00200034006140427a028a0202443354336433034b23
5b236b23033a0501351c01051f151f751f851f0400476021012121362b06144600138013021326501a1e1013000d153050060316003f33ed3f3f3f33ed012f5ded323212392f5ded3130005d5d5d5d5d
5d0110022322262723140e0207233e03351133111406070607333e0133321203342e0223220e0215141e0233323e020442cbbd74aa300202030301ae01020201b4010101010430a975c3c4ba1e3f5f42
426a4928284969413f6040210222fee4fee656621a372e2104092b3c482704edfe591d39161a17695ffeebfee170a167312d67a97c779e5f272e66a300010082ffec0438044e0025009d405264160164
1001791e891e02641a0144155415024b115b110275088508020b470a0a1c473f1b4f1b8f1b9f1baf1b05cf1bdf1bff1b03601b011b1b27134700000100701b01001b101b601b701b801bc01b061bb8ff
c0401c1b1e481b1b185021160e50051f0b7f0b8f0b030b401b1e480b0b0510003f332f2b5d10ed3fed332f2b5d71012f5ded12392f5d5d71ed322fed3130005d5d5d5d5d015d5d13343e0233321e0217
072e0123220e0215141e0233323637170e0323222e0282568db45f629a6f460dc01080765675471f1f4775556c8e11be0944729e6489bf7736021ea3d88035325776440e5b6937699b64649f6e3a6a6d
0c437c5e395697cd0002008affe6041905cc001f0033006b404b442254226422034b325b326b32033a016a0102350a010507150775078507040a041a047a048a040413462a005012019012a012021212
352047000601061a1512002f500b08102550000316003f33ed3f33ed3f3f012f5ded12392f5d713333ed3130005d5d5d5d5d5d250e01232202111021321617333426342635113311141e0217232e0335
01141e0233323e0235342e0223220e02035e31a279cabe018879a331020101b401020201ac01040302fde11d3d6043456b492525486a4540603f20ae695f0116011802365662052a322d0901a3fb1327
483c2b090424323a1a016c70a068313069a878749d602a2e66a3000000020085ffec0446044e0020002b00904065642401640301492959296929034924592402751d851d02771c871c02780e880e027a
0d8a0d024402540264020327471f1f0b49cf0adf0aff0a03000a100a600a030a0a2d2600470015e0150215005026260521501a10055010df0aef0a02800a010a0a1016003f332f5d5d10ed3fed12392f
ed012f5ded3212392f5d5ded322fed3130005d5d5d5d5d5d5d015d5d01141e0233323e0237170e0323222e0235343e0233321e021d0101220e0207212e030142264c714a375d4a320c9e10406a986873
b67e434f86af6083b57233fe252e65563b03024808344d6201f7558f6739192c3d242d2d5b492f488ed58d9cd482385497d4801801d21d4a7f625e7e4c2000000001008a0000044b05cc001f005eb900
0afff04036090c481d0146023f0501050506021e1e112f023f026f027f028f0205024014254800020102110211202117500c000004501d050f0115003f3f33ed323fed11120139392f2f5d2b5d11332f
1133332f5d10ed323130002b0111231121352135343e0233321e0217152e0323220e021d0121150244b4fefa01062d649f721d5050471512484f48134262401f01eb03acfc5403ac8e19658f5b2a0306
0804910306050314365e4b0a8e0000000002008ffe58041e044b00310045009c403e7a308a3002663f01450c550c650c034b3a5b3a6b3a03352101051d151d751d851d040a171a177a178a17042f4632
22500e01900ea00e020e0e1a47064605bafff00005ffc040280f154805053c47b01a01001ad01a021a280f3750221f1041500f151509500020063006020606001b003f322f5d10ed3f33ed3f33ed3f01
2f5d71ed332f2b38ed1112392f5d713333ed3130005d5d5d5d5d5d5d01222e0227371e0133323e023d01230e0323222e0235343e023332161733343e0237330e031511140613342e0223220e0215141e
0233323e0202675a89633d0db811715c395b412302133b536e4866905c2b2b5f986e71a72d0303030501ab01020201d421304f65354362401e1e3f614235665031fe582647623c194b51224b7856c228
4c3a234286ca8783cc8d4a6961193e372803092b3c4927fcc6e3e503cf719e632c2d639d71759c5e272b609b000100b90000041905cc001f003f4027700e800e02040d140d740d840d04114612122105
021e46001f801f021f1f11151850050b100000003f3f33ed3f33012f5ded323212392fed3130005d5d133311140607333e0333321e0215112311342e0223220e02151123b9b506030318405269405382
5a2fb5213b50303b674d2cb405ccfe7a32652e2f4c351d2c5c9065fd2f02b750673d182d557d51fd8d0000000002008f0000046505cc0009000d004840280707050404050b4a0c0c01010046a0050105
050e0f0b300a600a02800a010a0750080f0004500315003fed323fed2f5d712f111201392f71ed322f322fed11332f12392f3130252115213521112135212735331502e9017cfc2a01a6fec101f3c8c8
8e8e8e031e8ed2c0c000000000020075fe57034405cc0019001d004b402c75138513020a031a030218180a151a4a1d1d004600150115151e1f0a301a01801a011a1b1750180f1050051b003fed3fed2f
2f5d71012f1112392f5ded322fed1112392f3130005d5d05140e0223222e0227351e0333323e02351121352127353315034447769c5629524c4118163c484f2838614829fe69024bc8c81c6d975f2a07
0c0f098c050c0a071c3d5f4303c58ed2c0c00000000100ec0000046505cc000b00af402c66077607860703640974098409037b018b01024901590169010349085908026b007b008b000349005900020a
b8ffe840280b1148010a08091044095409020909009f0baf0bbf0b03000b100b020b100b0b0d07034600040104b8ffc0b31f254804b8ffc0b3161b4804b8ffc040110d11480402070a010701080f0500
040015003f323f3f393911331133012f2b2b2b5ded3211332f385d5d33332f5d3833393931302b5d5d5d005d5d015d005d2101071123113311013309010392fe9284b4b401dbd3fe4901ce01f362fe6f
05ccfc61020dfe2ffd970000000100860000045c05cc00090033401a08080504040501010046a0050105050a0b075008000004500315003fed323fed111201392f71ed322f11332f12392f3130252115
2135211121352102e0017cfc2a01a6fedf01d58e8e8e04b08e0000000001006300000464044e0039008eb9002affd0b3090f4820b8ffd04059090d4822394800000d2e48bf2fcf2fff2f03602f702f02
1f2f2f2f3f2f034f2f5f2f7f2f8f2f04102f202f302f032f2f3b190c48000d100d02f00d010f0d3f0d02cf0ddf0d02100d010d350650192228031f10130f2f0d0015003f32323f3f1733ed32012f5d5d
717172ed3212392f5d5d717171ed12392fed393130002b2b2111342e0223220e0215112311342e0227331e0315333e0333321617333e0333321e0215112311342e02232206151102130c1b2b20213426
14a9010202019501020201020d2433462e58571302122b38482f3d543417a80c1b2b20424d02ae4f6a411b335d8351fda10353224b43300705293538142747361f60642f49321a2c5c9065fd2f02ae4f
6a411ba8a8fd8d00000100b300000419044e0025003d402670228022020421142174218421042546000027190c46000d800d020d1906501f10130f0d0015003f323f3fed33012f5ded3212392fed3130
005d5d2111342e0223220e0215112311342e0227331e0315333e0333321e0215110364213b50303b674d2cb401020201aa01020302031841556a425380572d02b750673d182d557d51fd8d0353224b43
3007052c393b142f4c351d2c5c9065fd2f00000000020082ffec0449044e000f00230059403d6422014622562202641c01461c561c026b180149185918026b120149125912020047af10010010101060
10031010251a47000a010a15500d101f500516003fed3fed012f5ded12392f5d71ed3130005d5d5d5d5d5d5d5d01140e0223222e0235101233321203342e0223220e0215141e0233323e020449437eb5
726db17d44fde8f4eebd2b4d6a40416e4f2d2f4f6939416f502d021e8ed38c45448cd38f01170119feeafee67ea462272963a47b7ea562282762a600000200b3fe57044204500028003c00654044643b
01463b563b026b2b01492b592b020626162676268626043421013b08017903890302004760290129293e331e11460012801202122e501e2410180f111b38500a0516003f33ed3f3f3f33ed012f5ded32
3212392f5ded3130005d5d5d5d5d5d5d5d01140e0223222e0227231e0315112311342e0227331e0315333e0333321e0207342e0223220e0215141e0233323e020442285c966e37645643170501010101
b501020201af0103030304193f50643e6e965c28ba183b61493b684d2d25486a4449623b1902227bd09655142c4632021f2e3a1dfe590503284a3b2a090325343c1a344c31175093ce7d649c6b382561
ac87739d612a396ea20000000002008afe57041a044a00240038006640474b2d5b2d6b2d03443154316431033a1c013503010524152475248524040a201a207a208a2004124625055013019013a01302
13133a2f47002201223450191f16121b2a50050010003f32ed3f3f33ed012f5ded12392f5d713333ed3130005d5d5d5d5d5d01321e021733343e0237330e03151123113436373637230e032322021110
1201342e0223220e0215141e0233323e02021142664f3d180203030501af01020201b50101010102193f516541cabec30212284a694144613d1d1c3c60453b684e2d044a162e452f1939312303092844
6243fb3601b71a3a1a1e1c334e351a011901150119011dfde0779e5e283369a16e6a9f6a362763ab000100f200000415044e001f0046402b7806880602041a141a0204191419021f1f211609460a1010
1f0a6f0a7f0a8f0a040a1603521c10100f0915003f3f3fed33012f5d337c2f1810ed3212392f3130005d5d5d012e0123220e0215112311342e0227331e0317333e033332161704152f673e54865c31b4
0c101206ab070f0e0a02051c3e557452386c2a0396080b44759c58fe0402bd3874675218183e43441d3e6446260a0700000100a7ffec0424044b0038008540586c180179178917026c17016a36017331
83310247315731022437012a1d010a031a0302052015200228492727004815151f3a0b490a0a2f48001f601f701f801f041f2f15052d50222028902802282822101050050b0b0516003f332f10ed3f33
2f5d10ed123939012f5ded332fed1112392fed322fed3130005d5d5d5d5d5d5d5d5d5d01140e0223222e0227371e0333323e0235342e02272e0335343633321e0217072e03232215141e02171e030424
3b70a3695e97724d129f0c354b62393a63482933587947407e633dd4c94f8a6c490ea2072e43542ef52b4e6c414189714901354c7a552e1b3e674c1f3741220b11294130323d2a1e13112841644d93a6
1d3f6345142d38210c972e39261b11102a466d00000100befff003f90554001b005840366a187a188a18031828090e48060612121d0509461a9001010101021a401225481a400b0e48001a011a0c5017
1608005001030305010f003f33332f10ed323fed012f5d2b2b33332f5d10ed3212392f332f3130002b5d13353313331121152111141633323e0237150e032322263511beaa3a7801b0fe50556128554f
45181646576636a2a203ac8e011afee68efd744f4c070a0c058906100e0a8994029f0000000100b9ffec041f043a00250045402c7f228f22027b218b21020a211a212a2103391c01190e460b0b270146
0024802402241906501f1614150c000f003f323f3fed33012f5ded12392fed333130005d5d5d5d0111141e0233323e0235113311141e0217232e0335230e0323222e023511016d1735553f40674927b5
01020201aa01020302031a3e526a465a825427043afd524f6a411b2d557d510273fcad224b433007052c393b142f4c351d2c5c916402d1000001004500000487043a001000944042560401490e590e02
1b0f5b0f02090f012503550302060316030228016801020001520f090f4b1000141010000101520309034b020114020103020902701080100210b8ffc0401e0a0e4810101202400b0e48020f020f5509
014409012a093a090209000115003f33335d5d5d3f33012f2b11332f2b5d12391000c187052b872bc48718102b872bc43130015d5d5d5d5d5d5d21230133011e03173e0337013302cbd5fe4fca010c08
1214110606131415080114c9043afd40163f443f15153f423f1602c200010015000004b8043a002a01524010741e841e02461e561e02241e341e021db8ffe0404a131848901d01651d751d851d03241d
341d02061d161d021c201318489f1c016b1c7b1c8b1c031d1c2d1c3d1c030b1c017b1b8b1b021b200b0e480e200d11481a0e2a0e3a0e030b0e010db8ffe0401f131848900d01250d350d750d850d0401
201318489f01012a013a017a010300b8ffe040700d114815002500350003030600010e01521016104b0f0e140f0f0e0d01521c161c4c070d14070d0101521d231d4c07011407011d070001522923294b
2a00142a00230716030f142a742a842a032a2a2c0f2a66070132070102071c1c16292a10030f0f23167516851602160d01000e15003f333333335d11333f173312392f335f5d5d2f012f11332f5d1217
39872b872bc41000c18705182b872bc487182b872bc48718102b872bc43130015d5f5d2b5d5d2b5d5d2b5d5d2b2b5d5d5d5d5d2b5d5d5d5d2b5d5d5d2123032e0127262706070e010703230333131e03
1536373e01371333131e01171617343e0237133303facc8f0814090b09090b09140a94cbb7b25d030b0b080b0b09140787c1820613090b0a080c0c0365b001d71b4520252525241f471ffe2d043afda1
1855605c1d2d2923491401a8fe57144723292e1e5a5f5619025f00000001005e0000046d043a000b00b9407047040176078607023901790189010376080119085908020a0209070308060b0601020904
03080500050a010209020704030209015208030203084b090214090902000152060b064b050014050006050a01070404030b0909100b700b800b030b0b0d050503070a04010408050f030015003f323f
331739012f332f11332f5d332f111217391000c187052b872bc48718102b1000c187052b10c410c0c01087c0c0108708c008c0108708c008c03130015d5d005d5d015d21090123090133090133090103
a4febffebdc2019ffe73c7012c012ac9fe7301a401bcfe44022c020efe5b01a5fdf4fdd200010042fe570489043a001f00af40521e180f124865127512851203491d591d0203200d11482d033d03022b
023b020200480c49750085000226006600026a107a108a10034910591002001018691879188918031810090c48181f0808111e1f101fb8ffc0b70a11481f1f211211b8fff0400a11400b0f4811821801
18b8ffe0400d0d10481810101e110f0c50051b003fed3f33392f332b5d012f2b383311332f2b383312392f12392b5d1133335d5d5d5d3130002b5d5d2b015d5d2b210e0323222627351e013332363f01
0133011e03173e0337133302c1255062784e223a201330114f8b3012fe2ac001080b201e170203171e1f0cfcbe629d6f3b0407870303768131042ffd7e1b4f4d3e090b3d4b4f1d028100000000010093
0000042c043a0009005840370607160726075607040701520201024b06071406070206600601030608080603030b9f01af01bf010300010101060350040f0107500015003fed323fed32012f5d5d1217
392f2f2f5d1000c187052b872bc43130015d333501213521150121159302a9fd7f034cfd5602cf8903268b89fcda8b00000100e3fe57042505cc003100754012760f860f02690879088908030218090c
4814b8ffe04037090c4818803101313133202cf10525053f0c010c0c117f050100050105250bf50f0c4f0c6f0c038f0caf0c020c0c3019f5180030f5001b003fed3fed12392f5d71ed39012f5d5d3333
2f5d123910ed3212392f5d333130002b2b5d5d01222e023511342e0227353e033511343e0233211523220e021511140e0207151e03151114163b0115031c416c4e2b2f4e633333634e2f2b4e6c410109
d52d412a13294253292b534128505bd5fe572c506f44015f3f57361a0289021a37563e016046704f2a8b152e4a36fea6395c452c0a020a2d445d3afea56a598b00010213fe5702b905cc00030017400a
03ab000004050100001b003f3f111201392fed3130011133110213a6fe570775f88b0000000100a7fe5703e905cc00310073400d7622862202692979298929031cb8ffe0403d090d482f18090d480b30
25012525202cf111050533194f007f008f0003000001000b26f50f252f254f256f25048f25af250225250118f5190001f5001b003fed3fed12392f5d71ed39012f5d5d3312392f33ed32322f5d393130
002b2b5d5d13353332363511343e0237352e033511342e022b013521321e021511141e0217150e031511140e0223a7d55b512841522b29524229142a412dd50109416c4e2b2f4e633333634e2f2b4e6c
41fe578b596a015b3a5d442d0a020a2c455c39015a364a2e158b2a4f7046fea03e56371a0289021a36573ffea1446f502c0000000001006c0229046003270020005d401a16042604360466049604a604
0610300914004c11300914004c1eb8ffc040250914004c1f300914004c0d22101d011d18ad0d0040171c480040090c48001d0aad0f130113002f5ded33dd2b2b32ed012f5d10ce31302b002b2b2b5d01
321e02171e0333323637150e03232226272623220e0207353e010173294f4d4b251532333317457b341f3c3d4428459149815826413c381d328403270d141a0d07100e08322a95161f13082c1a2d0c17
20158f262e00000000020203fef602c9043a00030007002c401502049803000710070207070809000003069d070f03b8010e003f3fed11392f111201392f5d33ed3231300133132313152335021b9418
c4c6c202adfc490544c9c90000010085ffc304460563002900934060681088100244285428024a265a26021b0c49180d0d1306204721210646000510050205052b274600131013e013f0130413245018
1f217f218f210321211b180b0e20050100051005600570058005c00506050500500e180e180e0c0f1901190c002f2f5d1239392f2f10ed332f5d7111331133332f5d10ed012f5ded12392f5ded332fed
1112392f33ed323130005d5d5d01323e0237170e03071523352e0335343e02373533151e0317072e01232206151416027b385d482f09b6093a638c5a806ea46d363a70a368805685613d0db911837a9c
a1a8010d1c3651360c3e745c3e08c3c3085389b86e76bf8a5309b8b80737566f3f0e5b6cc9bdc6c90001003800000493055a0036009c406203180c0f480614011b6f22292d6f104f225f22027f0c8f0c
020f2a010c2a22222a0c0306006f003601363638326e060f0f06401724480006100602062c0e73291f0f013f0f6f0f9f0fcf0fdf0fef0f060f0f32257316222216070732730636360618003f332f10ed
323f332f10ed12392f5d7133ed32017c2f5d2b33182f10ed12397d2f5d18ed1217392f2f2f5d5d5d33ed3210ed3130005d2b250e032321353e033d0123353335343e0233321e0217320e020706072e01
2322061d0121152115140e0207213236370493092036513afc8f2b48331dbaba33689f6c4a7e654812010e181f11273216754e7a780198fe680f243c2c02583b390bf02b56442b9a163046664b8d8edc
5c9366371d3a563906080a050c103f40737dd88e8b3b604d3c1735350002009e00e1042f04730023003700ea409d49225922022d223d2202491d591d022d1d3d1d0249195919022d193d190246145614
02221432140246105610022210321002460b560b02220b320b024607560702220732070249025902022d023d02020321061e0c180f1515181e2104000e161612aa6f2e01102e012e2e003904202024aa
00001000800003000c060f031521181e1e21030604091b171f1f29b0401b601b023f1b011b0d050533b009002fed332f332f5d5ded332f33111217391133113311331133012f5ded332f331112392f5d
5ded322f33111739113311331133113331305d5d005d5d5d5d015d5d5d5d005d5d5d5d015d5d133436372737173e01333216173717071e01151406071707270e01232226270727372e0137141e023332
3e0235342e0223220e02b62925646863367f49477e36616860252b2c26646665367e4748803469666625299a2d4c663a39664d2c2c4d66393a664c2d02ac477f36646765272b2a26616960367f474780
3564696525292a26696966367f493a664c2c2c4c663a3a664d2c2c4d6600000000010034000004980545001600b3406f5d15014b15011915291539150352130103401301141324133413030000400050
00030f114f115f11030c040106061608100b0b120915160e0812090e132f120112140400085c110d4f095f097f098f090410090109091718070b600c040c030f60101400100c100c1008151203081200
3f3f331239392f2f11333310ed32113310ed32111201392f5d5d3333ed323239c45d322b0118104de4321112392f331112392f3331305f5e5d5d5d5d5f5d5d5d5d012115211521152111231121352137
213521013309013302fc0141fe81017ffe81b2fe83017d02fe810140fe65c70169016dc702a78e8990ff00010090898e029efd970269000000020213fe3902b905ae00030007002540110307ab000404
08090500050001041b0100003f3f1239392f2f111201392f33ed32313001113311031133110213a6a6a602a4030afcf6fb95030bfcf500000002009fff54042c05cc004d006101054073731083100269
56017560856002435f535f02435b535b02435a535a735a835a04433e533e026a157a158a15032a144a145a14032a514a515a516a51042a500150180d11482a48016c397c398c39032a3901051f151f02
0a241a2402054d154d025d1d2258494146534e0e494b0649053749224bb8ffc04040121948414b052222054b41042c1849004e104e024e4e632d49002c012c5313465d3c1d76468646026a4601461d00
325127202d802d022d2d270b510006060000003f322f10ed2f332f5d10ed1239395d5d113333113333012f5ded12392f5ded1117392f2f2f2f2b10ed10ed10ed12393910ed1139393130005d5d5d5d5d
5d2b5d5d5d5d5d5d5d5d5d5d5d01321e0217072e0323220615141e02171e0315140e02071e0315140e0223222e0227371e0333323e0235342e02272e0335343e02372e0335343601342e02270e031514
1e02173e0302784f8a6c490fa1082e43552e7d8132536d3b448b70461c375236314f371e3a70a46b5d9a754f13a10c374e63393a664d2c395f7b4141836841203b54352a4b3921d301e13558713d3a59
3c1f31526c3c365d442705cc1e3f6245142d3d251159472d402e200e102e496a4c2d5749370d15334051334975532c1b40684d1f37472910152d442f36493323100f2e496a492d5347350f0e2e405030
8b9bfccb304533230e03213445272e4230220f01182f46000002014f04c3037c057b000300070031401f03850000078510040104050190040f003f005f00af00bf00050040161e4800002f2b5d32ed32
012f5ded332fed3130013533152135331502d9a3fdd3a504c3b8b8b8b80000000003001f002604ad05cc0029003d0051011c40777b508b5002744a844a02764f864f02764b864b0274468446027b408b
4002794589450279418941020350135002034a134a020c121c122c1203031813182318036600760086000300c4150a20c61f0b1f701f801f020f1f1f1f022f153f15021540101448151f151f343e100d
11483ec3102a012a2a5348b8fff040570d114848c334300a400ab00ac00ad00a05300ab00ac00a030a0a05c91025c91a20201a7f108f10ef10ff1004501ab01ac01a03001a101a701a801a04101a101a
4d6943794389430343c83900664d764d864d034dc82fd0003fed5d3fed5d1239392f2f5d715d11332f10ed10ed332f5d71012fed2b11332f5ded2b1139392f2f2b5d5d5d113310ed3210ed5d3130005d
5d5d5d5d5d015d5d005d5d015d5d01141e0233323e0237170e0323222e0235343e0233321e0217072e0323220e020514020e0123222e01023534123e0133321e011207342e0223220e0215141e023332
3e02018e1c385438293f2f210c7313344a63435a85572b2b5682574363483110720a202e3c273a533519031f5b9ed57978d49f5c5c9ed47979d59e5b5c4d86b26665b2844d4d84b26566b2864d02fb48
7856301b2d381c232b513f2640729e5e629c6f3b243b4b27211a3226172e53764abbfeefb25555b20111bbbb0111b15656b1feefbba7ee98474798eea7a8ef98464698ef000200ce028b03fd0598002a
003b007ab90003ffe8b3090c4814b8ffd84037090d48222809114829180d11482928090c481e1e17e224060035013535003d0de30e0e2be30000100002001a30e42836e4050513242128b80116b40d0d
0ae413b80115003fed332f3f333312392fed10ed32012f5ded332fed1112392f5d3333ed322f3130002b2b2b2b2b13343e023f0135342623220607273e033332161511141633323637150e0123222627
230e0123222637141e0233323e023d01070e03ce3a638247bf5e56545c0b95083054794f99a61f2b0c1c09173817524b03042b9266798993122639273b5c41229a325a44280366455c381801042b5d59
444d0a2f523e238685fedd3a3c05035e0607554b4d59727d1c3226162d4451253304010d1e3500000002007f008d046003ac000800110050402b00eb0803eb04ec06eb0101130a09eb110ceb0dec0feb
000a010a0b020a010c090001020f03060603ef00ee003fe4393d2f17331133321133113301182f5dedfdedd4ed1112392fedfdedd4ed313025013501331509011521013501331509011503acfeae0152
b2feae0154fd6ffeb00150b1feb101518d016d3f01731ffe8cfe911d016d3f01731ffe8cfe911d000001007400b4045702f200050021401200aa05070002100202023f00010002ad03b3003ffdc65d01
2f5d10deed313025112135211103c6fcae03e3b401ac92fdc20000000004001f002604ad05cc001300270035003e00ec40297b268b2602742084200276258625027621862102741c841c027b168b1602
791b891b0279178917022fb8ffd840150b11483328090c4803261326020320132002342929b8fff0402629313b2bc42c283510353531c43670368036022c362c360a14100d114814c31000010000401e
b8fff040320d11481ec30a282c342ac83b3ac82d002d102d022c3b2d2d3b2c03236919791989190319c80f006623762386230323c805d0003fed5d3fed5d1217392f2f2f5d10ed10ed321133012fed2b
11332f5ded2b1139392f2f5d10ed322f383310ed3211393811333130005d5d2b2b5d5d015d5d005d5d015d5d0114020e0123222e01023534123e0133321e011207342e0223220e0215141e0233323e02
05032311231121321615140607130334262b011133323604ad5b9ed57978d49f5c5c9ed47979d59e5b5c4d86b26665b2844d4d84b26566b2864dfedec7a17f01338e976855dd9f5f51aab6505402f9bb
feefb25555b20111bbbb0111b15656b1feefbba7ee98474798eea7a8ef98464698effa0150feb0033f7e6f667b13fea202504548fed35500fffffffb05f204d1065212070042000006ce000000020147
03200385055a00130027004ab90011ffe8b3090c480db8ffe84026090c480718090c480318090c4814ac00001eac000a100a800a030a23af000501050519af0f04003fed332f5ded012f5ded332fed31
30002b2b2b2b01140e0223222e0235343e0233321e0207342e0223220e0215141e0233323e0203852d4e693b3b684e2e2e4e683b3b694e2d6d1b3041262541301c1c3041252641301b043d3b684d2d2d
4d683b3c684d2c2c4d683c2641311c1c3141262541311d1d31410000000200740000045704c3000b000f006a40370f0c040b0e0104020e040901aa060002010a03020210110dad400c020e0407050e2f
073f078f07038f079f07df07ef0704070004ad0905b8ffc0b313164805002f2b33ed32c65d712b0018104de62f1aed111201392f5f5e5d33ed32c62b0118104de6113332313001112311213521113311
21150135211502af93fe5801a89301a8fc1d03e302a8fe75018b91018afe7691fd5891910001013802330394058d00200053b55a026a020215b8ffd84028090e481f1f17e16f088f0802080800220ee0
0f0f001e1e0000100002000e0e0be414de011ee400dd003fed323fed332f012f5d332f11332fed1112392f5ded322f3130002b015d01273e0535342623220607273e0333321615140e04072117013a02
1b5864675234474a44580885062e4c6c43839136556763531601cb020233673d5f50474952323e4b494408335740257f703d625249494e2f6b0000000001013202270399058d0035007bb9002affe840
20090f480228090c48302d1622e12323082de11c4f165f1602161c161c0800e10fb8ffc04022090c480f0f3709e10008100802083015e416160c2822221fe428de0ce403090903df003f332f10ed3fed
332f1112392fed39012f5ded12392f2bed1239392f2f5d10ed11332fed1112393130002b2b01140623222e0227371e0133323635342e022b013533323e0235342623220607273e0333321e0215140607
151e0303999b8e5774492406880958554d532538431d3d391d3d32204a474454068707324f673a45684623565a344b3016031b74802740512b0d4345484c2d361d096d0d1f35283c4546410c3a56391c
213b4f2e4b6e1402052536440001019204b1033a05b40005001a400e7b028b02028003010300028c0593003fed012fcd5d31305d0135373315050192e3c5fed404b114ef1de6000000010090fe77043c
043a0027006840180b100f1248390701224605101f201f02601fc01fd01f031fb8ffc0402a2453481f1f29150d1146124027534812401c234812401215480012011220130f121b1a500c050916001500
3f3f3333ed3f3f33012f5d2b2b2bed323212392f2b5d7133ed3130005d2b212e0335230e0123222627231e01151123113311141e0233323e0235113311141e02170394010303030435a7725280200403
01b6b61b3f65494370522eb50102020106313e3f136d6e403a203f1bfe8b05c3fd7c4574552f345b7e4b0269fcaf224c433107000001007ffebc04380545001300414018059a00060106060c02131301
9a020215000c100c020c0413b80110b71207070112030601002f333f12392f10ed32012f5d12392fed322f1112392f5ded31300111231123112311222e0235343e0233211503b784c58550856035325f
8a57024704cbf9f1060ff9f103be2d58865854855d327a00ffff01f001a402db02cf120600d3000000010077fe4401e30000001b0052b9001affe8402f0f1348441a541a0203180d1348181919171640
090c481616081087000000080108139219400d10481919170b8d0595003fed2f392f2bed012f5d332fed11392f2b333311333130002b5d2b01140e0223222627351633323e02353426232a0107373307
1e0101e31e41684b142d1932242939220f3d480e1d0e416b275e5efefe2a45311a010362060d162013282a02b66403530001011d0233039b05820012003f40223f1101111110e002080801010e000210
02020202131408e40909030edc1001e400dd003fed323f33332fed111201392f5d33332f332f10ed322f5d3130013521110e032335323e023733113315012301090d374a562b305749381179ec02336b
02621a2a1e116d12233221fd1c6b0000000200e2028b03ea059800130021005240307a198a19027a158a15027521852102751c851c0200e39014a014c014031414231ae36f0a01000a100a500a030a17
e40fb80115b21fe405b80116003fed3fed012f5d71ed12392f5ded3130005d5d5d5d01140e0223222e0235343e0233321e0207342623220615141e0233323603ea316292625d9061333061926267935d
2c957872747d223d5533757f0412578f683939688f57548f683b3a688f55978688954c6d452085000002006b008d044c03ac000800110050402a03eb08ec0106eb0500eb0101130a0ceb10ec0a0feb0e
09eb0a110810070f090007080c03030306ef00ee003fe4393d2f17331133321133113301182fedd4ed10fded1112392fedd4ed10fded313025233509013533011501233509013533011502fab20152fe
b0b00152fcd3b40152feb0b2014f8d1d016f01741ffe8d3ffe931d016f01741ffe8d3f00ffff001b000004cd0545102700d4fef60000102600d10000110700d70198fdb6002840170302181803025019
014019011901601401140020000100115d35115d35115d5d3535003f35350000ffff0011000004b00545102700d4feec0000102600d1f000110700d501acfdb40018400d021718016014011400200001
00115d35115d35003f350000ffff000b000004cd0551102700d6ff210000102600d11900110700d70198fdb600204012030235180302503a01403a013a0040000100115d35115d5d3535003f35350000
00020099fedf046c04390027002b007740366606760686060366047604860403451b551b02690079008900030a0928982b2b001e98001d701d801d031d1d2d139800001000020009b8ffc040150f1348
0909232a9d2b692379238923031d1d189e23b8010e003fed332f5d2fed11392f2b012f5ded12392f5ded12392fed33325d3130005d5d5d37343e0637330e0715141e0233323e0237170e0323222e0201
15233599253e4f524f3f2701af02273e4e504d3c25294d6d44446f523207b80c4a7eaf716cb17e440271c34f456852423b3b445337436651413b3b4453353754391e2647653e0c5a976c3d305d890444
c9c90000ffff0000000004cc06a712260024000011070043ffa300f30015b40215052602b8ffa3b41619040725012b35002b3500ffff0000000004cc06a812260024000011070074005d00f40013400b
02150526025d1518040725012b35002b35000000ffff0000000004cc06a8122600240000110700c3000000f40013400b0216052602001b15040725012b35002b35000000ffff0000000004cc069d1226
00240000110700c5000000f40013400b0215052602002030040725012b35002b35000000ffff0000000004cc065d12260024000011070069000000e20017400d03021505260302001917040725012b35
35002b3535000000ffff0000000004cc0660122600240000110600c403550016400c030238030302031f15040725012b3535003f3535000000020001000004ab0545000f0018008540510b042b049b04
031701020401520211025e03041403040203090509050d080c5c180f0f030d0d1a03015f170b5f080f083f087f088f08046f088f089f08bf08df0805170817080c11075f04030c5f030f12003f33ed3f
ed321239392f2f717210ed10ed012f12392f12392f33ed321239392f2f1000c187052b872bc410c0c03130015d0121032301211521112115211121152111230e030703210286feb18ea801e6029bfeaf
0147feb9017afddb4904141515068b011c019cfe64054598fe5296fe2f9804b21042484412fe6f00ffff0071fe440476055a1226002600001107007801620000000bb6011b322a150b25012b35000000
ffff00a20000046206a812260028000011070043ffb900f40015b4010c052601b8ff9db40d10000a25012b35002b3500ffff00a20000046206a812260028000011070074005b00f40013400b010c0526
013f0c0f000a25012b35002b35000000ffff00a20000046206a8122600280000110700c3000000f40015b4010d052601b8ffe4b4120c000a25012b35002b3500ffff00a200000462065d122600280000
11070069001400e20019b602010c05260201b8fff8b4100e000a25012b3535002b353500ffff00ca0000040106a81226002c000011070043ffaf00f40015b4010c052601b8ffb0b40d10000125012b35
002b3500ffff00ca0000040106a81226002c000011070074006700f40013400b010c052601670c0f000125012b35002b35000000ffff00ca0000040106a81226002c0000110700c3000000f40013400b
010d05260100120c000125012b35002b35000000ffff00ca00000401065d1226002c000011070069000000e20017400d02010c0526020100100e000125012b3535002b35350000000002001b00000465
05450010001f008e406272168216020230166016021416241602791a891a027f198f19021b192b193b196b1904111106005a0017011717211e135a060a06080800061006020612085f1e1f094f09023f
096f099f09cf09df09ef090609401f28480909131d5f0b03135f0612003fed3fed12392f2b5d7133ed32012f5d332f113310ed3212392f5ded12392f3130005d5d5d5d5d5f5d0114020e012321112335
331121321e0201211133321211342e022b01112104655098dd8cfe8e878701379ef3a655fe1dfedfa2d4ce3872ac7579012102b0a8feffae59025595025b4da2fafef9fe47010a010a87c07939fe4100
ffff00a20000042a069e122600310000110700c5000000f50013400b0114052601001f2f081225012b35002b35000000ffff0066ffec046606a812260032000011070043ffaa00f40015b4021e052602
b8ffaab41f220a0025012b35002b3500ffff0066ffec046606a812260032000011070074006200f40013400b021e052602621e210a0025012b35002b35000000ffff0066ffec046606a8122600320000
110700c3000000f40013400b021f05260200241e0a0025012b35002b35000000ffff0066ffec0466069d122600320000110700c5000000f40013400b021e0526020029390a0025012b35002b35000000
ffff0066ffec0466065d12260032000011070069000300e20017400d03021e052603020222200a0025012b3535002b35350000000001009d00e1042e0473000b008e401db902c902d90203b900c900d9
0003b608c608d60803b606c606d6060307b8fff0400e12164801101216480a1012164804b8fff0b312164805b8fff0b612164819050103b8fff0402612164819030109101216481609010b1012164816
0b0104800a014f0a01000a100a020a0701b200193f33012f5d5d5d333130005d2b5d2b5d2b5d2b2b2b012b2b5d5d5d5d1309013709011709010709019d0162fea068015e015e69fea2016066fe9ffe9c
014a0162016067fe9f015f69fea4fea0690161fe9d00000000030038ffda04920570001a0022002b00c5408c8b1d01651975198519036a0c7a0c8a0c03770b0167258725024614561402461156110249
07590702490359030234227422842203761f861f027b1e8b1e02361e017426013926013b2b7b2b8b2b031d261e2504231b1815080b040e17005b5f1b011b40191c48001b011b1b2d235b0a500e01100e
010e1e251d260420295f080b18150405161304205f090513003f33ed3f33121739ed111739012f5d7133ed12392f5d2b71ed32111739111217393130005d5d5d5d5d5d5d5d5d5d5d5d5d015d5d005d01
14020e01232226270727372e013534123e013332173717071e010734270116333212011417012e0123220204664684bf786fac3f755e8d302f4482bf7cd980745e8c3030c91efe004f98a394fd921e02
0027744b9c9c02a9adfefab15946449c41bd58e891ad0102ac56869c41bb55e492a472fd528a0118010aa67402ac4241fef90000ffff008effec043d06a812260038000011070043ff9f00f40015b401
1a052601b8ffa0b41b1e0a0025012b35002b3500ffff008effec043d06a812260038000011070074005600f40013400b011a052601561a1d0a0025012b35002b35000000ffff008effec043d06a81226
00380000110700c3000000f40013400b011b05260100201a0a0025012b35002b35000000ffff008effec043d065d12260038000011070069000000e20017400d02011a05260201001e1c0a0025012b35
35002b3535000000ffff0024000004a806a81226003c000011070074006500f40013400b010905260165090c040825012b35002b35000000000200a20000045f05450010001b00634042760f860f0265
1a751a851a036a127a128a1203005a7f118f11021140191c4800110111111d160b075a00081008a008b008040816600615600b060b060b0709030712003f3f1239392f2f10ed10ed012f5ded32321239
2f5d2b5ded3130005d5d5d01140e022321152311331521321e020734262b011133323e02045f3b79b67bfee7bfbf010d7dba7c3ec0a4a4f6fe52794f2602b4589f7847fe0545eb3c6f9c638695fdc02c
4f6b00000001008effe3048f05cc004300bd40297c3a8c3a027538853802654201664101443f543f743f843f04652f01652e752e852e030328090c4833b8ffe84025090c48391f0136481f263d363d02
3d4818401850189018a018041f091818091f032a004811b8ffc0b312174811b8ffc04029090e481111452a462b40121548002b012b113d1f0305245031002b156009700980090309090e500516003fed
332f5d3f3fed121739012f5d2bed12392f2b2bed1217392f2f2f5d10ed5d10ed5d3130002b2b5d5d5d5d5d015d005d01140e022322262f011e0333323635342e0435343e0435342e0223220e02151123
11343e0233321e0215140e0415141e04048f2957875f509437021a424a4e255c6236515f513621313a3121213f5a39446b4927b43f78ae6e669b693521333a332137525f52370127427658341918a40f
1a120a5f4f3b523f39475e4633493a30343f2b253e2d1a234d7a58fc03040377ac70362e506d3e3b574334313220283d3a40546fffff0080ffec048805b4122600440000110600438e000015b4024211
2602b8ff70b44346150325012b35002b35000000ffff0080ffec048805b4122600440000110600744d000013400b02421126022f4245150325012b35002b3500ffff0080ffec048805b4122600440000
110600c3ee000019b70243112602204801b8ffd0b44842150325012b5d35002b35000000ffff0080ffec048805a9122600440000110600c5f8000022400e0242112602304d01204d01104d01b8ffdab4
4d5d150325012b5d5d5d35002b350000ffff0080ffec0488057b12260044000011060069f000001e400a03024211260302204601b8ffd2b44644150325012b5d3535002b35350000ffff0080ffec0488
060b122600440000110600c4f8000017400d03024711260302124c42272f25012b3535002b35350000030014ffec04b8044e00410050005900c640817c588c58027c548c54026a137a138a13036c117c
118c1103053d153d02053715370203211321021b28090e4814385600494b244b09490a0a404857cf4bdf4b021057014b574b571e5b2f48303042484f1e5f1e9f1eaf1e041e512a50350050564b512456
2456243b38352f2f8f2f022f2f351006455019140f1980090109091916003f332f5d11333310ed323f332f5d11333339392f2f10ed10ed10ed32012f5ded332fed111239392f2f5d5d10ed322fed1133
10ed3239393130002b5d5d5d5d5d5d5d01151e0333323637170e0323222e02270e0323222e0235343e023f0135342e0223220e0207273e03333216173e0133321e021d0105141633323e023d01070e03
01220e0207212e0102a701152c46314b59108a0c2b496d4d395c4835121536495c3a44613f1e395d763ea70f2134242033261704a808284a71505b79232d7d4c60814e20fc053939385134197a254a3a
25029e1c3e3323020169095801f711528862375e482d2d5b492f1837594031553e242c54774b6b894e1f01043b435e3a1b0f27433311406b4e2b46454a41589bd27a18cc52643f6074355904010f305b
02521d4a7f62ab9dffff0082fe440438044e1226004600001107007801510000000bb601212e26001c25012b35000000ffff0085ffec044605b412260048000011060043b1000015b4022c112602b8ff
b2b42d30151f25012b35002b35000000ffff0085ffec044605b4122600480000110600746f000013400b022c1126026f2c2f151f25012b35002b3500ffff0085ffec044605b4122600480000110600c3
07000013400b022d11260207322c151f25012b35002b3500ffff0085ffec0446057b1226004800001106006908000017400d03022c1126030208302e151f25012b3535002b353500ffff008f00000465
05b4122600c0000011060043bd00001e400b010a112601600b01500b01b8ffa9b40b0e030125012b5d5d35002b350000ffff008f0000046505b4122600c000001106007468000013400b010a11260154
0a0d030125012b35002b3500ffff008f0000046505b4122600c00000110600c30a000022400e010b112601601001501001301001b8fff6b4100a030125012b5d5d5d35002b350000ffff008f00000465
057b122600c00000110600692900002f401f02010a11260201900e01800e01700e01600e01500e01400e01000e0c060925012b5d5d5d5d5d5d3535002b3535000002007dffec044e05d80025003700bb
408145365536653603453155316531034a2e5a2e6a2e034a285a286a28032b043b046b0403052415240213100609041208080c120c1221174703702601a02601002610266026032626393047ff2101ff
210100210121651075108510036a067a068a0603100913060407112b500003000000100002070007000c33501c161211110c00003f332f333fed1139392f2f5d113310ed111217395d5d012f5d5d71ed
12392f5d717233ed1139392f2f332f1217393130005d5d5d5d5d5d013216172e01270527372e0127331e011725170716121d01140e0223222e0235343e0201342e0223220e0215141633323e02026447
752b27694cfefc37da387842d12d532a010837d3a4a73978bc847db576383474ba01b22147715051734921909151764b2403d3201b599f45736e5e2d4e2314331f726c5c95fe7df40679c48a4b4984b9
6f6bb6864bfe0e5e895a2c2d5c885cbeb22c5a8bffff00b30000041905a9122600510000110600c50a000013400b0126112601093141132425012b35002b3500ffff0082ffec044905b4122600520000
11060043ba000015b40224112602b8ffbbb425280a0025012b35002b35000000ffff0082ffec044905b41226005200001106007465000013400b02241126026524270a0025012b35002b3500ffff0082
ffec044905b4122600520000110600c300000013400b0225112602002a240a0025012b35002b3500ffff0082ffec044905a9122600520000110600c500000013400b0224112602002f3f0a0025012b35
002b3500ffff0082ffec0449057b1226005200001106006900000017400d030224112603020028260a0025012b3535002b3535000003007400df0457047500030007000b0063403c060e0b05080e7005
0105030bab003f084f08020008010a0308080d0c09ae40080e0401050e00ae2f013f014f01034f018f019f01ef01040104ad05b3003fedd65d71ed2b0018104df61aed111201392f5f5e5d5d33ed32c4
5d2b0118104de431300135331501352115013533150211a8fdbb03e3fdbaa803beb7b7fea29292fe7fb7b7000003006effec045d044e00190025003100db4024452455246524036b30014a305a30020c
200b1148790789070207400b0f48741984190219b8ffc040090b0f48741484140214b8ffc040670b0f481d2a1e2904261a1815080b040e17170047201a301a401a701a801a901a06001a501aa01ab01a
c01a05001a101a601a701a801ae01a061a1a3326470e0a0a0e40282e480e40181b480e2a1d291e04212d080b181504130516162d50131021500509090516003f332f10ed3fed332f1112173911121739
012f2b2b332f10ed12392f5d7172ed322f111739111217393130002b5d012b5d002b5d012b005d5d5d01140e02232226270727372e0135343e023332173717071607342627011e0133323e0225141617
012e0123220e02045d4683bc7765a6404d4c5430334381bc79d57a484e5061bd1110fe032b724045765630fd8b111101fd2a714545755530021e8ed38c45353657445f45bc7a8cd18d466452445a89f7
4c782efdc0352a2762a67e4b752e024032272963a4000000ffff00b9ffec041f05b412260058000011060043a2000015b40126112601b8ff9cb4272a241325012b35002b35000000ffff00b9ffec041f
05b4122600580000110600745a000013400b0126112601542629241325012b35002b3500ffff00b9ffec041f05b4122600580000110600c300000015b40127112601b8fffab42c26241325012b35002b
35000000ffff00b9ffec041f057b1226005800001106006900000019b602012611260201b8fffab42a28241325012b3535002b3535000000ffff0042fe57048905b41226005c0000110600746b000013
400b01201126016b2023111f25012b35002b3500000200b9fe57044205cc0013003800654044792189210264120146125612026b020149025902023516013a2601061b161b761b861b041e4760000100
003a320a2f4600308030023031002f1b280f5023161405501910003fed333fed333f3f012f5ded323212392f5ded3130005d5d5d5d5d5d5d5d01342e0223220e0215141e0233323e02013e0333321e02
15140e0223222e0227231e0315112311331114060706070388183b61493b684d2d25486a4449623b19fde8193f50643e6e965c28285c966e37645643170501010101b5b4010101010222649c6b382561
ac87739d612a396ea201ce344c31175093ce7d7bd09655142c4632021f2e3a1dfe590775fe591d39161a1700ffff0042fe570489057b1226005c00001106006905000017400d02012011260201052422
111f25012b3535002b3535000001008f00000465043a00090033401a07070504040501010046a0050105050b0a0750080f0004500315003fed323fed111201392f71ed322f11332f12392f3130252115
2135211121352102e9017cfc2a01a6fec101f38e8e8e031e8e00000000020014fffb04c5054b0017002800744049762686260246095609024904590402120e120e1611155c1b1b0616162a245c06145f
0f113f117f118f11046f118f119f11bf11df110511110e18165f17030317121f0f5f0e0b0b0e03003f332f10ed323f332f10ed3211392f7172ed012fed12392f12392fed321239392f2f3130005d5d5d
210e012322021134123e013332172115211121152111211525323637112e0123220e0215141e02026117311affec3d7bb97c3a33022efe72016ffe9101b7fd3b163b13133b154e754e27284f74020301
52015cb40100a24c0698fe4896fe39988c0204042103033a7fc88f8ecc843f000003001cffec04b4044e0026003a004300aa40737b428b42027a3e8a3e027638863802792a892a026603760386030364
3374338433036b2f7b2f8b2f030522152202051a151a020a151a1502101d4000482727182508490909254810410141414531484f185f189f18af18041800504040363b2c50201d1b1005365013100e13
80080108081316003f332f5d11333310ed323f3333ed3212392fed012f5ded12392f5ded322fed1112392fed3239393130005d5d5d5d5d5d5d5d5d5d01141e0233323637170e032322270e0123222e02
3510123332173e0133321e021d0125342e0223220e0215141e0233323e0201220e0207212e0102c81329402c414e0e8a0b28446648b6512a855b4b78552daaa2bb4f2d85515b7b4a1ffd6b182c3f2727
412f191b2e3e2328412e19014c19372f20020144085001f7558f67395e482d2d5b492fb75d5a448cd38f01170119b15e53589bd27a18277ea462272963a47b7ea562282762a602291d4a7f62ab9d0000
0001011b04b103b105b4000900254017200150016001800104010603200b0f48038e098c050293003f33fded2b012fc45d31300115232723072335373303b169db02e868fea404c5148a8a14ef000000
00020174047c0358060b00130027008bb90011ffe8b3090f480db8ffe84061090f480718090f480318090f48148400001000200003300070008000b000c000d000066000010f001f0002001e84000a01
0a198e7f0f8f0f020f40090c480f238e2f054f056f05df05040f052f053f054f056f057f059f05bf0508054016194805002f2b5d71edd42b5ded012f5dedd45d5d7172ed3130002b2b2b2b01140e0223
222e0235343e0233321e0207342e0223220e0215141e0233323e0203581e3c5b3d3d5b3c1e1e3c5b3d3d5b3c1e790d1d2e21212e1e0d0d1e2e21212e1d0d054427483821213848272748372121374827
0f1d180f0f181d0f0f1e180f0f181e00000100ff04b103cc05a9001f005140100a1f1a1f021e18090f48050f150f020eb8ffe84024090f481a830f1b2f1b3f1b7f1b8f1b051b1b0a83000b300b020b1a
058f108c158f0a0093003f32edfded33012f5ded332f5ded3130002b5d2b5d01222e0223220e0207233e0333321e0233323e0237330e0303022a54504d23171d130a056f05142d4a3b2c54504a21171e
140a047005142b4a04b1252d25101e2c1d2d58472c252d25101f2c1c2c58472d000100aa01c30422024c0003000fb5020000b901bc003fed012f2f313013352115aa037801c389890001fff601c304d6
024c0003000fb5020000b901bc003fed012f2f3130033521150a04e001c389890001018d035e033f05cc0003002ab5060116010201b8fff040110e1148039a0097019900020102019c0000003fed012f
5dedfded31302b5d01032101033fa9fef7013705ccfd92026e0000000001018f035e034105cc00030033402200180e11480900190002090219020202100e114801990297039a00000100039c0100003f
ed012f5dedfded31302b5d5d2b01132101018fa90109fec9035e026efd9200000001015dfe95030f01030003003b402800180e11480900190002090219020202100e114801990297039a2f0001000010
000200009c019b040010f6ed012f5d5dedfded31302b5d5d2b01132101015da90109fec9fe95026efd920000000200b1035e041a05cc000300070057b5060516050205b8fff040090e11480601160102
01b8fff040260e1148079a049705992f069f0602000610060206039a009701990002100202020507019c0300003fed3332012f5dedfded2f5d5dedfded31302b5d2b5d01032101210321010263a9fef7
01370232a9fef7013705ccfd92026efd92026e00000200b2035e041b05cc000300070068404b04180e11480904190402090619060206100e114800180e11480900190002090219020202100e11480599
0697079a000410049004030401990297039a2f00010000100002000705039c0100003fed3332012f5d5dedfded2f5dedfded31302b5d5d2b2b5d5d2b01132101211321010269a90109fec9fdcea90109
fec9035e026efd92026efd92000200b2fe94041b010200030007006a404c04180e11480904190402090619060206100e114800180e11480900190002090219020202100e114805990697079a00041004
9004030401990297039a2f000100001000020004009c05019b080010f432ed32012f5d5dedfded2f5dedfded31302b5d5d2b2b5d5d2b01132101211321010269a90109fec9fdcea90109fec9fe94026e
fd92026efd920000000101500191037b03bc00130037402515112511351103150d250d350d031a072a073a07031a032a033a03030000100a010a05050f002f332f012f5d332f3130005d5d5d5d01140e
0223222e0235343e0233321e02037b2c4d663a39634b2b2b4b63393a664d2c02aa3a664c2d2d4c663a39644a2b2b4a640001015a008d035d03ac0008002e401800eb0803eb04ec06eb4f010100010101
0201060603ef00ee003fe4393d2f333301182f5d5dedfdedd4ed313025013501331509011502aafeb00150b1feb101518d016d3f01731ffe8cfe911d0001016e008d037103ac00080030401a03eb08ec
0106eb0500eb00016001d00103010807030306ef00ee003fe4393d2f333301182f5dedd4ed10fded31302523350901353301150220b20152feb0b001518d1d016f01741ffe8d3f00000100cc000003fe
054500030027b70203100303050001b8fff0400a00011001020102060018003f3f012f5d383312392f383331302123013301427602bf7305450000000001005bffec0463055a003c0096405569310137
0347035703032b2a2a0c350000390b0c0c1c3e330101396e1c2000170117171c2216161c400b0e481c011773183b183620732133216f18013f210118211821062e73252b2b2507067311300b010b0b11
19003f332f5d10ed3f332f10ed1239392f2f5d5d113310ed32113310ed32012f2b332f3311332f5d3210ed322f331112392f3312392f33113311333130005d5d01211e0333323e0237170e0323222e02
272335332e01353436372335333e0133321e0217072e0123220e02072115210e01151417210369fe35082a4d745034574b421e4c2a58606c3e78b27b470d83770202020277811dfbe538645d572b523a
8a56497253330b01cdfe2501010301da01cf4f7a542b121e2513871c2e2012447db26d901722121a23128eeaec0e1c2c1e8c2a3a1c477a5f8d0e2b152a200000000101f001a402db02cf000300284018
039600000100000405019b0f001f003f000300400e114800002f2b5ded111201392f5ded31300111331101f0eb01a4012bfed50000010125024d02c7054500120044401c111110e002080801010e0002
1002020202131408e40009010909030eb80111b31001e400b80112003fed323f33332f5ded111201392f5d33332f332f10ed322f3130013533110e032335323e0237331133150149870d2f332f0d1230
332f117974024d6b02011a24170a770c1b2c21fd736b0000000100fa024d030405510020004fb90015ffd840200911481f1f17e1080800220ee00f0f001e1e0000100002005f0e010e0e0be414b80113
b3011ee400b80112003fed323fed332f5d012f5d332f11332fed1112392fed322f3130002b13353e0535342623220607273e0333321615140e04072115fa184a5454442a333c3d3b08820627415c3d78
792e4756524410017c024d613755483f40472c39403e3d052f503a21706737594b413f41266b0000000100ea023b02ed05510033008ab9002affd840400911480328091148001c101c201c037b300122
e123230a2de11c181c181c0a00e1110be10a1111354f0a01000a100a020a30181716160e285f220122221fe428b8011340090ee405500b010b0b05b80114003f332f5d10ed3fed332f5d1112392fc411
39012f5d5d12392f10ed10ed1239392f2f10ed11332fed005d015d3130002b2b01140e0223222e0227371e0133323635342e022b013533323635342623220607273e0333321e0215140607151e0102ed
27445e38405c3e22068809413036421929361d2925444036292d420687072c4455303a573a1d4345524b031a3653391d253c4c270d3c3d3c41252f1a0a6d394235323f390c3650361a1f374a2a42551a
02126400000200c5024b03350545000a0017005d401209090801e012066002700280020302021911b80117402004400d11480004100402040004e408050f111f112f119f11bf11051111010b06ba0111
00010112003f3f3312392f5d3333ed32012f5d2bed12392f5d3333ed32322f31300115233521350133113315030e03070321113c023602bb7ffe8901698d7af80615181707b901090102eca1a16301f6
fe0c6501d9091f22200aff0001040d201f1b00000000001e016e00010000000000000035006c0001000000000001000700b20001000000000002000700ca0001000000000003001200f8000100000000
00040007011b0001000000000005000c013d00010000000000060007015a00010000000000070050020400010000000000080014027f0001000000000009000e02b2000100000000000a016305890001
00000000000b001c0727000100000000000c002e07a2000100000000000d0035083d000100000000000e001a08a90003000104090000006a00000003000104090001000e00a20003000104090002000e
00ba0003000104090003002400d20003000104090004000e010b0003000104090005001801230003000104090006000e014a000300010409000700a00162000300010409000800280255000300010409
0009001c0294000300010409000a02c602c1000300010409000b003806ed000300010409000c005c0744000300010409000d006a07d1000300010409000e00340873004400690067006900740069007a
006500640020006400610074006100200063006f007000790072006900670068007400200028006300290020003200300031003000200047006f006f0067006c006500200043006f00720070006f0072
006100740069006f006e002e00004469676974697a6564206461746120636f7079726967687420286329203230313020476f6f676c6520436f72706f726174696f6e2e000043006f007500730069006e
00650000436f7573696e65000052006500670075006c006100720000526567756c6172000041007300630065006e0064006500720020002d00200043006f007500730069006e00650000417363656e64
6572202d20436f7573696e65000043006f007500730069006e00650000436f7573696e650000560065007200730069006f006e00200031002e00300030000056657273696f6e20312e3030000043006f
007500730069006e00650000436f7573696e65000043006f007500730069006e006500200069007300200061002000740072006100640065006d00610072006b0020006f006600200047006f006f0067
006c006500200061006e00640020006d006100790020006200650020007200650067006900730074006500720065006400200069006e0020006300650072007400610069006e0020006a007500720069
007300640069006300740069006f006e0073002e0000436f7573696e6520697320612074726164656d61726b206f6620476f6f676c6520616e64206d6179206265207265676973746572656420696e20
6365727461696e206a7572697364696374696f6e732e000041007300630065006e00640065007200200043006f00720070006f0072006100740069006f006e0000417363656e64657220436f72706f72
6174696f6e00005300740065007600650020004d00610074007400650073006f006e00005374657665204d61747465736f6e000043006f007500730069006e0065002000770061007300200064006500
7300690067006e006500640020006200790020005300740065007600650020004d00610074007400650073006f006e00200061007300200061006e00200069006e006e006f0076006100740069007600
65002c002000720065006600720065007300680069006e0067002000730061006e0073002000730065007200690066002000640065007300690067006e00200074006800610074002000690073002000
6d006500740072006900630061006c006c007900200063006f006d00700061007400690062006c00650020007700690074006800200043006f007500720069006500720020004e006500772122002e00
200043006f007500730069006e00650020006f0066006600650072007300200069006d00700072006f0076006500640020006f006e002d00730063007200650065006e00200072006500610064006100
620069006c006900740079002000630068006100720061006300740065007200690073007400690063007300200061006e00640020007400680065002000700061006e002d004500750072006f007000
650061006e002000570047004c0020006300680061007200610063007400650072002000730065007400200061006e006400200073006f006c00760065007300200074006800650020006e0065006500
6400730020006f006600200064006500760065006c006f00700065007200730020006c006f006f006b0069006e006700200066006f0072002000770069006400740068002d0063006f006d0070006100
7400690062006c006500200066006f006e0074007300200074006f0020006100640064007200650073007300200064006f00630075006d0065006e007400200070006f00720074006100620069006c00
69007400790020006100630072006f0073007300200070006c006100740066006f0072006d0073002e0000436f7573696e65207761732064657369676e6564206279205374657665204d61747465736f
6e20617320616e20696e6e6f7661746976652c2072656672657368696e672073616e732073657269662064657369676e2074686174206973206d6574726963616c6c7920636f6d70617469626c652077
69746820436f7572696572204e6577aa2e20436f7573696e65206f666665727320696d70726f766564206f6e2d73637265656e20726561646162696c6974792063686172616374657269737469637320
616e64207468652070616e2d4575726f7065616e2057474c206368617261637465722073657420616e6420736f6c76657320746865206e65656473206f6620646576656c6f70657273206c6f6f6b696e
6720666f722077696474682d636f6d70617469626c6520666f6e747320746f206164647265737320646f63756d656e7420706f72746162696c697479206163726f737320706c6174666f726d732e0000
68007400740070003a002f002f007700770077002e0061007300630065006e0064006500720063006f00720070002e0063006f006d002f0000687474703a2f2f7777772e617363656e646572636f7270
2e636f6d2f000068007400740070003a002f002f007700770077002e0061007300630065006e0064006500720063006f00720070002e0063006f006d002f007400790070006500640065007300690067
006e006500720073002e00680074006d006c0000687474703a2f2f7777772e617363656e646572636f72702e636f6d2f7479706564657369676e6572732e68746d6c00004c006900630065006e007300
65006400200075006e0064006500720020007400680065002000530049004c0020004f00700065006e00200046006f006e00740020004c006900630065006e00730065002c0020005600650072007300
69006f006e00200031002e003100004c6963656e73656420756e646572207468652053494c204f70656e20466f6e74204c6963656e73652c2056657273696f6e20312e31000068007400740070003a00
2f002f0073006300720069007000740073002e00730069006c002e006f00720067002f004f0046004c0000687474703a2f2f736372697074732e73696c2e6f72672f4f464c0000000002000000000000
fe230054000000010000000000000000000000000000000000d80000000100020003000400050006000700080009000a000b000c000d000e000f0010001100120013001400150016001700180019001a
001b001c001d001e001f0020002100220023002400250026002700280029002a002b002c002d002e002f0030003100320033003400350036003700380039003a003b003c003d003e003f004000410042
0043004400450046004700480049004a004b004c004d004e004f0050005100520053005400550056005700580059005a005b005c005d005e005f0060006100a30084008500bd009600e80086008e008b
009d00a900a4008a00da0083009301020103008d0097008800c300de0104009e00aa00f500f400f600a200ad00c900c700ae006200630090006400cb006500c800ca00cf00cc00cd00ce00e9006600d3
00d000d100af006700f0009100d600d400d5006800eb00ed0089006a0069006b006d006c006e00a0006f0071007000720073007500740076007700ea0078007a0079007b007d007c00b800a1007f007e
0080008100ec00ee00ba00d700b000b100d800dd00d900b200b300b600b700c400b400b500c5008700be00bf00bc01050106010701080109010a07756e693030423207756e693030423307756e693030
4239044575726f07756e69323231390b6f6e656672616374696f6e0b74776f6672616374696f6e0d74687265656672616369746f6e0c666f75726672616374696f6e0000000000030008000200110001
ffff0003000000010000000000010000000e000000180000000000020001000100d7000100040000000200000000000100000000c7feb0df00000000bd76892400000000c96f2dc4
]],
["fonts/DroidSans.ttf"] = [[
000100000013010000040030474445460026038700023d200000001e47504f5394bda2f000023d400000a8fa475355426c91748f0002e63c000000204c545348c99bb9200000101c000003864f532f32
a0cb958b000001b800000060636d6170ce11d4a2000056780000079e63767420397e3e4c0000680c000001fc6670676d73d323b000005e1800000705676173700004000700023d140000000c676c7966
c293c651000071100001a81e68646d78ef0efaab000013a4000042d468656164ebc60a350000013c00000036686865610c5309a40000017400000024686d7478d2d72e270000021800000e046c6f6361
92fa294c00006a08000007066d617870062a028500000198000000206e616d65b559905600021930000006a2706f7374d850a92700021fd400001d3f7072657082dc211300006520000002ec00010000
000100005e1db5c15f0f3cf50019080000000000c19a330000000000c61b8c03fb89fdd5095a086200000006000200000000000000010000076dfe1d00000964fb89fea2095a00010000000000000000
000000000000038000010000038200a00016007a000500020010002f005a0000021f01390003000100030499019000050008059a05330000011e059a0533000003d0006601f20000020b060603080402
0204e00002ef4000205b00000028000000003141534300400020fffd061ffe140084076d01e32000019f00000000044a05b600000020000204cd00c10000000004140000021400000227009303370085
052b00330468007b069a0066059e006d01cf0085026800520268003d04680052046800660200003f029300520225009302fc001404680062046800b20468006004680052046800170468008304680071
0468005a0468006a0468006a022500930225003f0468006604680066046800660368002506ee006d04dd000004f800c704d3007d057900c7043900c703ee00c70585007d059c00c702b60052022bff48
04a200c703ee00c706f600c705d500c705f0007d049c00c705ee007d04b800c70427006804270014059600b8048b000007120014046000000437000004500052026d00a402fc0017026d003304420029
034afffc049e0189043f005e04b000ae03b4007104b000710448007102a2001d0425002504b600ae021200a00212ffbc03f800ae021200ae072b00ae04b600ae049e007104b000ae04b00071033100ae
039c005a02b6002104b600a403d5000005f800140400002303e9000a0387005202d5003d046801e902d50033046800660214000002270093046800bc046800440468007b0468001d046801e903e30079
049e013306a8006402a6004403e50052046800660293005206a800640400fffa036d007b0468006602a6003102a6001f049e018904c100ae053d00710225009301a4002302a6003f02cd004203e50054
05e5003f05e5002c05e5001f0368004404dd000004dd000004dd000004dd000004dd000004dd000006d1fffe04d3007d043900c7043900c7043900c7043900c702b6003e02b6005202b6001102b60040
0579002f05d500c705f0007d05f0007d05f0007d05f0007d05f0007d0468008d05f0007d059600b8059600b8059600b8059600b804370000049c00c704d100ae043f005e043f005e043f005e043f005e
043f005e043f005e06aa005e03b40071044800710448007104480071044800710212ffde021200ae0212ffbd0212ffee049e006f04b600ae049e0071049e0071049e0071049e0071049e007104680066
049e007304b600a404b600a404b600a404b600a403e9000a04b000ae03e9000a04dd0000043f005e04dd0000043f005e04dd0000043f005e04d3007d03b4007104d3007d03b4007104d3007d03b40071
04d3007d03b40071057900c704b000710579002f04b00071043900c704480071043900c704480071043900c704480071043900c704480071043900c7044800710585007d042500250585007d04250025
0585007d042500250585007d04250025059c00c704b600ae059c000004b6001202b6fff50212ffa302b6003d0212ffeb02b600300212ffde02b600520212004402b60052021200ae04e10052042500a0
022bff480212ffbc04a200c703f800ae03f800ae03ee00c7021200ab03ee00c70212006603ee00c7021200ae03ee00c7026600ae03ee001d0212fff605d500c704b600ae05d500c704b600ae05d500c7
04b600ae0546ffff05d500c704b600ae05f0007d049e007105f0007d049e007105f0007d049e0071071f007d0748007104b800c7033100ae04b800c70331006004b800c70331007204270068039c005a
04270068039c005a04270068039c005a04270068039c005a0427001402b600210427001402b600210427001402b60021059600b804b600a4059600b804b600a4059600b804b600a4059600b804b600a4
059600b804b600a4059600b804b600a40712001405f800140437000003e9000a04370000045000520387005204500052038700520450005203870052024c00ae046800cb04dd0000043f005e06d1fffe
06aa005e05f0007d049e007304270068039c005a049e0102049e01020475011b049e0121021200a0049e016d017b001f049e0102049e00df049e01f8049e011404ddffe90225009304c9ffe70617ffe7
03b2ffe70642ffe70554ffe7064affe7029effe404dd000004f800c703ee00c7048b0025043900c704500052059c00c705f0007d02b6005204a200c7048b000006f600c705d500c7043f005205f0007d
058700c7049c00c70442004e042700140437000006230068046000000623006805f0004e02b600400437000004b0007103a0005a04b600ae029e00a404b800a404b0007104d100ae03e9000a049e006f
03a0005a03a4007104b600ae04870071029e00a403f800ae0417fff204c100ae0423000003a40071049e0071050a0019049e00a403a4007104b400710398001204b800a4059600710431ffec05ec00a4
06060071029e001504b800a4049e007104b800a406060071043900c705a0001403ee00c704d3007d0427006802b6005202b60040022bff4807480000077500c705a0001404a200c704ae0019058700c7
04dd0000049c00c704f800c703ee00c70533000e043900c706850004045c004805d500c905d500c904a200c7055a000006f600c7059c00c705f0007d058700c7049c00c704d3007d0427001404ae0019
0623006804600000059a00c7054800a607fa00c707fa00c7053d0014069600c7049c00c704c1003b081d00c704b80019043f005e048f0075048900ae033300ae046000290448007105c3000403ae0044
04e900ae04e900ae03ee00ae045e001005be00ae04e900ae049e007104c100ae04b000ae03b400710387002903e9000a059300710400002304d100ae04ac009a06f400ae070400ae056d002905fe00ae
048900ae03b20037067900ae043f00230448007104b60012033300ae03b40071039c005a021200a00212ffee0212ffbc0683001006e500ae04b6001203ee00ae03e9000a04c100ae03ee00c7033300ae
0712001405f800140712001405f800140712001405f800140437000003e9000a040000520800005208000052034afffc01660017016600170200003f0166001902e7001702e700170381003f03e3007b
03f8007b03020096066f0093095a006601cf008503370085024e0052024e005203f60093010afea002f8006a0468006004680044060000960468003f0668008d0400007707e700c705fe002505f0004e
04f40066060e0053060e0033060e004f060e007104a60062048b002905ee00c7050c004a046800660464002505a80077031900100468006604680066046800660468006604aa006d04b4001d04b4001d
049e00cf0212ffbc040001870400016f0400017d02a6002502a6000c02a6003b02a6002502a6002f02a6003102a600210400000008000000040000000800000002ab0000020000000155000004680000
02250000019a000000cd0000000000000000000008000054080000540212ffbc0166001704cd000a0452000006a0001206f600c7072b00ae04dd0000043f005e0642fed302aa0073033300930756001d
0756001d05f0007d04aa007105f400b8052f00a40000fc8f0000fd480000fc460000fd040000fd31043900c705d500c90448007104e900ae07db007d066f00000525001404dd0014072700c705b600ae
052f00000464000a072900c7061200ae057f001404f0000a078b00c706a200ae045e003903ae00170623006805ec00a405f0007d049e007104c1000003dd000004c1000003dd00000964007d08580071
063f007d0519007107b2007d0650007107db007d066f000004d3007d03b4007104df0068047500c9049e00f4049e01e1049e01e107e9002907a6002905e700c904fc00ae049c002f04890012049c00c7
04b000ae03ee002f03330012050000c7040800ae06e9000406230004045c004803ae0044050a00c7044a00ae04a200c7042b00ae04a2002f03f800120544001404d1002905cd00e504fa00ae065c00e5
05ac00ae084400c706b800ae05ec007d04f2007104d3007d03b4007104270014038700290437000003d500000437000003d5000004d30000043d002306890014058b0029054800a604bc009a054800a6
04ac009a054800c704ac00ae0677003d051d00330677003d051d003302b600520685000405c30004054a00c7042d00ae056a000004710010059c00e504e900ae05cb00e504fc00ae054800a604ac009a
070600c705d100ae02b6005204dd0000043f005e04dd0000043f005e06d1fffe06aa005e043900c704480071058f007104480066058f0071044800660685000405c30004045c004803ae0044045c0048
03b8001b05d500c904e900ae05d500c904e900ae05f0007d049e007105f0007d049e007105f0007d049e007104c1003b03b2003704ae001903e9000a04ae001903e9000a04ae001903e9000a054800a6
04ac009a03ee00c7033300ae069600c705fe00ae03ee002f0333001204c30000042f00230460000004000023049c008904b0007106f4008906fe007106fc004e0646005004c5004e0410005007a40000
06a4001007e500c7072f00ae05c3007d04e100710581001405120029045c006f03ae005c055a0000045e001004dd0000043f005e04dd0000043f005e04dd0000043f005e04dd0000043f001204dd0000
043f005e04dd0000043f005e04dd0000043f005e04dd0000043f005e04dd0000043f005e04dd0000043f005e04dd0000043f005e04dd0000043f005e043900c704480071043900c704480071043900c7
04480071043900c7044800710439002704480012043900c704480071043900c704480071043900c70448007102b600520212006202b600520212009e05f0007d049e007105f0007d049e007105f0007d
049e007105f0007d049e003305f0007d049e007105f0007d049e007105f0007d049e007105f0007d04aa007105f0007d04aa007105f0007d04aa007105f0007d04aa007105f0007d04aa0071059600b8
04b600a4059600b804b600a405f400b8052f00a405f400b8052f00a405f400b8052f00a405f400b8052f00a405f400b8052f00a40437000003e9000a0437000003e9000a0437000003e9000a04b00071
0000fbdb0000fc6a0000fb890000fc6a0000fc640000fc730000fc730000fc730000fc6401a4003100100010000003820101011a281801010a012b01010a0b160114050b0b0b0b0b0b0b0b0b0b14140b
0b0b1c0b2622140b01120b15191515141c1922131e161b1b151710102612010501200101110b1c0b152a192e0c0c0a0c182e150b0b122a0b2e190b1a16121918190b1a280b0b0b0b1815010414270b01
0401100b111101120b1401110a2701010a1c262626262626011401010101191919190b1922222222220b2215151515261316111111111111161c151515150c0c0c0c152e15151515150b152e2e2e2e16
0b16261126112611141c141c141c141c0b0b0b0b011501150115011501150b190b190b190b19152e152e190c190c190c190c190c1517150c150a0a140c140c140c1424140c192e192e192e18192e2215
221522152a281612161216121b2a1b2a1b2a1b2a1b0b1b0b1b0b152e152e152e152e152e152e100b261626121212121212300b2611011622151b2a01012401010101010101012614152d0c150d151626
221917011215221915191c1911221513201b261910192219260b162e16120b0c161516162e17160a2d12181815181318180112191313181612151218010e19141b19191528190e153015261322191f01
1812191915101c15221513141b30191015141919171019162416111419141615191a19190a16131915120b1c1916191a16121919171819111911152e141c2a0c0c0c18182e0a16121701100b100b100b
2616010101011515191515151919191919142b1819190c01190b0b110b01012a1922010c0c0c0c26171e2b010117170b0b0b0b191414190c010101111111111111110101010101012e01010101010101
010c151116181c18261101010114142215191c0101010101011915191d17202601011c18181619191714011a19132215181618161801141519011d18141c0d240101012e0116190117130b1214151919
19121a15011519150a0b18181919192e191213141c1b192619261919192a151316141219180a160a16191819201818141519181914121515192611261101160115151515151819121a12111919191922
1522152215161130163016301614121914101812141601101a130b1715181819161718271915191317121a10162611261126112611261126112611261126112611261126110115011501150115011501
1501150115190c190c221522152215221522152215221522152215221522152215152e152e191c191c191c191c191c2616261626160b0101010101010101010101010000000000130000038406070400
0302020204030504010202030302020202030303030303030303030202030303030504040404030304040202030305040404040403030404050303030202020302030404030403020304020203020504
0404040203020403040403030203020302020303030303030305020303020503030303030304040201030203040404030404040404040504030303030202020204040404040404030404040404030304
0404040404040503030303030202020204040404040404030404040404030403040404040404040304030403040304040404030303030303030303030403040304030403040404040202020202020202
0202040402020303030302030203020302030204040404040405040404040404040405050402040204020303030303030303030203020302040404040404040404040404050403030303030303030302
0304030505040403030303030302030103030303040204050305040502040403040303040402030405040304040404030306030604020304030402040404030403030404020304040403040404030403
0405040506020404040603050304030202020606050304040404040304030503040403040504040404040304060304040606050604040604040404020303040304040304050404040403030305040404
0606040503030604030402030302020205050403030403020504050405040303030606020101020102020303030206070102020203010203030503050306050404050505050404040403030402030303
0304040403020303030303030303030303060306020201030201010000060602010403060505040405020206060404040400000000000304030406050404050404030505040406050303060504040403
0403070605040605060504030403030303060604040303040403020403050503030403030303030404040404040605040404030303030303030403050404040404040405040504020504040304030404
0404040405050204040404050503030403040305040303030304040404040404040404040304030403040304040302060503020403030403040606050504030605060604040405030304040404040404
0404040404040404040404040404040404040403030303030303030303030303030303020202020404040404040404040404040404040404040404040404040404040404040404040404040404030303
0303030400000000000000000001010107090400040202030504060502020204040202020304040404040404040404020204040403060504040504040505040204030605050405040404050506040404
0203020403040404030404030404020204020604040404030302040406040403020402040202040404040403040602030402060403040303040405020103020305050503050505050505060404040404
0404040405050505050505040505050505040404040404040404060304040404020202020404040404040404040404040404040405040504050404030403040304030504050404040404040404040404
0504050405040504050405040402040204020402040205040202040404030203020302030203020504050405040505040504050405040606040304030403040304030403040304020402040205040504
0504050405040504060604040404030403040302040504060605040403040404040204010404040405020405050505060205040305040405050404050605040505040404040604060504040403040204
0404040403030404020404040403040404030403040604060602040404060405030404040402070705040405050404030504060405050405060505050404040406040505080805060404070404040403
0404060304040304050404040403030406040404060605060403060404040303030202020606040304040303060606060606040404070703010102010303030303030608020302020301030404050406
0407050504050505050405050404040503040404040404040402040404030303030303030407040702020104020101000007070201040406060605040502030606050405040000000000040504040606
0504060505040605060407050404060605040504050409070504060606060403040404040407070504040404040403040406060403040404030403050405040504070606060403040304040404040407
0606040504050406050605040606050405040504050405040605040504050406060404050405040606040304040504050405040504050404030404040404040504030306060403040404040404060606
0504040706070605040505040305040504050405040504050405040504050405040504050405040404040404040404040404040404040404020402050405040504050405040504050405040504050405
040504050405040504050405040504050404040404040404000000000000000000010101080a050004020203050407060202020404020302030404040404040404040402020404040407060505050404
0606040205040806060506050404060608050405020302040305040504050403040502020402080505050503040305040604040403040304020204040404040405070304040307040304040405050502
0204030406060604060606060606070504040404040404040506060606060604060606060604050504040404040407040404040402020202050505050505050405050505050405040604060406040504
0504050405040505050504040404040404040404060406040604060406050605040204020402040204020504020205040404020402040204020402060506050605060605060506050605070705030503
0503040404040404040404030403040306050605060506050605060508060404040504050405040204060407070605040405050405020501050505050602050605060506020605040604050606040506
0806040606050504040605060604040504050205050504050404050502040405040405050504050405060406060205050506040604050404040208080605050606050504060408040606050608060606
0505040506050605080806070505080504050503040406040505040506050505050404040604050508080506040407040405030404020202070705040405040308060806080604040408080301010201
0303040404030609020302020401030404060406040806060506060606050606050404060304040404050505050204040404040404040404040804080302010402020100000808020105040708080604
0603030707060506050000000000040604050806050507060604070506060807040406060605060406040a08060508060806050405040505050808060505050505040305040806040405040504050405
0506050605080607060504040404040404050407070605050505050705070504080605040605060506050505080604060406040707040406040604080604040404060506050605060506050504050405
0405040505040307060403050405040505070806060404080709070605050504040605060406040604060406040604060406040604060406040604040404040404040404040404040404040402040206
0506050605060506050605060506050605060506050605060506050605060506050605060504040404040405000000000000000000020202090c05000502020406050806020303060502030203050505
0505050505050502020505050408060505060505060604030504080607050705050406060806060503030305040505050405050405050202050208050505050404040505060505040305030502020505
0505050405070405050307050405040405050602020404050707080406060606060608050505050504040404060607070707070507060606060605050505050505050804050505050202020205050505
0505050505050505050505050605060506050504050405040504060506050505050505050505050506050605060506050605060504020402040204020402060403020505050402040204020403040206
0506050605060605070507050705090805040504050405040504050405040404040404040605060506050605060506050806060506050405040504020506050808070505040505050502050205050505
0602060705080708020605050605050607040506080605070605050406060606070406050405020505050505040405050205050505040506050405040506060606020505050605060505050404030909
0605060606050505060508050606050708060706050504060606060608080707050509050505050405050604050505050605050505040405060505050808060705040705050504040402020208080505
0505050408060806080606050509090402020202040404040403060b02040303040104050508050705090807060808080805060606050507030505050506060604020505050404040404040405090509
03020205020201000009090202060606080806050703040909070506050000000000050605050807060508060606080706060807050406060705060506050c0907050807080705040505050505090906
0505050505050405050806050406050505050506060605060509080706050404040604060406050807070606050605080608060408060605070506050605060508060406050605080805050705070508
060504050406050605070507050705050406050605060506050504070705040505060505050808080705050a070908060506050504070506050605060506050605060506050605060506050605060505
050505050505050505050505050505040204020705070507050705070507050705070507050705070507050605060506050605060506050605060506050605050000000000000000000202020a0c0600
0503020406060807020303060502030204050505050505050505050202050505040806060606050506060403050408060705070505040606080606050304030504060505040505040505020205020805
0505050404040505060505040405040503020505050505050608040505030805040504040605060202040405070707040606060606060906050505050404040406060707070707050706060606060505
0505050505050804050505050202020205050505050505050505050505050505060506050605060406040604060406050605050505050505050505050605060506050605060506050402040204020402
0402060503020505050402040204020404040206050605060507060507050705070509080504050405040504050405040504040404040404060506050605060506050605080606050605040504050402
0506050908070505040606060603060206060606060206070508070802060605060505060704050608060507060505040608060807040605040502050505050504040505020505050504050605040504
0506060606020505050605070506050404030909070506060606060506050805060605070806070605060406080606060a0a070806060a05050505040505060405050505060505050504040506050505
0808070705040805050504040402020208080505050505040806080608060605050a0a0402020202040404040403090c0204030304010405050805080509070706080808080506070606050703050505
05060606060205050504040404040404050a050a0303020603020100000a0a0202060606080806050803040909070506050000000000050605050a07060609070606090808060a070504080607050605
06050c0a07050a080a07060406060606060a0a06050606050505040605080605040605050505050606060507060a08070606040404060406040605080707060605060508060806040806060507050605
0605060508060406050605090805050705070508060504050406050605070507050705060406050605060506050504080705040605060505050808080705050a08090807050705050407050605060506
0506050605060506050605060506050605060505050505050505050505050505050505040204020705070507050705070507050705070507050705070507050605060506050605060506050605060506
050605050000000000000000000202020b0e07000603030507060908030303060603040304060606060606060606060303060606050a0707070806050808030307050a08090609070605080709060707
0304030605060606050606040606020205020a06060606040504060608060605040504060303060606060505060904050604090605060404060607030204040508080805070707070707090706060606
03030303080809090909090609080808080706060606060606060a0506060606020202020606060606060606060606060606060607060706070607050705070507050806080606060606060606060606
0806080608060806080608060302030203020302030207050302070505050205020502050305020806080608060808060906090609060a0a070407040704060506050605060505040504050408060806
0806080608060806090807060707050705070502060706090a09060605060606060306020606060607030709040908090407070607060708090307070a08050908060605070906090903070605060406
06060606050506060405060606050608060506050608060808040606060806080607060303030a0a080706080707070607060906080807070a08090806070506090608080b0b080907070c0706060604
07060805060605060706060606050606080606060a0a08080605090606060405050202020a090605060605040908090809080706060b0b0502020202040404050505090d030503030601040606090609
060b09090709090909060708070606080406060606070606060206060604040404040404060b060b0403020603020100000b0b02020707080a0a07060904040a0a090608070000000000060806060b08
07070a0807060a0807080a0a060509080906080608060e0b09060b090b08070506060606060b0b08060606060605040706090806050706070507050707080609070b0a0907070505060706070606060a
0908070806080609070907030908070607060806080608060a070307060706090a0606090609060908060506050806080609060906090607050606060606060806060409080504070606060606090a09
0906060a090b0908060807060507060706070607060706070607060706070607060706070607060606060606060606060606060606060603020302090609060906090609060906090609060906090609
0609060806080608070807080708070807070607060706060000000000000000000202020c0f07000603030508070a08030404070703040304070707070707070707070303070707050a070707080606
0808040307060b0909070907070708070b0707070404040605070707060707030607030306030b07070707040604070509060506040704070303070707070706070a040607040a060507040407070803
02040406090909050707070707070a070606060604040404080909090909090709080808080707070707070707070b060707070703030303070707070707070707070707070507050707070707070706
07060706070608070807060706070607060706070806080608060806080708070403040304030403040307060303070606060306030603060406030907090709070809070907090709070b0b07040704
070407060706070607060704070407040807080708070807080708070b09070507070607060706030707070a0b0907070607070707030702070707070703070906090909040707060706070809040707
0b0907090807070707090709090407070607040707070507060507070406050706060708070607050709060909040707070906090607070404030b0b090707080707070608060907090907080b080908
07070707090708080b0b080a07070c070707070506070906070706070907070707060505090607070b0b080907060a0707070506060303030b0b0706050706050b090b090b090705060c0c0502020202
040404050505090f0305040406020407070a070a060d0a090709090909070708080707090507070707070707070306060604040404040404060c060c0403020703020100000c0c03020707090b0b0707
0904050b0b090709080000000000060907070b0907070b0907070a0a07070a0a070609090907080508050e0d09070b090b09070606070707070c0b090707070707060507070a09070608060706070608
07080709080c0b0a09070607050705070507060a080907080708070a080a08040909080708070807080708070b0904070707070a0b060709070907090907060706090709070907090709070706070507
050705080706050a0906050706070607070b0b0a0907060b0a0c0b0907090707060807070707070707070707070707070707070707070707070707060706070607060706070607060706070403040309
07090709070907090709070907090709070907090709070807080709080908090809080908070507050705070000000000000000000202020d1008000703040508070b09030404070703040305070707
070707070707070303070707060b0808080907060909050308060b090a070a08070709070b0707070405040705080708060807040708030306030b0808080805060408060a0706060507050703040707
07070706080b050607040b070607050508080903030505060a0a0a060808080808080b08070707070505050509090a0a0a0a0a070a090909090707080707070707070b06070707070303030308080808
0808080708080808080608060807080708070806080608060806090809080707070707070707070709070907090709070908090805030503050305030503080603030806060603060306030604060309
08090809080909080a080a080a080c0c08050805080507060706070607060704070407040908090809080908090809080b0a070607070607060706040708070b0b0a0807060808070803080208080808
0803080a060a090a04080807070707090a0508080b09070a09070707070a070a0a050708060804080808060806060808040607080706080808060806080a070a0b040808080b070a0708070505030c0c
0a0808090808080708070b07090908090b090a09070807080a0709090d0d090b08080d080708070507070906080806070908080808060506090708080b0b080a07060b0707080506060303030b0b0806
060806050b0a0b0a0b0a0706070d0d050202020205050505050509100305040406020507070b070a070d0a0a080a0a0a0a080709080707090507070707070707070307070705050505050505070d070d
0403020703030100000d0d030208070b0b0b08070a04050b0b0a080a090000000000070907080d0a08080c0908070c0a09070c0a07060a0a0a08080608060f0e0a080d0a0d0a080608070808080d0c0a
0807080708060508070b090706080708060806090709080a090d0b0a0a080607050706070607070b090908090809080b090b09050b09080809070908090809080b0905080708070b0b07070a070a070b
0907060707090809080a080a080a080806080608060806090807050b0a06050807070707080b0c0b0a08070d0b0d0c090809080706090708070807080708070807080708070807080708070807080707
070707070707070707070707070707050305030a080a080a080a080a080a080a080a080a080a080a080a08090809080a090a090a090a090a09070607060706080000000000000000000303030e110800
0704040609080c0a030404080804050405080808080808080808080404080808060c0909080a07070a0a050308070c0a0b080b0807070a080d0807080405040706080708070808040708040407040d08
08080805070508070a070707050705080404080808080707080c050708050c070608050508080904030505070a0a0a060909090909090c0807070707050505050a0a0b0b0b0b0b080b0a0a0a0a070808
0707070707070c0708080808040404040808080808080808080808080807080709070907090708070807080708070a080a08070807080708070807080a070a070a070a070a080a080504050405040504
050408080304080707070407040704070507040a080a080a080a0a080b080b080b080c0d08050805080507070707070707070705070507050a080a080a080a080a080a080d0a07070708070807080704
0809070c0c0b08070708080808040803080808080904080b060b090b040909070907080a0b0508090c0a080b0a080807070b080b0b050708070804080808070807060808040707080707080908070806
080a070a0b040808080b070a0708070505030d0d0a08080a0908090709070b080a0a08090c0a0b0a080807080b080a090d0d090c09080f080708080508080907080807080a0808080807050709070808
0b0b090b08070b0808080507070404040c0c0807070807060d0a0d0a0d0a0707070e0e06030303030606060707060c110306040407020508080c080b070e0a0b090b0b0b0b08090a0908080a05080808
08070808080407070705050505050505070e070e0504020804030100000e0e040308080b0c0d09070b05060d0d0b080b090000000000070a08080d0b09080d0a09070c0a09090d0c08070b0a0b080907
0907110f0b080d0b0d0b080709080808080e0d0a0808080808070509080b0a080709080807080709080a080b0a0f0c0b0a080707050707070708070b0a0a0909080a080b090b09050b09090809080a08
0a0809080c0a05090709070c0c07080b080b080b09080708070a080a080b080b080b080807080708070807090807050c0b07050807080708080c0c0c0b08070d0c0e0c0a080a09080709080907090709
0709070907090709070907090709070907090707080708070807080708070807080708050405040b080b080b080b080b080b080b080b080b080b080b080b080a080a080b090b090b090b090b09070707
070707080000000000000000000303030f120900080404070a080c0b040505080804050406080808080808080808080404080808060d0a0a090a08080a0b060409070d0b0b090b0907080b090e090808
0506050806090809070908040808040407040d0809090906070508070b070707050705080404080808080707090c050708050c080708050509080a04030505070b0b0b060a0a0a0a0a0a0d0908080808
060606060a0b0b0b0b0b0b080b0b0b0b0b0809090808080808080d070808080804040404090809090909090809080808080709070a080a080a0809070907090709070a090a0908080808080808080808
0a080a080a080a080b080b080604060406040604060409080404090707070407040704070507040b080b080b080a0b080b090b090b090d0e09060906090607070707070707070805080508050b080b08
0b080b080b080b080e0b08070808070807080704080a080d0d0b09070709090809040903090909090a04090c070c0a0c040a0a080a08080b0b06090a0d0b080b0b090808080b090b0b06080907080408
0909070907070809040707080807090909070907080b080a0c040809080c080b0809070606040e0f0b09090b0a090a080a080c080b0b090b0d0b0b0b090908090b090b0a0e0e0a0d0a09100908090806
08080b07080807080b080908090707070b0708080d0d0a0b08070c0808080607070404040c0d0807070807060e0b0e0b0e0b0807080f0f06030303030606060808070c120407040407020508080c080c
080f0c0b090b0b0b0b090a0b0908080b0608080808090909090408080805050505050505080f080f0504030804030200000f0f040309080c0d0d0a080c05060e0e0b090b090000000000080b08080f0c
0a090d0b0a080d0a0a090e0c08070b0a0b090907090712100b090d0c0f0c090709080909090f0e0b080909090908060a080c0b08070908090709070a090b080c0a100d0b0a090708070807080709070d
0a0a090a080b080c090c09060c0b0a080b080b080b080a080d0b060a080a080d0d08080b080b080c0b080708070b080b080b090b090b0909070907090709070a0808060d0b08060908090709090e0e0e
0c09080f0c0f0c0b090b0a08070b080a080a080a080a080a080a080a080a080a080a080a080a0808080808080808080808080808080808060406040b090b090b090b090b090b090b090b090b090b090b
090b090b080b080b090b090b090b090b090807080708070900000000000000000003030310140a00080404070a090d0b040505090904050406090909090909090909090404090909070e0b0b0a0b0808
0b0b060409080e0c0b090b0a08080b0a0e0908090506050907090909080909040809040408040e0909090906070509080c080808060906090404090909090908090d060809050d080709060609090a04
030606080c0c0c070b0b0b0b0b0b0e0a08080808060606060b0c0b0b0b0b0b090b0b0b0b0b08090a0909090909090e080909090904040404090909090909090909090909090809080b090b090b090a08
0a080a080a080b090b09080908090809080908090b080b080b080b080b090b09060406040604060406040a080404090808080408040804080608040c090c090c090b0c090b090b090b090e0f0a060a06
0a0608070807080708070805080508050b090b090b090b090b090b090e0c08080809080908090805090b090e0e0b09080709090909040903090909090b040a0c070d0b0d050b0b080a08090b0b06090a
0e0c090b0b090908080d090d0b06080907090509090a080907070909050808090808090a09080907090b080c0c050909090c080b080a080606040f0f0b090a0b0b090b080a080e090c0c090b0e0b0b0b
090a080a0d090b0a10100b0d0a0a100a0909090609090b07090908090b090909090807080b0809090e0e0b0c09080d0909090608070404040e0e0908080908060e0c0e0c0e0c08080810100703030303
0606060808070c140407050508020609090d090d08100d0b0a0c0c0c0c090a0b0a09090b06090909090909090a0408080806060606060606081008100504030904030200001010040309090c0e0e0b09
0d05060e0e0b090b0a0000000000080c0909100d0b090e0b0b090e0c0b090f0e09070d0c0b090a080a0813110b090e0d100d0a080a09090909100f0c090909090908060b080e0b09070a09090809080b
0a0b090c0b110d0d0a0a0808070807080709080d0b0b0a0a090b090d0a0d0a060e0b0b090b090b090b090a090e0b060b090b090e0e08090b090b090e0b090709080c090c090b090b090b090a080a080a
080a080a0908060d0c08060a08090809090e0e0f0d0a08100e100e0b090b0a09070b090b090b090b090b090b090b090b090b090b090b090b090b0908090809080908090809080908090809060406040b
090b090b090b090b090b090b090b090b090b090b090b090b090b090b0a0b0a0b0a0b0a0b0a0808080808080900000000000000000003030311140a00090404070b090e0c040505090904050406090909
090909090909090404090909070f0a0b0a0c09090c0c06050a080f0d0d0a0d0b09090c0a0f09090a05060509070a090a080a09060809040408040f090a0a0a06080609080d0808080609060904040909
090909080a0e060809050e09070906060a090b04030606080d0d0d070a0a0a0a0a0a0e0a09090909060606060c0d0d0d0d0d0d090d0c0c0c0c090a0a0909090909090e0809090909040404040a090a0a
0a0a0a090a09090909080a080a090a090a090a080a080a080a080c0a0c0a090909090909090909090c080c080c080c080c090c09060406040604060406040b0805040a0808080408040804080608040d
090d090d090b0d090d0a0d0a0d0a0f0f0b060b060b0609080908090809080906090609060c090c090c090c090c090c090f0d0908090a080a080a0805090a090e0e0d0a09080a0a090a040a030a0a0a0a
0a040a0d080d0b0d050a0b090a090a0c0d060a0a0f0d090d0c0a0909090e090e0d06090a080905090a0a080a0808090a0508090908080a0b0a080a08090c090c0d05090a090d090c090a090606050f10
0c0a0a0c0a0a0b090b090e0a0d0d0a0b0f0c0d0c0a0a090a0e090c0b11110b0e0b0a120b090a090609090c08090908090c090a090a0808080c0809090f0f0b0c09080e0909090608080404040e0e0908
080908070f0d0f0d0f0d090809111107030303030606060808060f140407050508020609090d090e09120e0d0b0d0d0d0d0a0a0c0b09090c0709090909090a0a0a040909090606060606060609110911
060403090503020000111104030a0a0d0f0f0a090d060710100d0a0c0a0000000000090d0909110e0b0a0f0c0a090f0c0c0b110e09080e0c0d0a0a080a0814120d0a0f0d110e0a080a090a0a0a11100e
090a0a0a0a09060b090e0c0a080b090a080a080b0a0c090d0b120f0e0b0a0809080907090709080d0c0c0a0b090c090e0a0e0a060e0c0b090b090c090c090b090f0c060a090a090e0e09090d090d090e
0c0a080a080d090d090d0a0d0a0d0a0a080a080a080a080b0909060e0c09060a0909080a0a0f0f100d0b09100e110e0c0a0d0b0a080b090a090a090a090a090a090a090a090a090a090a090a090a0909
090909090909090909090909090909060406040d0a0d0a0d0a0d0a0d0a0d0a0d0a0d0a0d0a0d0a0d0a0d0a0c090c090c0a0c0a0c0a0c0a0c0a0908090809080a00000000000000000003030312160b00
090504070c0a0f0d0405050a0a040604070a0a0a0a0a0a0a0a0a0a04040a0a0a08100b0b0b0c0a090c0d06050a09100d0d0b0d0b09090d0a100a090a0507050a070a0a0b080b0a070a0b05050905100b
0a0b0b0709060b0a0d090a08060a060a05040a0a0a0a0a090a0f060a0a060f09080a06060a0b0c040406060a0d0d0d080b0b0b0b0b0b0f0b0a0a0a0a060606060c0d0d0d0d0d0d0a0d0d0d0d0d090b0b
0a0a0a0a0a0a0f080a0a0a0a050505050a0b0a0a0a0a0a0a0a0b0b0b0b0a0b0a0b0a0b0a0b0a0b080b080b080b080c0b0c0b0a0a0a0a0a0a0a0a0a0a0c0a0c0a0c0a0c0a0d0b0d0b0605060506050605
06050b0a05050a0909090509050905090609050d0b0d0b0d0b0d0d0b0d0a0d0a0d0a10100b070b070b0709090909090909090906090609060d0b0d0b0d0b0d0b0d0b0d0b100d090a090a080a080a0805
0a0b0a0f0f0d0a09090a0a0a0a050a030a0a0a0a0b040b0e080e0c0e060b0b0a0b0a0a0d0d060a0b100d0a0d0d0b0a09090e0a0e0d06090b080b060b0b0b0a0a08080b0a0609090b09080a0c0b080a08
0b0d0a0c0d060b0a0b0d0a0d0a0b0906060510110d0a0b0d0b0b0b0a0c0a0e0a0d0d0a0c100d0d0d0b0b090b0e0a0d0c12120c0f0b0b130b0a0a0a070a0a0d080b0b090a0e0b0a0b0b08080a0d090b0b
10100c0e0a080f0a0a0b0708090505050f100b090a0b0907100d100d100d090a09121207030304030606060808070f15040706060902070a0a0e0a0e09130e0d0b0e0e0e0e0a0b0d0b0a0a0d070a0a0a
0a0b0b0b0b0509090906060606060606091209120605030a0504020000121205030b0a0f10100b0a0e060711110d0a0d0c00000000000a0d0a0b120e0c0b100d0b0a100e0c0b110e0a080e0c0d0a0a0a
0a0a16130d0a110e120e0b080b0a0a0a0a12110d0b0a0b0b0b09070b090e0d0a080b0a0a090a090c0b0d0b0e0c130f0d0c0b080908090909090a090f0d0d0b0c0b0d0b0f0c0f0c060e0d0c0a0c0a0d0b
0d0b0c0b100e060b0a0b0a0f0f0a0a0d0a0d0a0e0d0a080a080d0b0d0b0d0a0d0a0d0a0b080b0a0b0a0b0a0c0b0a070f0e09070b090a090b0b1011100f0b09120f13100d0a0d0c0a080c0a0b0a0b0a0b
0a0b0a0b0a0b0a0b0a0b0a0b0a0b0a0b0a0b0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a060506050d0a0d0a0d0a0d0a0d0a0d0a0d0a0d0a0d0a0d0a0d0a0d0a0d0b0d0b0d0c0d0c0d0c0d0c0d0c090a09
0a090a0b00000000000000000004040413170b000a0504080c0a100d0406060a0a040604070a0a0a0a0a0a0a0a0a0a04040a0a0a08100c0c0c0d0a090d0e06050b0a110e0e0b0e0c0a0a0e0b110a0a0a
0607060a080b0a0b090b0a070a0b05050905110b0a0b0b0809060b0a0e0a0a08070a070a05040a0a0a0a0a0a0b10070a0a06100a080a06060b0b0c040406070a0e0e0e080c0c0c0c0c0c100c0a0a0a0a
060606060d0e0e0e0e0e0e0a0e0e0e0e0e0a0b0b0a0a0a0a0a0a10090a0a0a0a050505050a0b0a0a0a0a0a0a0a0b0b0b0b0a0b0a0c0a0c0a0c0a0c090c090c090c090d0b0d0b0a0a0a0a0a0a0a0a0a0a
0d0a0d0a0d0a0d0a0e0b0e0b060506050605060506050b0a05050b09090a050a050a050a060a050e0b0e0b0e0b0d0e0b0e0a0e0a0e0a11110c080c080c080a090a090a090a090a060a060a060e0b0e0b
0e0b0e0b0e0b0e0b110e0a0a0a0a080a080a08050a0c0a10100e0a0a090b0b0b0b050b040b0b0b0b0c040b0e090f0d0f060c0c0a0b0a0a0e0e060b0c110e0a0e0e0b0a0a0a0e0a0e0e060a0b080b060b
0b0b0a0a08090b0a06090a0b0a090a0c0b090a090b0d0a0e0e060b0a0b0e0a0d0a0c0a06060511120d0b0b0e0c0b0c0a0c0a100a0e0e0b0d110e0e0e0b0c0a0b0e0a0e0c12120c100c0c140c0a0a0a07
0b0a0e080b0b090a0e0b0a0b0b09090a0d0a0b0b11110d0f0a090f0a0a0b0709090505050f100b090a0b0a08110e110e110e0a0a0a131308030304030707070808070f17040806060902070a0a0e0a0f
0a130e0e0c0e0e0e0e0b0b0e0b0a0a0d070a0a0a0a0b0c0c0b050a0a0a060606060606060a130a130605030a0504020000131305030b0a1011110c0a0f060812120e0a0e0c00000000000a0e0a0b130f
0c0b110e0c0b110f0e0d13110a080e0e0e0a0b0a0b0a16140e0b120f130f0c090c0b0b0b0b13120e0b0b0b0b0b09070c0a100e0a080c0a0b090b090d0b0e0b0f0c14100e0c0c090a090a090a090a0a10
0d0d0b0c0b0e0b0f0c0f0c06100e0d0a0d0a0e0b0e0b0c0b110e060c0a0c0a10100a0a0e0a0e0a100e0a080a090e0b0e0b0e0a0e0a0e0a0c090b0a0b0a0b0a0c0b0a07100f09070b0a0a0a0b0b111111
0f0b09131014110e0a0d0c0a080d0a0c0a0c0a0c0a0c0a0c0a0c0a0c0a0c0a0c0a0c0a0c0a0c0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a060506050e0a0e0a0e0a0e0a0e0a0e0a0e0a0e0a0e0a0e0a0e
0a0e0a0e0b0e0b0e0c0e0c0e0c0e0c0e0c0a0a0a0a0a0a0b00000000000000000004040414180c000a0505080d0b110e0506060b0b050605070b0b0b0b0b0b0b0b0b0b05050b0b0b09110d0c0c0e0b0a
0e0d07060b0a110e100c100c0b0b0d0c120b0b0b0607060b080c0b0c090c0a070b0c05050a05130c0b0c0c0809070c0a0f0a0a09070b070b05050b0b0b0b0b090c11070a0b06110a090b07070c0c0d05
0407070a0f0f0f090d0d0d0d0d0d110c0b0b0b0b070707070e0e10101010100b100d0d0d0d0b0c0c0b0b0b0b0b0b11090a0a0a0a050505050b0c0b0b0b0b0b0b0b0c0c0c0c0a0c0a0d0b0d0b0d0b0c09
0c090c090c090e0c0e0c0b0a0b0a0b0a0b0a0b0a0e0b0e0b0e0b0e0b0d0c0d0c070507050705070507050d0a06050b0a0a0a050a050a050a060a050e0c0e0c0e0c0e0e0c100b100b100b12120c080c08
0c080b090b090b090b090b070b070b070d0c0d0c0d0c0d0c0d0c0d0c120f0b0a0b0b090b090b09060b0d0b1111100b0b090c0c0b0c050c040c0c0c0c0d050d0f09110d11060d0c0b0c0b0b0d10070b0c
110e0b100d0c0b0b0b0f0b0f10070b0c080c060c0c0c0b0b08090c0b060a0a0c0a090b0d0c090b090c0d0a0f0f060c0b0c0f0b0e0b0c0b07070613130e0b0c0d0d0c0c0b0d0b110b0e0e0b0d110d100d
0c0c0b0c0f0b0d0d13130d100c0d150c0b0b0b080b0a0f080c0c0a0b0e0c0b0c0c09090a0f0a0c0c11110e100b09110b0a0c08090905050511110c0a0a0c0908120f120f120f0b0a0a14140804040404
0808080a0a080f17050806060a03070b0b0f0b100a140f100c0f0f0f0f0c0c0e0c0b0b0e080b0b0b0b0b0c0c0c050a0a0a070707070707070a140a140705030b0504020000141405040c0b1111130d0b
1007081212100b0f0d00000000000b0e0a0c13100d0c120e0d0b120f0f0d13110b080f0f100b0c0a0c0a1815100c121013100c090c0b0c0c0c14130e0c0c0c0c0c0a080c0a110f0b080c0b0b0a0b0a0d
0c0d0c0f0e15110f0c0c090b090b0b0b0b0b0a110f0d0d0d0c0d0c100d100d07110f0d0b0d0b0d0c0d0c0d0c110e070d0b0d0b11110b0a0f0a0f0a110f0b080b090e0c0e0c100b100b100b0d090c0a0c
0a0c0a0d0c0b0810100a080c0a0b0a0c0c1212110f0b09131113120f0b0e0e0b080d0b0d0b0d0b0d0b0d0b0d0b0d0b0d0b0d0b0d0b0d0b0d0b0d0b0b0a0b0a0b0a0b0a0b0a0b0a0b0a0b0a0705070510
0b100b100b100b100b100b100b100b100b100b100b100b0d0c0d0c0f0d0f0d0f0d0f0d0f0d0b0a0b0a0b0a0c000000000000000000040404151a0d000b0506080e0c110f0406060c0c060706080c0c0c
0c0c0c0c0c0c0c06060c0c0c09120d0d0d0e0b0a0e0f08060c0a120f0f0c0f0d0b0a0f0c130b0c0b0608060b090c0b0c0a0c0b070b0c05050a05130c0c0c0c0809070c0b100b0b09070c070c05060c0c
0c0c0c0a0c11070a0c07110b090c07070c0c0e060407070a0f0f0f090d0d0d0d0d0d120d0b0b0b0b080808080e0f0f0f0f0f0f0c0f0f0f0f0f0c0c0c0b0b0b0b0b0b120a0b0b0b0b050505050c0c0c0c
0c0c0c0c0c0c0c0c0c0b0c0b0d0b0d0b0d0b0d0a0d0a0d0a0d0a0e0c0e0c0b0b0b0b0b0b0b0b0b0b0e0b0e0b0e0b0e0b0f0c0f0c080508050805080508050d0b06050c0a0a0a050a050a050a060a050f
0c0f0c0f0c0e0f0c0f0c0f0c0f0c13140d080d080d080b090b090b090b090a070a070a070f0c0f0c0f0c0f0c0f0c0f0c13100c0b0c0b090b090b09060c0d0b12120f0c0b090c0c0c0c050c040c0c0c0c
0d060d100a100e11060d0d0b0c0b0b0f0f080c0d120f0b0f0f0c0b0a0c100b100f080c0c090c060c0c0d0b0c09090c0c060a0b0c0b0a0c0d0c0a0c090c0f0b100f060c0c0c0f0b0f0b0d0b0808061314
0f0c0c0f0d0c0d0b0e0b120b0f0f0c0e120f0f0f0c0d0a0c100b0f0e14140e110d0d150d0b0c0b080c0b0f090c0c0a0c0f0c0c0c0c0a090b0f0b0c0c11110e100b0a120b0b0c080a0905050511120c0a
0b0c0a081310131013100c0b0b151509040404040808080a0a091219040806060a03070c0c100c110b15110f0d101010100c0c0f0c0c0c0f080c0c0c0c0b0c0c0c050b0b0b070707070707070b150b15
0705040c0604020000151505040d0c1112130d0b10070813130f0c100d00000000000b0f0b0c14110d0c130f0d0b120f0e0d14110b0910100f0c0c0b0c0b1a16100d131114110d0a0d0c0c0c0c151410
0c0c0c0c0c0a080d0b120f0b090d0b0c0a0c0a0e0d0f0c110e1612100d0d0a0a090c0b0c0b0b0b110f0e0d0e0c0f0c110e110e08120f0e0b0e0c0f0c0f0c0e0c120f080d0b0d0b12120b0b0f0b0f0b12
0f0b090b0a0f0c0f0c0f0c0f0c0f0c0d0a0c0b0c0b0c0b0e0c0b0811100a080d0b0b0b0c0c121212100c0a141215120f0c0e0e0b090e0c0d0b0d0b0d0b0d0b0d0b0d0b0d0b0d0b0d0b0d0b0d0b0d0b0b
0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b080508050f0c0f0c0f0c0f0c0f0c0f0c0f0c0f0c0f0c0f0c0f0c0f0c0f0c0f0c100d100d100d100d100d0c0b0c0b0c0b0c000000000000000000040404161a0d00
0b0606080e0c120f0407070c0c060706080c0c0c0c0c0c0c0c0c0c06060c0c0c09130d0e0d0f0c0b0f0f08060d0b1310100d100d0b0b0f0d130c0c0c0708070c090d0c0d0a0d0c070b0d06060b06140d
0d0d0d090a070d0b100b0b0a080c080c06060c0c0c0c0c0b0d12070b0c07120b090c07070d0d0e060507080b101010090d0d0d0d0d0d130d0c0c0c0c080808080f1010101010100c100f0f0f0f0c0d0d
0c0c0c0c0c0c120a0c0c0c0c060606060d0d0d0d0d0d0d0c0d0d0d0d0d0b0d0b0d0c0d0c0d0c0d0a0d0a0d0a0d0a0f0d0f0d0c0c0c0c0c0c0c0c0c0c0f0b0f0b0f0b0f0b0f0d0f0d0806080608060806
08060d0c06060d0b0b0b060b060b060b070b06100d100d100d0f100d100d100d100d14150d090d090d090b0a0b0a0b0a0b0a0b070b070b070f0d0f0d0f0d0f0d0f0d0f0d13100c0b0c0c0a0c0a0c0a06
0c0d0c1312100d0b0a0d0d0c0d060d040d0d0d0d0d060d110a110f11070d0e0c0d0c0c0f10080d0d13100c100f0d0c0b0c100c1010080c0d0a0d070d0d0d0b0d0a0a0d0d070b0b0d0b0a0d0e0d0a0d0a
0d100c1010070d0d0d100c0f0c0d0b08080614150f0d0d0f0d0d0e0c0e0c120c10100d0f130f100f0d0d0b0d100c0f0f16160f120e0d170d0c0d0c090c0c100a0d0d0b0c100d0d0d0d0a0a0b100b0d0d
14140e110c0a130c0c0d090a0a06060612130d0b0b0d0a091310131013100c0b0b161609040405040808080b0b08121a040807070b03080c0c110c120b1611100e111111110d0d0f0d0c0c0f080c0c0c
0c0d0d0d0c060b0b0b070707070707070b160b160706040c0604020000161606040d0c1213140d0c1107091414100d100e00000000000c100c0d16110e0d14100d0c13110e0e14120c0a1010100d0d0b
0d0b1a17110e151116120d0a0d0c0d0d0d1615100d0d0d0d0d0b090e0c12100c0a0e0c0d0b0d0b0e0d0f0d110f1713100e0d0a0b0a0c0a0c0a0c0b120f0f0d0f0d0f0d120e120e0812100f0c0f0c0f0d
0f0d0f0d1310080d0c0d0c13120c0c0f0c0f0c12100c0a0c0a100d100d100d100d100d0d0a0d0b0d0b0d0b0f0d0c0912110b090d0c0c0b0d0d141314110d0b16121613100d0f0f0c0a0f0c0d0c0d0c0d
0c0d0c0d0c0d0c0d0c0d0c0d0c0d0c0d0c0d0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c08060806100d100d100d100d100d100d100d100d100d100d100d100d0f0d0f0d100e100e100e100e100e0c0b0c
0b0c0b0d000000000000000000050505171c0e000c0606080f0d13100407070d0d060706090d0d0d0d0d0d0d0d0d0d06060d0d0d0a140e0e0e100c0b101008060d0b1411110d110e0c0c100d140d0c0c
0709070c090d0c0d0b0d0c080c0e06060b06140e0d0d0d090a080e0b110b0b0a080c080d06060d0d0d0d0c0b0d13080b0d07130c0a0d08080d0e0f060508080b1111110a0e0e0e0e0e0e140e0c0c0c0c
08080808101111111111110d11101010100c0d0e0c0c0c0c0c0c130b0c0c0c0c060606060d0e0d0d0d0d0d0d0d0e0e0e0e0b0d0b0e0c0e0c0e0c0e0b0e0b0e0b0e0b100d100d0c0c0c0c0c0c0c0c0c0c
100c100c100c100c100e100e080608060806080608060e0c06060d0b0b0b060b060b060b080b06110e110e110e10110e110d110d110d14150e090e090e090c0a0c0a0c0a0c0a0c080c080c08100e100e
100e100e100e100e14110c0b0c0c0a0c0a0c0a070d0e0c1413110d0c0a0d0d0d0d060d040d0d0d0d0e060e120b120f12080e0e0c0d0c0c1011080d0e14110c11100d0c0c0c120d1211080c0d0a0e080e
0d0e0b0d0a0a0e0d080b0c0e0b0b0d0f0d0b0d0a0e100c1110080e0d0e100c100c0e0c0808061516100d0d100e0d0e0c0f0c120d11110d0f141011100d0e0c0d120d100f16160f130e0e180e0c0d0d09
0d0c100b0e0e0b0d110e0d0e0d0b0a0b100b0e0d141410120d0b130c0c0e090b0a06060614150e0b0b0e0b091411141114110c0b0c171709040405040808080a0a0a121b040808080b03080d0d110d12
0c1712110e111111110d0d100e0d0d10090d0d0d0d0d0e0e0d060c0c0c080808080808080c170c170806040d0605020000171706040e0c1214140e0c1208091515110d110f00000000000c110c0e1612
0e0d15100e0c1411100e16130d0b1211110d0d0b0d0b1c18120f151216130e0b0e0d0d0d0d1716110e0d0d0d0d0b090e0d12100d0b0e0c0d0b0d0b0f0d100e12101814110e0e0b0c0a0c0b0c0b0d0b13
100f0e0f0d100e130f130f0812100f0d0f0d100e100e0f0d1411080e0c0e0c14130c0c100c100c12100d0b0d0b110e110e110d110d110d0e0b0d0b0d0b0d0b0f0d0c0913120b090e0c0d0b0d0d141415
130e0c16141715110d100f0d0b0f0d0e0c0e0c0e0c0e0c0e0c0e0c0e0c0e0c0e0c0e0c0e0c0e0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c08060806110d110d110d110d110d110d110d110d110d110d11
0d110d100e100e110f110f110f110f110f0c0b0c0b0c0b0d000000000000000000050505181c0e000c06060a100d14110507070d0d060806090d0d0d0d0d0d0d0d0d0d06060d0d0d0a150f0f0e100d0c
111109070e0c1512120e120e0d0d110e150d0d0d0709070d0a0e0d0e0b0e0d080d0e06060c06160e0e0e0e0a0b080e0c120c0c0b090d090d06060d0d0d0d0d0c0e14080c0d08140c0a0d08080e0e1006
0508080c1212120a0f0f0f0f0f0f140e0d0d0d0d09090909101212121212120d12111111110d0e0e0d0d0d0d0d0d140b0d0d0d0d060606060e0e0e0e0e0e0e0d0e0e0e0e0e0c0e0c0f0d0f0d0f0d0e0b
0e0b0e0b0e0b100e100e0d0d0d0d0d0d0d0d0d0d110d110d110d110d110e110e090609060906090609060f0c07060e0c0c0c060c060c060c080c06120e120e120e10120e120e120e120e15160e0a0e0a
0e0a0d0b0d0b0d0b0d0b0d080d080d08110e110e110e110e110e110e15120d0c0d0d0b0d0b0d0b070d0f0d1414120e0d0b0e0e0d0e060e040e0e0e0e0f060e120b131013080f0f0d0e0d0d1112090e0f
15120d12110e0d0d0d130d1312090d0e0b0e080e0e0e0c0e0b0b0e0e080c0c0e0c0b0e0f0e0b0e0b0e100d1212080e0e0e120d110d0e0d0909071617110e0e110f0e0f0d100d140d12120e1015111211
0e0e0d0e130d1110171710140f0e190e0d0e0d0a0d0d100b0e0e0c0d110e0e0e0e0b0a0c100c0e0e141410120d0b140d0d0e0a0b0b06060614150e0c0c0e0c0a1512151215120d0c0c18180a04040504
0909090b0b0a121c050a08080c03080d0d120d130c1813120f121212120e0e110e0d0d11090d0d0d0d0f0e0e0d060c0c0c080808080808080c180c180806040d0605020000181806040e0d1415160f0d
13080a1616120e110f00000000000d120d0e17130f0e15110f0d1512110e17140d0b1312120e0e0c0e0c1c19130f161317130e0b0f0d0e0e0e1817120e0e0e0e0e0c0a0f0d14100d0b0f0d0e0c0e0c10
0e110e12101915120f0e0b0d0a0d0c0d0c0d0c1411100e100e110e130f130f091410100d100d110e110e100e1511090f0d0f0d14140d0d110d110d14100d0b0d0b120e120e120e120e120e0e0b0e0c0e
0c0e0c100e0d0a14120c0a0e0d0d0c0e0e151515130d0c17141815110e110f0d0b100d0f0d0f0d0f0d0f0d0f0d0f0d0f0d0f0d0f0d0f0d0f0d0f0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0906090612
0e120e120e120e120e120e120e120e120e120e120e120e110e110e110f110f110f110f110f0d0c0d0c0d0c0e00000000000000000005050500000003000000030000001c00010000000002d800030001
000004e2000402bc00000090008000060010007e017f019201a101b001f001ff021902bc02c702c902dd02f3030103030309030f0323038a038c03a103ce03d203d6040d044f045f0486049105131e01
1e3f1e851ef11ef91f4d200b2015201e2022202620302033203a203c2044207f20a420a720ac21052113211621222126212e215e22022206220f2212221a221e222b22482260226525cafb04fefffffd
ffff0000002000a0019201a001af01f001fa021802bc02c602c902d802f3030003030309030f03230384038c038e03a303d103d60400040e04500460048804921e001e3e1e801ea01ef21f4d20002013
201720202026203020322039203c2044207f20a320a720ab21052113211621222126212e215b22022206220f2211221a221e222b22482260226425cafb01fefffffcffffffe3ffc2ffb000bf00b20061
ff49ff31ff96fe85fe84fe76ff68ff63ff62ff5d0067ff44fdd0fdcffdcefdcdfe82fe7f0000fd9a0000fe0c0000fe09e458e418e37ae47d0000e30de242e1efe1eee1ede1eae1e1e1e0e1dbe1dae1d3
e199e176e1740000e118e10be109e0fee0fbe0f4e0c8e025e022e01ae019e012e00fe003dfe7dfd0dfcddc690000034f0253000100000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000060000000780000009400000000000000000000009c00000000000000000000000000000000000000000000000000000000008e0000000000000000
00000000000000000000000000000000000000000000000000000000006c0000000000000268019c019d019e019f01a001a101a201a301a401a501a601a70269026a01ea01eb01ec01ed01ee01ef01f0
01f101f201f301f401f5026b01f601f70293029402950296029702980299029a01f801f902000201036f037003710372037303740375021c02340235025d025e0006020a000000000100000100000000
000000000000000000000001000200020000000000020000000000000000000000000000000000000000000000000000000000000001000000000003000400050006000700080009000a000b000c000d
000e000f0010001100120013001400150016001700180019001a001b001c001d001e001f0020002100220023002400250026002700280029002a002b002c002d002e002f003000310032003300340035
0036003700380039003a003b003c003d003e003f0040004100420043004400450046004700480049004a004b004c004d004e004f0050005100520053005400550056005700580059005a005b005c005d
005e005f006000610000008600870089008b00930098009e00a300a200a400a600a500a700a900ab00aa00ac00ad00af00ae00b000b100b300b500b400b600b800b700bc00bb00bd00be020d00720064
00650069020f007800a10070006b02200076006a02300088009a022d007302310232006700770227022a0229018d022e006c007c022100a800ba00810063006e022c0142022f0228006d007d02100062
0082008500970114011502020203020a020b0206020700b9023300c1013a0217021c0214021502340235020e00790208020c02110084008c0083008d008a008f00900091008e0095009600000094009c
009d009b00f3014b01520071014e014f0150007a01530151014c000402bc00000090008000060010007e017f019201a101b001f001ff021902bc02c702c902dd02f3030103030309030f0323038a038c
03a103ce03d203d6040d044f045f0486049105131e011e3f1e851ef11ef91f4d200b2015201e2022202620302033203a203c2044207f20a420a720ac21052113211621222126212e215e22022206220f
2212221a221e222b22482260226525cafb04fefffffdffff0000002000a0019201a001af01f001fa021802bc02c602c902d802f3030003030309030f03230384038c038e03a303d103d60400040e0450
0460048804921e001e3e1e801ea01ef21f4d20002013201720202026203020322039203c2044207f20a320a720ab21052113211621222126212e215b22022206220f2211221a221e222b224822602264
25cafb01fefffffcffffffe3ffc2ffb000bf00b20061ff49ff31ff96fe85fe84fe76ff68ff63ff62ff5d0067ff44fdd0fdcffdcefdcdfe82fe7f0000fd9a0000fe0c0000fe09e458e418e37ae47d0000
e30de242e1efe1eee1ede1eae1e1e1e0e1dbe1dae1d3e199e176e1740000e118e10be109e0fee0fbe0f4e0c8e025e022e01ae019e012e00fe003dfe7dfd0dfcddc690000034f02530001000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000780000009400000000000000000000009c00000000000000000000000000000000
000000000000000000000000008e000000000000000000000000000000000000000000000000000000000000000000000000006c0000000000000268019c019d019e019f01a001a101a201a301a401a5
01a601a70269026a01ea01eb01ec01ed01ee01ef01f001f101f201f301f401f5026b01f601f70293029402950296029702980299029a01f801f902000201036f037003710372037303740375021c0234
0235025d025e0000404559585554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363531302f2e2d2c28272625242322211f181411100f0e0d0b0a090807060504030201002c
4523466020b02660b004262348482d2c452346236120b02661b004262348482d2c45234660b0206120b04660b004262348482d2c4523462361b0206020b02661b02061b004262348482d2c45234660b0
406120b06660b004262348482d2c4523462361b0406020b02661b04061b004262348482d2c0110203c003c2d2c20452320b0cd442320b8015a51582320b08d44235920b0ed51582320b04d44235920b0
042651582320b00d44235921212d2c20204518684420b001602045b04676688a4560442d2c01b10b0a432343650a2d2c00b10a0b4323430b2d2c00b0282370b101283e01b0282370b10228453ab10200
080d2d2c2045b00325456164b050515845441b2121592d2c49b00e23442d2c2045b0004360442d2c01b00643b00743650a2d2c2069b04061b0008b20b12cc08a8cb8100062602b0c642364615c58b003
61592d2c8a03458a8a87b0112bb0292344b0297ae4182d2c4565b02c234445b02b23442d2c4b525845441b2121592d2c4b515845441b2121592d2c01b005251023208af500b0016023edec2d2c01b005
251023208af500b0016123edec2d2c01b0062510f500edec2d2c462346608a8a462320468a608a61b8ff8062232010238ab10c0c8a70456020b0005058b00161b8ffba8b1bb0468c59b0106068013a2d
2c2045b0032546524bb013515b58b0022546206861b00325b003253f2321381b2111592d2c2045b00325465058b0022546206861b00325b003253f2321381b2111592d2c00b00743b006430b2d2c2121
0c6423648bb84000622d2c21b08051580c6423648bb82000621bb200402f2b59b002602d2c21b0c051580c6423648bb81555621bb200802f2b59b002602d2c0c6423648bb84000626023212d2c4b5358
8ab004254964234569b0408b61b08062b020616ab00e23442310b00ef61b21238a121120392f592d2c4b535820b0032549646920b00526b0062549642361b08062b020616ab00e2344b0042610b00ef6
8a10b00e2344b00ef6b00e2344b00eed1b8ab00426111220392320392f2f592d2c4523456023456023456023766818b08062202d2cb0482b2d2c2045b0005458b040442045b04061441b2121592d2c45
b1302f4523456160b0016069442d2c4b5158b02f2370b01423421b2121592d2c4b515820b0032545695358441b2121591b2121592d2c45b01443b0006063b0016069442d2cb02f45442d2c452320458a
60442d2c45234560442d2c4b235158b90033ffe0b134201bb3330034005944442d2cb0164358b00326458a586466b01f601b64b020606620581b21b04059b001615923586559b02923442310b029e01b
2121212121592d2cb0024354584b53234b515a58381b2121591b21212121592d2cb0164358b004254564b020606620581b21b04059b0016123581b6559b0292344b00525b00825082058021b0359b004
2510b005252046b0042523423cb00425b0072508b0072510b006252046b00425b0016023423c2058011b0059b0042510b00525b029e0b02920456544b0072510b00625b029e0b00525b0082508205802
1b0359b00525b003254348b00425b0072508b00625b00325b0016043481b2159212121212121212d2c02b00425202046b004252342b0052508b003254548212121212d2c02b0032520b0042508b00225
43482121212d2c452320451820b00050205823652359236820b040505821b04059235865598a60442d2c4b53234b515a5820458a60441b2121592d2c4b545820458a60441b2121592d2c4b53234b515a
58381b2121592d2cb000214b5458381b2121592d2cb002435458b0462b1b21212121592d2cb002435458b0472b1b212121592d2cb002435458b0482b1b21212121592d2cb002435458b0492b1b212121
592d2c208a08234b538a4b515a5823381b2121592d2c00b0022549b000535820b04038111b21592d2c014623466023466123201020468a61b8ff80628ab140408a704560683a2d2c208a2349648a2353
583c1b21592d2c4b52587d1b7a592d2cb012004b014b54422d2cb1020042b123018851b1400188535a58b910000020885458b202010243604259b12401885158b920000040885458b2020202436042b1
2401885458b2022002436042004b014b5258b2020802436042591bb940000080885458b202040243604259b94000008063b80100885458b202080243604259b94000010063b80200885458b202100243
604259b94000020063b80400885458b202400243604259595959592d2c451868234b51582320452064b04050587c59688a6059442d2cb00016b00225b0022501b001233e00b002233eb10102060cb00a
236542b00b234201b001233f00b002233fb10102060cb006236542b0072342b00116012d2c7a8a104523f5182d000000401009f803ff1f8ff79ff7027ff30160f201b8ffe8402beb0c1046df33dd55de
ffdc5530dd01dd010355dc03fa1f30c2016fc0efc002fcb6181f30b70160b780b702b8ffc04038b70f1346e7b1011faf2faf3faf034faf5faf6faf0340af0f1346ac51181f1f9c5f9c02e09b01032b9a
011f9a01909aa09a02739a839a0205b8ffea40199a090b46af97bf9702032b96011f96019f96af96027c960105b8ffea408596090b462f923f924f920340920c0f462f91019f91018786181f407c507c
020310742074307403027401f274010a6f01ff6f01a96f01976f01756f856f024b6f010a6e01ff6e01a96e01976e014b6e01061a0118551913ff1f0704ff1f0603ff1f3f67011f672f673f67ff670440
665066a066b066043f65010f65af650205a064e0640203b8ffc0404f64060a46615f2b1f605f471f5f50221ff75b01ec5b01545b845b02495b013b5b01f95a01ef5a016b5a014b5a013b5a0106133312
5505010355043303551f03010f033f03af03030f571f572f570303b8ffc0b356121546b8ffe0b356070b46b8ffc0b354121546b8ffc0406d54060b4652502b1f3f504f505f5003fa4801ef4801874801
6548015648013a4801fa4701ef47018747013b4701061c1bff1f1633155511010f5510330f550201005501470055fbfa2b1ffa1b121f0f0f011f0fcf0f020f0fff0f02066f007f00af00ef0004100001
8016010501b80190b154532b2b4bb807ff524bb006505bb00188b02553b00188b040515ab00688b000555a5b58b101018e59858d8d00421d4bb0325358b0601d594bb0645358b0401d594bb0805358b0
101db11600425973735e7374752b2b2b2b2b2b2b2b015f7373737373737373737300732b012b2b2b2b5f730073742b2b2b015f73737373737373737373002b2b2b012b5f735e7374737374002b2b2b2b
015f737373737473737373737400737474015f732b0073742b73012b5f737374745f732b5f73737474005f7373012b002b73740173002b73742b017300732b2b732b2b012b737373002b185e0614000b
004e05b60017007505b605cd0000000000000000000000000000044a0014008f0000ffec00000000ffec00000000ffec0000fe14fe000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000ac00b600bc000000d5000000000000005500830097009f
007d00e500ae00ae007100710000000000ba00c500ba0000000000a4009f008c0000000000c700c7007d007d00000000000000000000000000b000b9008a00000000009b00a6008f0077000000000000
000000000096000000000000000000000069006e009000b400c100d500000000000000000066006f0078009600c000d5014700000000000000fe013a00c5007800fe011601f600000000000000000000
00000000000000000000000000ee00000096008800ae00960089010c009601180000031d0094025a00820396000000a8008c00000000027900d900b4010a00000183006d007f00a000000000006d0088
00000000000000000000000000000000009300a00000008200890000000000000000000005b6fc940011ffef0083008f00000000006d007b0000000000000000000000bc01aa03540000000000bc00b6
01d7019500000096010000ae05b6febcfe6ffe83006f02ad0000002a002a002a002a006f00a1012901e202710329034d037c03ac03e2040f0444045b049504b40506053f05940612066506cc073f0767
0811088208da093d097909bb09f70a6a0b340ba00c170c740cb30cec0d390d970dcd0e100e490e970eb70f2b0f7c0fd51022108b10fc116611a711e5123812ee134d139e13d113f71417143b14591470
14951508156915ae160e166b16ce17a217e31814185f18af18c81939197719c01a221a831ac91b391b8e1bcf1c271cd41d651dce1e1b1e751e991ef51f481f481f901ff4206a2101217421a4225d22a1
234a23b424082435243d250c2523257e25ba260a267f26a426ee272b276527ab27e2282a287a28a328c828f7296d2985299c29b429cb29e42a092a722a852a9d2ab42acc2ae52afd2b142b2b2b442ba8
2bbf2bd72bee2c052c1d2c362c9b2d1c2d342d4b2d632d7c2d932ddd2e782e8f2ea52ebb2ed12ee92f012fb12fc42fdb2ff13007301f3037304e3065307e310e3124313b31513168317f319832093282
329932af32c532de32f4335933713389339f33b633cc33e334003417342f3446345e3475348e34a534bd34d534e834f035693580359635ad35c335da35f136053626363d3653366a3680369736ae36c5
36dd36f03706371e373b378c37e737fe3815382c3843385a38713884389838af38c838f13915392c39433956396939b239ca39e739fa3a0e3a263a3e3a513a743ac13b0e3b253b3b3b4f3b623b793b90
3bb23c133c6c3c833c993cb03cc73ce03cf83d693df73e0f3e253e383e4c3e643e7c3e933ea93ec03ed63eea3efd3f143f2a3f3d3f513f683f7a3fd1403b405340694081409740ae40c440dd40f5410e
41264139414c4163417a419141a741c041d741ed4204421b42324248427b42df43c1449644ae44c544dc44f245054518454e4584459c45d745fe464e467b46c24705473247a347bc47f6481b48414882
48b348e7490f492849304938496a49b649be49c649ce4a3d4a454a4d4a994aa14aa94af64afe4b294b314b794b814b894c184c204c8e4d104d294d424d584d6e4d844d9c4db14e234ea54f044f8c5008
506e50ae5120515e516651e2523052725312531a536e53c85437549054da551c559a562e568f57195732574b57615777579557ae5828583f58aa58b258ba58d358db597259d15a2f5a465a5d5aa65aae
5b015b095b115b845b8c5c195c9c5cfb5d135d5b5dbf5dc75dcf5dd75ddf5de75def5df75e675e6f5e775eac5ef35f3b5f915fe7603d608b60ee616561e261ea626562d762ff6365636d63e2646064a4
64bb64f5654065a565eb65f3661c6624662c666b667366f066f86732677267bd6816687368d5691f698869fc6a536a6b6ada6af06b4c6b546b5c6b756b7d6bf56c546cae6cc46cda6d1f6d546d7f6d97
6dae6dc56ddc6df66e106e286e406e5b6e766e916eba6ee56f116f446f726fce702a709470ef717571b8725d732a7332733a736d73a273b473d27415747074f77581761576c3774177f7787e788678e9
791279417970799b7a157a627a917adb7b017b2d7bc97c1a7ca37d1b7d607da47df07e0f7e2d7e707ea37ec97eee7f137f567f9f7ffc805b808a81278191819181918191819181918191819181918191
819181918191819182db834e835a8362840b84888524853b85528566857a85a785f48647867486a08715877c87db883e8863888888cf890e892e8946895e8975898c8a298ae48b658bda8c4b8cbc8d40
8dd38e6b8f178fb8905c910a91ba92a49393939b93a39410947094d095389552956c962c964296c89746984a993399f99ae69b489b979c569c9b9cde9d0d9d3e9e1d9ebb9f619fe9a044a0a8a124a1b2
a211a261a2cfa333a3d1a486a49ca4b2a526a591a5ffa672a6d8a744a78ea7e0a82ea887a8d0a91ea989a9fcaaadab57ab6bab7fabceac1dac25ac88acf9ad68ae0faecfaf36af94aff3b04fb0b6b119
b167b1adb22fb2bfb34cb3e6b3eeb405b41cb48ab4f9b56cb5c4b616b677b6beb717b771b7bdb841b8c1b8c9b8e0b8f6b90fb927b92fb937b94eb964b9caba28ba42ba5bba74ba8ebaa8bac2bb2cbb94
bbacbbc3bbddbbf6bc0fbc28bc30bc38bc51bc69bc83bc9dbcb5bccbbce5bcfdbd16bd2ebd47bd5fbda6bde1bdfabe13be25be37be50be66bee7bf9abfe4bfecc066c0f2c18bc21dc2a2c328c3bbc428
c484c4ebc54fc5cbc626c687c705c789c79dc7b1c7c8c7dfc7f6c80dc827c840c85ac873c88dc8a6c8c0c8d9c8fdc91ec937c950c969c982c99bc9b4c9cdc9e6ca08ca29ca40ca57ca6eca85ca9dcab3
cacdcae6cb00cb19cb33cb4ccb66cb7fcba3cbc4cbdbcbf2cc0acc22cc39cc51cc68cc7fcc98ccb2cccbcce5ccfecd18cd31cd4bcd6dcd91cda8cdbfcdd7cdeece06ce1ece36ce4dce65ce7dce94ceac
cec3cedacef2cf07cf1fcf34cf4acf60cf78cf8dcfa5cfbdcfd5cfebd002d019d030d046d05ad09dd0f6d14bd1bad22fd27ad2c8d329d392d3bfd3e7d40f0000000200c10000040a05b600030007002b
400b0502020904700380030203b8ffc04009060948030403070003003f322f33012f2b5d3311332f3331301321112137211121c10349fcb7680279fd8705b6fa4a6804e600020093ffe3019105b60003
0017003ab90001fff040130a1448101980199019a01904030e9a04020204b8ffc0400a070a480401099b130203003f2ff5ce012f2b332f10e1325d31302b0123033303343e0233321e0215140e022322
2e0201507933dff014222e1b1a2f221414222f1a1b2e2214019e0418fab92635210f0f21352625352210102235000002008503a602b205b600030007003740230498070709d009e009022f096f097f09
03009800031003e003f0030403060202070303003f33332f33012f5de15d5d12392fe131300103230321032303014a297329022d29722905b6fdf00210fdf002100000020033000004f805b6001b001f
0099405803031a1a18161e1d070406171706190001040405b1181821151f1c0804091414120f0e0b0413b10a50100110100c0c09500a010a1c01480d010dae0c08040c1f0010ae111915113f114f11df
11030c110c11051713060a05002f333f331239392f2f5d11333310e1323211333310e15d3232012f5d33332f332f5d10e41739321112173911332fe4173932331112173911332f332f31300103211521
03231321032313213521132135211333032113330321150121132103d73f0118fecd529354fedd52904efefe011d41feee012b52935201255490540106fceb012340fedd037dfeb889fe5401acfe5401
ac8901488901b0fe5001b0fe5089feb801480003007bff8903d90612002d0036003f00b44033342f2901292f21012106702f3c013c2f1e011e1320070107070d2e6e240f001f0002ff00010000010700
410d0d376ee0190119b8ffc04030080b4819331414293c3c132e37083d28732125211f400e11481f1f1e5021010f211f21020821341373080e0806060508b8ffc0b30a0d4808002f2b33332f113310e1
322f5e5d5d33332f2b113310e13212393912391133331133012f2b5de1332f10d65e5d5d7132e111392f5d33335d335de1325d325d32313001140e0207152335222e0227351e0333112e0335343e0237
35331516161707262627111e0307342e022711363601141e021711060603d9325d85548a3266605420215760652f5983562a315b814f8a64a94342388c4a58875b2eb0142b46335d5bfe121128423159
5301be467254370ce6dd09121a11ac10211a1101b21e42556e4a436f533509b4b0052a1f91192906fe5a1f42536b4821372d2612fe8b0e6202a324392f2611017110590000050066ffec063305cb0009
001d0027003b003f005db23c103eb8fff040333c3e3c3e28141eb432b523b428410f410105b40ab500b410142014301403143f063e1825b637b721b62d1903b60fb707b61907003fe1f4e13fe1f4e13f
3f012f5de1f4e15d10dee1f4e1111239392f2f383831301314163332111023220605140e0223222e0235343e0233321e020114163332111023220605140e0223222e0235343e0233321e0201012301fa
47509c9c504701c7244a734f49704c262349714e4b714d2701ac47509c9c504701c6234a734f4a704b262349714e4b714c27ff00fcd59e032c0402a5a5014a0148a3a56cac763f3f76ac6c6caa753e3e
75aafd4aa5a401490148a3a56cab763f3f76ab6c6caa753e3e75aa0392fa4a05b6000003006dffec057d05cd0011002100530080404d2718174a04492c49480a4736414742204201364236421d053b3c
4704480047002c102c02082c2c2220480148551d4810222022022241413112514f162718174a040f473c3b05043149150f5031002fe13f1217391217393fe111392f012f5de110c65d11392f5e5de111
17391239392f2f5d10e110e1113311121739313001141e02173e0335342e0223220613323e0237010e0315141e0225343e02372e0335343e0233321e0215140e0207013e0337330e03070123270e0323
222e0201a6102134243b56381c192f422a5664873a62544820fe7d3450371c234260fe7d284d6f471f3c2d1c325e8a5853835b3032546d3c01601b2b221b0ab80f293541270115e1a831606c7c4e69a7
733d048d2241414325233e404629243d2c1959fbaf1728361f0197213f485538365b4124f04e7a64562a244d5763394b77532b2b53774b406d5d4f24fe8c1d3c444e2f426f625529fedbac2d47311b35
67950001008503a6014a05b60003002a401cc005d005e005032f056f0502009800031003e003f003040302020303003f332f012f5de15d5d313001032303014a29732905b6fdf002100000010052febc
022b05b60013001a400d060ef209f03f0001000ef905f8003f3f012f5de1e432313013343e023733060215141e0217232e0352244a714eac8c9125476a45aa4e714a2402317df3e5d35dc1fe32f477ec
e2d45e5acee1f0000001003dfebc021705b60013001c400e060ef20bf0b0000100150ef805f9003f3f0110de5de1e432313001140e0207233e0335340227331e030217244b714eaa456a4824908dac4e
714b2402317cf0e1ce5a5ed4e2ec77f401cec15dd3e5f30000010052027704140614000e002440151f10010098000e800e900e03080e1f060106060000003f322f5d012f5e5de55d3130010325170513
07030327132537050302982b018d1afe86f5b2b09eb8f2fe891d01872b0614fe776fc11cfeba600166fe9a6001461cc16f018900000100660106040204a2000b00294018100d010609aa03ef00012000
6000a00003000900ad0603b3003f33e132012f5d5d32e1325d313001213521113311211521112301e9fe7d0183960183fe7d960287960185fe7b96fe7f000001003ffef8017900ee000c00384014cf0e
01100e900ea00e031b0c2b0c020c01970607b8ffc0400d1014485f070110070107069c0c002fed012f5d5d2b33ed325d5d5d313025170e0307233e0337016a0f0e272f33198a0f1d1b1608ee17367a7c
7b383d84837d35000001005201d10242027900030015400902054000010000b901002fe1012f5d10ce3130133521155201f001d1a8a800010093ffe3019100fa00130035401b80159015a01503111501
0a96c000d0000234004400640074000400b8ffc0b6070a4800059b0f002fed012f2b5d5ded5d5d313037343e0233321e0215140e0223222e029314222e1b1a2f221414222f1a1b2e22146f2635210f0f
2135262535221010223500010014000002e705b60003001eb10102b8fff04009020300100005010003003f2f11013338322f383331300101230102e7fde0b3022105b6fa4a05b60000020062ffec0408
05cd00130027002640151e6f0029102901146f200a010a23730f0719730519003fe13fe1012f5de15d10dee13130011402060623222626023534123636333216161205141e0233323e0235342e022322
0e0204083371b27f76af7339336fb17e77b0743afd131e426b4d4d6c451f1f456c4d4d6b421e02ddb1fee8c26666c20118b1b10118c16665c1fee8b296e0954b4a94e19796e0944a4a94e000000100b2
000002c705b60010003540214012010f010e0e006ebf01ff01027e010100011001200140010406010d0f060018003f3fcd012f5e5d5d5de1332f11335d3130212311343e02370e03070727013302c7b0
01030301111a1b1e159460017f9603912b62615922121a181b12797b012b00010060000003f005cb0023003c402023086f1b1b25102501226f0121011111200101010822100d7316070222740118003f
e1323fe1331239012f5d332f113310ed5d11332fe1333130212135013e0335342e0223220607273e0333321e0215140e020701152103f0fc70015e4b76532c223f56355f994566285c6a7641609b6c3b
355d814bfee702b19c017d518680814c3b5a3f204d3c77243f2e1b3665915b559a959651fed5080000010052ffec03ee05cb0039005d400b21302130121a6f09276f00b8ffc040281417480000093b20
3b014f120112062073ab21017921010b2101082121152f2c7335071573120e19003f33e13fe13312392f5e5d5d5de139012f5d5d10ce322f2be110e11139392f2f313001140e020715161615140e0223
22262735161633323e0235342e0223233533323e0235342e0223220607273e0333321e0203c12e537447b1b84184ca8a6dc15557cb5d5c86572935628d598585517e552c24425c386ba34a5c265d6e7d
466ca36e380460497858390c0616b59160a07440222daa2e32284a6c4344613f1e97284a663d3452391e43367d1f362918366185000200170000043f05be000a0018004e402c095600010000026e110c
0b0720030103031a101a017718871802185f05010509061874010505021107060218003f3f3312392f33e13232012f5d335d5d12392f5d33333333e1322f5d3231300123112311213501331133211134
3e0237230e030701043fd5b0fd5d0297bcd5fe7b03040501090715191a0bfe650148feb801489f03d7fc300164387b7566221431312e10fda00000010083ffec03f605b6002a004e4018261a6f052c10
2c01272424286823015923012323f00f010fb8ffc04012080b480f1d73000015277424061573100a19003f33e13fe112392fe1012f2b5d332f5d5d333311335d10dee133313001321e0215140e022322
2e0227351e0333323e0235342623220e02072713211521033636022163ab7f484486c58033635b5221215962632a4f7c562eb0a81b3f3f39155a3702b2fdec2720690381376ca06972b67e430a131e14
ac1724180d254e76518f97050809043902b0a6fe5d060e0000020071ffec040a05cb002b003f00374020316e0c2241104101173b6f000010002000030036751d1d072c73271910730707003fe13fe111
392fe1012f5de1325d10de32e1313013343e0433321e021715262623220e0407333e0333321e0215140e0223222e0201323e0235342e0223220e0215141e027115355c8ec685132e2f2b1123582b5a89
64432a14030c14394c5f3b5f9a6c3b3e74a46664af804a01db3c63482721426342436f4e2b25496e027169d0bfa47945020507059b0c0c2b4e6c839450243f2d1a3b72a56a72b67f444ea0f2feb92953
7f57466f4e2a2f4b603043856a430001005a0000040605b60006002bb10600b8fff040110000020105081008010205027403060018003f3fe132012f5d10ce3211392f38333130210121352115010119
0233fd0e03acfdd50510a691fadb0003006affec040005cd0027003a004a008040531e23326e0f0a28486ec305d30502b5050105050f4c104c01286e193e6ed52301cc2301ba23012323101920190219
0a1e6838983802593801283838384838033893430126435643024343002d7314193b730007003fe13fe111392f5d5dc15d5d5d3939012f5d332f5d5d5de110e15d10ce322f5d5de1123910e111393130
01321e0215140e02071e0315140e0223222e0235343e02372e0335343e0203141e0233323e0235342e022727060601220615141e02173e03353426023554957142284660383a6f57354379a9666eab75
3d2d4c683a31563f25437295c720446848466b48242749663f1e7e8001166a7d233e573330553f247e05cd2c588458436c57451c1f4c5f76495c9568383665925c4b78604a1c1f495a6d425783582cfb
a635593f2323415c38345448401f0e3c9b03546a6539524033181634425436656a000002006affec040405cb0029003d0035401e39156f003f103f012f6e0c10202020022034751b1b072a7325071075
071a003fe13fe111392fe1012f5d33e15d10dee132313001140e0423222e022735161633323e0237230e0323222e0235343e0233321e0201220e0215141e0233323e0235342e02040415355c8ec68513
2e2e2c1123582b87ae662b050d14384c603b5f9a6c3b3f73a56665ae804afe253c63482721426342446e4e2b25496e034669d1bea57845020506059c0d0c5ea1d677243e2e1a3b72a56a72b77f444ea0
f3014728547f57466f4e2a2f4b603043856b420000020093ffe30191046600130027003e401c102980299029a029041e0a9614c000d0000234004400640074000400b8ffc0400b070a4800239b191005
9b0f002fed3fed012f2b5d5d33e5325d313037343e0233321e0215140e0223222e0211343e0233321e0215140e0223222e029314222e1b1a2f221414222f1a1b2e221414222e1b1a2f221414222f1a1b
2e22146f2635210f0f2135262535221010223503912735210e0e213527253422101022340002003ffef801910466000c00200061402f102280229022a022041796c00dd00d02640d740d02500d01440d
013b0d011f0d2f0d020d0d1b0c2b0c020c01970607b8ffc040111014485f0701100701071c9b1210069c0c002fed3fed012f5d5d2b33ed325d332f5d5d5d5d5d5de55d313025170e0307233e03370334
3e0233321e0215140e0223222e02016a0f0e272f33198a0f1d1b16081114222e1b1a2f221414222f1a1b2e2214ee17367a7c7b383d84837d3502ed2735210e0e213527253422101022340001006600ee
040204dd0006004e40300008400801400101010201050503066f007f0002300001000004200301500370038003d003f003053f03010003010603002f5e5d5d5d7133332f5d5d3212393d2f333301182f
5d5d10ce3130250135011501010402fc64039cfd2102dfee01a86601e1a0fe94febe0002006601ba040203e900030007005c403d07020940090104c60001bb0001a900018600017b0001680001420001
3900010004ad1f052f05027f0501000510050206050500adf001010f016f010201002f5d5de1332f5e5d5d71e1012f5d5d5d5d5d5d5d5d335d10ce323130133521150135211566039cfc64039c035495
95fe669696000001006600ee040204dd0006004e40300508400801400601060504010103006f067f0602300601060602200301500370038003d003f003053f03010003010603002f5e5d5d5d7133332f
5d5d3312393d2f333301182f5d5d10ce3130130101350115016602e0fd20039cfc64018f0142016ca0fe1f66fe5800020025ffe3032505cb0027003b003e4021329a282827460000140b461c3d2f3d01
140b170f00010600002d9b371310511704003fe1332fe5322f5e5d1239012f5d10dee111392fe1332fe131300135343e02373e0335342e022322060727363633321e0215140e02070e03151503343e02
33321e0215140e0223222e0201190f27423230442b151e3955385396463f51bc615d9568381b3650363442260ebb14222e1b1a2f221414222f1a1b2e2214019e25395c504d2a2943454f35304f391f34
22912a3b33608b5743695a542f2d433f422c12fed12635210f0f213526253522101022350002006dff4a068105b600570068006f403f581760271f17017f2701172746462717034e31200001006a406a
013b404e014e2c0c5b120712641c0f121f12bf120306001c0107121c121c40365303404549002f33c13fc11239392f2f5e5d5e5d10c1113310c13232012f5dc15d10de71c11117392f2f2f5d5d10c110
c1313001140e0423222e0227230e0323222e0235343e0233321e0217030606141415141e0233323e0235342e02232204060215141e0233323e0237150606232224260235341236243332041612011416
33323e023737262623220e0206811325394c613a2d493421060412364759354d77522b3b6f9e622d5a52451717010115222b172e462f185698d17ba9fefeaf5a4f99e3933d776f642b56d882b3fee7c3
6676db0137c19c0106bf6afc156555374e321a040e1c4d2a4a653f1c02db3e7d716148291e3241232542311c38658e5665a87a44080e1108fe60161b1008033544280f3d688c4e8edd984f6fc7feefa2
97eaa0520e181f118d262c66c30119b3bc0145ee8865bdfef1fed585772d537345fd080d3a5e780000020000000004dd05bc000700140084402406054602014614010214034908014901010801000e0e
03000010070180079007d0070307b8ffc04018060a4807100707160f161f162f168f169f16df1606070304b8fff0401104025f0e200a0e480e0514140503040012003f323f392f12392be1012f38335e
5d11332f382b5d71331112393d2f1239395d5d1239395d5d33333130210321032301330101032e03270e030703041fa0fddfa2bc0219aa021afe6794061112120807121211069101c5fe3b05bcfa4402
6a01a812343c411f1f423d3311fe5800000300c70000048705b600170022002f0062403e0b1e5a050601e506f50602d606010606242a5b70118011021167317f318f310210310118245a1764300b2360
7918010b1801081818002460171222600003003fe13fe111392f5e5d5de1390110f6e1325d5d10f65de112392f5d5d71e13231301321321e0215140e0207151e0315140e0223211333323e0235342623
23111121323e0235342e0223c7018f80c38342274a6d4545795a34417bb06ffe1bbaf45472461f9aa6df010a58774920214b7c5c05b627578d673e6c5237090a0c2d4f7856649d6d3a034a1e3b593b78
68fd97fdf02848653d385e4325000001007dffec049805cb0023004c4014af0e010e401518480e0e18ba2001602070200220b8ffc04018060a48202025af2501055b18662421005f1d040d0a5f131300
3fe1333fe1330110f6e15d11332f2b5d5d12392f2b5d313001220e0215141e0233323637150e03232226260235341236363332161707262603196bae7b433b76b07659a04e274e55613ba4f09d4c57a9
faa26cc44f4e3f9405275198da898ddb964e2317a20f170e076cc60116a9a60114c66e2c2a9c202e000200c7000004fc05b6000c0017002640150d5b006719101901145a066418136007031460061200
3fe13fe10110f6e15d10f6e131300114020604232111213216161207342e0223231133200004fc60b6fef7a8fe92019799f8ae5fc5427eb875c9a20108010c02e9b9fee9bb5e05b65cb5fef4b692d58a
43fb89012400000100c7000003be05b6000b004240261408010808010400670d060a5a01640c095f4f06010f06af06020806060a055f02030a5f0112003fe13fe112392f5e5d71e10110f6e13210e632
11392f5d313021211121152111211521112103befd0902f7fdc30217fde9023d05b6a4fe3ca2fdf8000100c7000003be05b60009007040110808010f0301ff030180039003d0030303b8ffc04038070a
4803030b0f0b2f0b8f0baf0b040706005a01640a095f0f06010f063f066f06ff06040806401a1d480640101548060600055f02030012003f3fe112392f2b2b5e5d71e10110f6e1325e5d11332f2b5d5d
7112392f3130212311211521112115210181ba02f7fdc30217fde905b6a4fdfca4000001007dffec04f205cb002b0037401e2b2b0c295a1402672d102d011f5b0c662c2b5f0000241a5f1104245f0713
003fe13fe112392fe10110f6e15d10f632e111392f31300121110e032322262602353412362433321617072e0323220e0215141e0233323e02371121030e01e4377076824b9df2a6565fb6010bab6fcc
58482453585d2e7abc7f423778be862c493e371afed50304fd33121c130a69c30117aeac0116c3692c2aa2111e170e5198da8982d89c5605080b0501b400000100c7000004d505b6000b003d40230901
5a00650dc00d01bf0d01200d0108045a05640c035f0f08010808080a0603050012003f323f33392f5e5de10110f6e1325d5d5d10f6e132313021231121112311331121113304d5bafd66baba029aba02
aafd5605b6fd98026800000100520000026405b6000b005740260b0d2b0d027b0d9b0dab0dfb0d04540d012b0d3b0d4b0d031f0d0102080b0a5a0502c9030103b8fff840100d10480003010603090406
03030a0012003fc1323fc132012f5e5d2b5dc132f1c1325f5d5d5d5d7131302121353711273521150711170264fdeeacac0212acac6629049829666629fb68290001ff48fe7b017305b60013002f401c
df150160157015022f15010f5a0c0303000c100c02070c0d03075f00002fe13f012f5e5d332f10e15d5d5d31300322262735161633323e0235113311140e021d334c1c224e2d254b3d26bb3b6993fe7b
0d0ba0090b1332584405b6fa5e699a653100000100c7000004a205b6000c0064402d020c660c010c000a0b100b0b01000010000207001000000eb00e012f0e01100e0108045a05640d02100b104808b8
fff0400c0b10480208050a0603000512003f333f331239392b2b0110f6e1325d5d5d11332f385e5d33332f383311395d113331302123010711231133113701330104a2d3fe3d8bbaba7901c4d1fdf802
ba72fdb805b6fd25a80233fd8300000100c7000003be05b60005002340130407af0701100701035a0064060103035f0012003fe13f0110f6e15d5d11333130331133112115c7ba023d05b6faf0a60001
00c70000062f05b60019008b4013361901390001170e080c0f48390e010e115a19b8fff8401c0c0f481900080c0f48000d0d0c0910651b4f1b01201b010f1b01080bb8fff8401a0c0f48260b010b0208
5a09641a18010110091248010e0b03110cb8fff0b60912480c080012003f32322b323f33332b11330110f6e132325d2b5e5d5d5d10f611393911332b332be1325d2b3231305d5d210123161716161511
23112101330121112311343637363723010323fe450806040405ac0114019c06019e0114ba0403040308fe4105004a493f8b39fc9605b6fb5804a8fa4a037734863d4749fb02000100c70000050e05b6
0017005140290e28010101155a006519b019018f19010019101902270c010c03095a0a6418160210061848020b030db8fff0b60618480d0a0012003f32322b3f332b330110f6e132325d5d5d5d10f6e1
325d32313021230123161716161511231133013326272e03351133050ed7fd310806040405acd502cc07030401030301ae04ba4d4c418e39fce705b6fb4c4c4a2043433e1a0320000002007dffec0571
05cd00130027003440201e5b006729c02901bf29017029012f295f2902145b0a6628235f0f04195f0513003fe13fe10110f6e15d5d5d5d10f6e131300114020606232226260235341236363332161612
05141e0233323e0235342e0223220e02057151a0ed9ba3ef9d4c4c9ef0a39beba051fbd1346ba57272a56b32326aa47272a66c3402dda9feeac66c6cc60117aaaa0115c46b6bc5feebab89db99515199
db898ada97515197da00000200c70000043305b6000e00190046402c155b2800380048000300671bcf1b01401b010f1b01060f075a08641a0f603006400602060607196009030712003f3fe111392f5d
e10110f6e1325e5d5d5d10f65de1313001140e02232311231121321e020133323e0235342623230433377ecf9896ba016a86c27e3cfd4e815d8b5b2ea4aea0040a5ba8814dfdc705b6396da0fe672047
71518e890002007dfe62057105cd001d003100384022285b006733c03301bf33017033012f335f33021e5b1466322d5f1904235f050f1309002f3f33e13fe10110f6e15d5d5d5d10f6e1313001140e02
0716161707262627060623222626023534123636333216161205141e0233323e0235342e0223220e020571315f8e5d2b895a7967ad33112912a3ef9d4c4c9ef0a39beba051fbd1346ba57272a56b3232
6aa47272a66c3402dd83e2b584265e8f3c8e49c67f02026cc60117aaaa0115c46b6bc5feebab89db99515199db898ada97515197da00000200c7000004a005b6000f001c00824056090f190f02f90f01
0f080b0f480f0c090c01070c01165b08071807020707090e01e90ef90e020e080b0f480e0d100d1e3f1e8f1e9f1ebf1edf1e05201e0110015a02641d0c031060000001080000011c6003030e0112003f
333fe111392f5e5de112390110f6e1325d5d10ce38322b5d71322f5de112395e5d11332b5d7131300111231121201615140e02070123012733323e0235342e0223230181ba0164010afe31516837018e
dbfea1e5a45a7e512529537f57a0025cfda405b6ced157825d3e14fd71025c9e234567454864401d00010068ffec03c905cb00330042402759230123115a006735bf35ff35026035013f35012a5a091b
6634112a05275f2420040e60090513003f33e13f33e11239390110f632e15d5d5d10f6e1335d313001140e0223222627351e0333323635342e02272e0335343e023332161707262623220615141e0217
1e0303c94580b8736fc1412257606632a0991d497a5d598355294074a16177be4a4341a5587a861e4673545b895c2f018761996a372322b2101f180f78703650433f252353688454588a5f322d239c1d
2b71603953433b21244c607e000100140000041205b60007005e40320f0901d009014f09cf090210092009300903af06ef06028406010606075a024003e003020f03010803035702670277020302b8ff
c0400b070a480207035f04030012003f3fe132012f2b5d332f5e5d5d10e1322f5d5d5d5d5d71313021231121352115210271bbfe5e03fefe5f0512a4a400000100b8ffec04dd05b80017002f401c165a
016519b019016f19af19021019010e5a0b6418115f06130c0003003f323fe10110f6e15d5d5d10f6e131300111140e0223222e0235113311141633323e02371104dd4285c98880c48544bbadaf598052
280105b8fc4c72c490524d8ec77a03aefc48afc03662885103b8000100000000048b05b6000c006c401003020909040060017001b001f0010401b8ffc04016060a48011001010e2f0e7f0ebf0e030e40
0609480504b8fff0b40405040309b8ffe0b30a114809b8fff0400a06094809020312000103003f333f33332b2b3f33012f38332b5d11332f382b5d3312393d2f33333130013301230133011616173636
3703c5c6fe17bbfe19c501271d2a110f2e1f05b6fa4a05b6fc615ba94a4aa96100010014000006fe05b6002a00dfb61008151848100fb8fff8b51518480f0701b8fff840121518480100081518480016
1d081518481d1cb8fff8402f1518481c2514070104074407b40703070425142524254425542505071625251607031e0d000e01700e800ec00e030eb8ffc04018070a480e100e0e2c6f2c7f2c02202c30
2c020f2c01081f1eb8fff040131e16200a11481610060948160d001e03072525b8ffe0b30a114825b8fff0b606094825101d12003f33332b2b11333f3333332b2b012f38335e5d5d5d11332f382b5d71
331217393d2f2f2f5d5e5d7111332b332b11332b332b11332b332b31300133131e03173e033713330123032626272627060706060703230133131e03173e03370329c5e50f1d191306040c10130bc8c7
fe91bcfe0e1a0b0c0b0b0b0a190ef2bcfe7ec5df0c14110e05050f14170d05b6fca83870695e26265a6367310372fa4a03aa336c2f373433372f7036fc5c05b6fc872e63625b2625626c6f3100010000
0000046005b6000b0081401d090a100a0a00370b010b083805010502020100000170008000c0000300b8ffc04014070a48001000000d0f0d1f0d2f0d7f0d04080706b8fff0b306060304b8fff0401004
280201270801020804090603040012003f323f331239395d5d012f3832322f38335e5d11332f382b5d7133393d2f335d33335d1133182f383331302123010123010133010133010460d3fe9efe91bc01
c5fe5ac6014c014ebefe5b027bfd8502fc02bafdd1022ffd4c00000100000000043705b6000800734019ef0a010a40090c4808ab07019807014007011b07010f070107b8fff0402f070705018002014f
02011b02010210020200045a770587059705034f05010005100502070536000100010412070103003f333f12395d012f5e5d5d5de139322f385d5d5d3311332f385d5d5d5d5d332b5d31300101330111
23110133021b0154c8fe42bbfe42cb02d302e3fc83fdc7022f03870000010052000003fe05b600090038402009090307670b0f0b3f0b4f0b9f0b040808040401660a07045f050302085f0112003fe139
3fe1390110e6322f325e5d10e632322f31302121350121352115012103fefc5402c7fd4d0383fd3a02db91047fa691fb8100000100a4febc023905b60007002640170400f306f100011001b001c00104
0105f502f806f501f9003fe13fe1012f5de1ed32313001211121152311330239fe6b0195dfdffebc06fa95fa310000010017000002e905b600030021b70201011001050003b8fff0b40302010003003f
2f2f012f38331133381133313013012301c90220b2fde005b6fa4a05b60000010033febc01c905b60007002440140300f301f16006700602060900f507f903f504f8003fe13fe10110d65de1ed323130
173311233521112133dfdf0196fe6aae05cf95f90600000100290225041905c100060012b603030800000106003fcd012f11332f3130130133012301012901cb6601bfa1feaffea30225039cfc6402df
fd210001fffcfebc034eff4800030012b60000050101ba02002fe1012f11332f313001213521034efcae0352febc8c000001018904d903120621000d0016400a000608800f005f000200002f5d1acc01
2fcd313001232e032735331e031703127823524d3f10db102b2e301504d91c5358511b152251514c1d000002005effec039c045e0023003200544011100129472355340f346f34020630480c1ab8ffd0
401e0d11481a10090c481a1a0c56331916501d2a5210101d1024500207160015003f3f33e13f392fe110e1320110e6322f2b2b10e15e5d10f6e1323231302127230e0323222e02353436373735342e02
2322060727363633321e02151125323e023535070e031514160319250821424e603f45745530e7ecb81d375134538f42404ab66466956130fe2f3d684c2b8f5a7a492061982d412a1427517b54a4b008
0745435a3718302289283829598a62fd107f264d754f630704203951335c5600000200aeffec043f0614001f002f0038b52d4805573131b8ffb840170a4915102547125430130012152a500f0a162050
1b0010003f32e13f33e13f3f0110f6e132322b10f6e1313001321e0215140e0223222e02272307231133111406070607333e0317220e0215141e02333236353426029e5e9a6d3c3c6d9a5e3b604d3b17
0c2585b60202020208173a4d6019506b411b1b416c51877f7f045e488fd48c8dd590491a2b3a208b0614fe88234f222826233c2c199733689c69659d6b37daccd0ce00010071ffec036f045e001f002a
40181d0d215f217f2102102101164805562013510a1019510016003fe13fe10110f6e15d5d10ce32313005222e0235343e0233321617072e0323220615141633323637150606025265b0824a4c85b266
4e95323617383c3a1a9d909194518336367b143f89d5969ddb893e22199a0a130f09c9d4d3c32519a21d1e0000020071ffec04020614001f00300034401d26001b471e55321032012e480b56311f151c
002b501610102050010616003f33e13f33e13f3f0110f6e15d10f6e13232313025230e0323222e0235343e0233321e02173326272626351133112325323e023735342e02232206151416035408163b4d
603c5d9a6e3c3c6e9a5d3b604d3c160c03030204b693fec64c69411f021b416c51877f7f93223d2e1a488fd48c8dd590491a2c3a20221f1a371001b4f9ec832e5e8d5e29659d6b37daccd1cd00020071
ffec03e1045e001e002700434028234819105729e029016f290122114805562811501b222b22020f2201062222141f500a1014510016003fe13fe112392f5e5d5de10110f6e1325d5d10f632e1313005
222e0235343e0233321e02151521161633323e0237150e030322060721342e0202606eb683484278a765639e6e3bfd4c0599973357514c27284d51576072850b01ec1b3958144a8ed28788d6954e4781
b56e71c1b60a131d12a2131c120803db9c954471502c0001001d000002f0061f001b0070404ecf1ddf1d02601d801d901da01d041f1d3f1d4f1d031b1b7f10bf100210101a02470307030f051f052f05
af0504050500031003200380039003a00306060301054f07001a01071a0f14500d010215003f3fe13f5e5d33e132012f5e5d332f5d113310e132322f5d392f5d5d5d3130012311231123353735343e02
3332161707262623220e02151533028bf5b7c2c22d557c4e3b63272f1f4928283a2613f503c1fc3f03c14b44606b8d5423170e8d0b1113305341680000030025fe1403fc045e003f0052005e00a74019
0d3205534737122f60377037803703372f372f27481d594705b8ffc0404d070a480505010a1d01fd1d01b01d01881d01201d301d401d031d1d601f6001bf60df6002a060014027400c0f48270205320d
043c56520a0a225c4f3c2c4e0f17010717173c10454f221b01000f003fc13fe13f392f5e5dc13910e111392fe1121739012f2bc15d5d7111332f5d5d5d5d7133392f2be110c11139392f2f5d10c110e1
1239393130011507161615140e02232226270e0315141e023333321e0215140e0223222e0235343e02372626353436372e0335343e023332161701141e0233323635342e022323220e02131416333236
35342623220603fcc51c262f5f8c5d162c0e11211b111829381fb05d8051244186cd8b6ba06a352742572f2a3640452b47311b32629261254f1bfe401a3b6148bab918375a41b0234c3f295c6c636467
6964636a044a711b236d454c815e3501030a192028181b2112062f506d3d588c61342a5071473c5b422a0b1352353d592a123f516033598c62340b09fb0225402e1b736c2e3a210c102c4d036073706f
777b7478000100ae00000412061400190032401d004719551b101b601b801b030f0e0a470b541a10045015100c000b0015003f323f3fe1330110f6e132325d10f6e131302111342623220e0215112311
331107333e033332161511035c6970516e431db6b6080a1945525c30b7b902c3828234669460fdc70614fe32902b3f2a14bfd2fd3300000200a00000017505e5000300110025401410132013020c0047
0401541207530f0f020f0015003f3f332fe50110f632e1325d31302123113303343633321e021514062322260164b6b6c43d2d16271d113f2c2d3d044a01293c360d1c2b1e3a39380002ffbcfe140175
05e500130021002e401910232023021c0f470c1403030c542217531f1f0d0f0750001b003fe13f332fe50110e6322f3210e1325d31301322262735161633323e0235113311140e0213343633321e0215
140623222642303f171a36231b2e2313b622486d133d2d16271d113f2c2d3dfe140e0b940a0b0f27413304f4fb184d7b572f075f3c360d1c2b1e3a393800000100ae000003f00614000e005e400b0704
040203030644050105b8ffc04017070a4805100505100f102f1002070d09470a540f0b0000b8fff840100c0f4807080c0f48000703060a15030f003f3f331239392b2b3f0110f6e1325e5d11332f382b
5d3333113339113331300137013301012301071123113311030156870125d3fe6f01acd1feb06db4b4100237aa0169fe25fd9101f852fe5a0614fd36feed000100ae0000016406140003001a400e1005
200502004701540402000015003f3f0110f6e15d3130212311330164b6b60614000100ae00000687045e002c0065403f230a47b90b01960ba60b02890b01670b770b020b0b1600472c552ef02e01cf2e
01202e502e020f2e010819154716542d231a1a040f50281f10170f160b0015003f32323f3f33e1323211330110f6e1325e5d5d5d5d10f6e111392f5d5d5d5de13231302111342623220e021511231134
2623220e02151123113317333e0333321617333e03333216151105d164694966411eb763694d683f1bb6941a0a18424f592e789f26081a49576032afb102c382822f5b8758fda202c3828234669460fd
c7044a942b3f2a14585e2f442d16bfd2fd33000100ae00000412045e00180030401c004718551a101a601a801a030e0a470b54190f045014100c0f0b0015003f323f3fe1330110f6e1325d10f6e13130
2111342623220e02151123113317333e033332161511035c6970516e431db6941a0a1945525c30b7b902c3828234669460fdc7044a942b3f2a14bfd2fd3300020071ffec042d045e0013001f0030401d
1a480057214021d021e021030f21010614480a56201d500f1017500516003fe13fe10110f6e15e5d5d10f6e1313001140e0223222e0235343e0233321e02051416333236353426232206042d437db26f
67ae7f47437cb36f67ae7f47fd00899a9a87899a9a87022789d5914c4c91d58988d3914b4b91d388d1d3d3d1d1cfcf00000200aefe14043f045e001f00300036401e2e481b57321032012610060c470d
543120501116100e0f0c1b2b50050016003f32e13f3f3f33e10110f6e13232325d10f6e1313005222e02272316171616151123113317333e0333321e0215140e0203220e020715141e02333236353426
029e3b604d3b170c03030204b6941a08173a4d603c5e9a6d3c3c6d9a814c69411f021b416c51877f7f141a2b3a20221f1a3710fe2b063694233d2d1b488fd48c8dd5904903db2e5e8c5f29659d6b37da
ccd0ce0000020071fe140402045e001000300036401e2b200525472455321032010e48165631241b220f200b501b102c00501116003fe1333fe1333f3f0110f6e15d10f6e1323232313025323e023735
342e0223220615141617222e0235343e0233321e02173337331123113436373637230e0302354c69411f021b416c51877f7f665d9a6e3c3c6e9a5d3b604c3c17081b93b6040203030c163b4d60832e5e
8d5e29659d6b37daccd1cd97488fd48c8dd590491b2d3d2394f9ca01d5133a1b2022223d2e1a000100ae00000308045e00160041400bb203c203029003a0030203b8ffc0401b070b4803031830185018
801803110d470e54170f0f0d1512070010003fc1333f3f0110f6e1325d11332f2b5d5d31300132161707262623220e02151123113317333e0302891d481a181c3b1a3f684b29b694160819394758045e
0505a80507335f8551fdb0044ac92b503d250001005affec033f045e00350048402d2513479000a000020057373f375f379f37031037012c47099f1daf1d021d5636262950132c052210090e50051600
3fe1323f123939e1320110f65d32e15d5d10f65de133313001140e0223222627351e0333323e0235342e02272e0335343e023332161707262623220615141e02171e03033f3a6d9a606d9c3b1f4c5459
2c415b391a14355c484873502b37648c5661a1483f418947666217385e464871502a012d507851282322a6101f180f16293b241f3231321f1f3c4a6143466d4a262a22931d2b433e23342e2f1d1e3c4b
600000010021ffec028f0546001d0050b11905b8ffc0402f080b4805051f3f1f4f1f02171b47141f122f120212001010102010b010c010d0100606101a124f1614170f00500b16003fe13f3333e13201
2f5e5dc65d33e1325d11332f2b33313025323e0237150e0323222e02351123353737331521152111141601fa122d2a23090d283034193e6a4d2c9b9b4e690114feec3f81040608038a060c0905204e85
65027d514ee6fc89fd836162000100a4ffec0408044a001a0030401c0117471a551c101c601c801c030f470c541b180d0f12500207160015003f3f33e13f330110f6e15d10f6e13231302127230e0323
222e0235113311141633323e023511331103751b0a1945525c305b8a5c2fb66a6f516e431db6932b3f29142e62986902cdfd3d828234659460023afbb60000010000000003d5044a0011006db90011ff
f8400f0a0e481100080a0e48000909010f10b8ffc0b312154810b8ffc0401c070b481010101013bf13cf13ef13035013010f132f134f1303070201b8fff0400a01470901090f010f0015003f3f33395d
012f38c15e5d5d5d11332f382b2bc112393d2f332b332b3130210133131e0317333e03371333010177fe89bcc70b1e1e19040705181e1e0bc7bcfe89044afd9d21686c601919606c68210263fbb60001
0014000005e3044a002f00c3b9002ffff8400c0a0e482f0008090e48002720b8fff84012090e48201f08090e481f0910080a0e48100fb8fff84009090e480f1854270127b8ffe04015070a485b180118
20070a4827091818092703112d2eb8ffc0b31215482eb8ffc04013070b482e102e2e3120313031020f3101071211b8fff04016112d1f09090109110f27190619661976190319001015003f33335d1133
3f335d3333012f38335e5d5d11332f382b2b331217393d2f2f2f2b5d2b5d11332b332b11332b332b11332b332b313021032e0327262723060706060703230133131e0317333e03371333131e0317333e
033713330103f0a8040c0c0d060e0f060e0d0b190bacd3fee7bf830a14120e0406051115160ab3c4ac091716120406030d12150b89bafee40268122d3234193a3e3f3a326a25fd9c044afdb82d69675b
1d1a57615f21026bfd95225c5f581d1a57686d2f0248fbb600010023000003db044a000b00e540a1890901860301060401f70401e504013604010405e806010603e700010009090201f80201ea020139
020102016b057b05025705013a054a050264017401025801013501450102050109010905030b060801f70801e5080136080108074016194807400e11486b077b07025707013a074a0702070d100d300d
02900db00d020f0d0106d90a01c80a01ba0a01090a010a3b0b4b0b02280b01050b150b020b0715010f003f3f012f5d5d5dc15d5d5d5d5e5d5d7110de5d5d5d2b2bc15d5d5d711217393d2f182f2f5d5d
5d5d5d5d10c15d5d5d7111335d33335d10c15d5d5d713130005d5d0101331313330101230101230198fe9fcffafacffe9d0175cffef4fef2cf02330217fe66019afde9fdcd01b4fe4c000001000afe14
03df044a00220064b622100808000e0fb8ffc0b31215480fb8ffc0401d070b480f100f0f24bf24cf24ef24035024010f242f244f240307180100b8fff0400c00221008231c50151b0e000f003f323fe1
11333333012f38c1335e5d5d5d11332f382b2bc112393d2f333331301333131e0317333e03371333010e032322262735161633323e0237370abdd70e1d1912040605161b1d0bc7bcfe4e1c4156745034
4c1b154023304634250f39044afd9b28585852231956615e210263fb2751815a310b06910507172c4029a0000001005200000335044a0009006c400b099703010308090d480307b8ffc04011070a4807
070b3f0b5f0b7f0b0398080108b8fff8b5090d48080402b8ffc0b71215483f02010207b8fff04012070c4807044f050f0210070c4802084f0115003fe1322b3fe1322b012f5d2b33332b5d5d11332f2b
332b5d333130212135012135211501210335fd1d0218fe0902b0fdf4021e7d03448992fcd1000001003dfebc02a205b60027004040251a0505f72027f123130ff6100c010c230ff5d910010f105f1002
1010291af519f805f506f9003fe13fe112392f5d5de139012f5de63333f132e22f32313005141e0217152e0335113426233532363511343e0237150e0315111406071516161501f4182d41284d835f36
837d7d83365f834d28412d187773737710303d230d01960121476e4e014e67569b5667014d4e6e47210195010d233d30feb4697b140c147a6a00000101e9fe14027f06140003002d401f000501300540
05700580050402aa0003100340038003c003050703021b0000003f3f012f5e5de15d7131300133112301e996960614f8000000010033febc029805b60029004040250d2424f70700f11af61403901d01
1d041af5ef19ff1902d9190119190e24f523f90df50ef8003fe13fe111392f5d5de139012f5d3333e6f132e22f323130133436373526263511342e0227351e031511141e02331522061511140e020735
3e0335e177737377182d41284d835f362141603e7d83365f834d28412d18013b6a7a140c147b69014c303d230d01950121476e4efeb334482d149b5667feb24e6e47210196010d233d3000010066024a
0402035a0023003c400d1d25102501100a010a17ad0a1fb8ffc040161013481f1f05ad1c0f0d1f0d3f0d4f0d6f0d8f0d060d002f5d33f1c82f2b32e1012f5d5d10ce3130012e0323220e020735363332
1e02171e0333323e0237150623222e02021225372d29161c3c3b381964941d3237432f25372f28161c3c3b381863951d323743028b10160d0513212c19a26c050d191410160d0513212c19a26c050d19
00020093fe8b0191045e000300170041b90000fff040130a14483019a019b019c0190402049a0e03030eb8ffc0400f070a480e00099b1300021002020702002f5e5d2ff5ce012f2b332f10e1325d3130
2b1333132313140e0223222e0235343e0233321e02d57933dfef13232e1b1a2e231414232e1a1b2e231302a4fbe705482635210f0f2135262534221010223400000100bcffec03ba05cb0025005a4025
1203460f04040a2515274027011e48000a300a400ad00a04060a1b730f120f21730502050fb8ffc0400c0f12480f050f050310070319003f3f1239392f2f2b113310e1113310e1012f5e5de15d10c632
11392f33e13231302406071523352e0335343e0237353315161617072e03232206151416333236371503766e4c89578a623435618b568948882e3517383c3b199d909194518336d41e02c8ce0d4b85c7
898dcb884b0daca40321179a0a130f09cad4d2c32518a100000100440000042305c90028007540110d116f230f0f1f0f02071f0f1f0f190317b8ffc0b30a0e4817b8ffc8403006094817172a102a0121
19400b0e481919291021750d2f227f228f22af22bf22df22ff22072222001674191807730007003fe13fe111392f5d33e1321101332f2b335d11332f2b2b331239392f2f5e5d33e13231300132161707
262623220e02151121152115140e0207211521353e03353523353311343e02029a6aae4242388d4b30523c210178fe881727331b02ecfc212c49351ec6c63b699205c92d23901d2b1b3b5e42fed989d3
3e59402b10a69a0b29446143d589014457895f320002007b011d03ec048b00230037008640230e8f160116162eab150f0c18061e21030800701201121239103901042024aa80000100b8ffc04031060a
4800003817801f011f0c06181e0f06040929ae001b011b0d0533aecf09ef09029009a009b009031f093f096f090309002f5d5d5de1c6322f5de11217391133c65d321101332f2b5de1c6325d11332f5d
121739f1c02f5d32313013343637273717363633321617371707161615140607170727060623222627072737262637141e0233323e0235342e0223220e02ba231f81627f2f6c3c3c6b2e8163821f2523
217f60812e6b3c3c6e2d7f607f1f238a2541573233594226264259333257412502d33b6b2f816281202424208160812e6d3c3c6e2d7f607f1f2324207f607f2d6c3c3257412626415732335942262642
59000001001d0000044c05b600160095405b3018010f130c16aa150115150c040801ab02011f020102029800010007030b5a99140114100f0c018a0c01000c100c400c03070c0a0e600f070f06126013
0300137f0f8f0f02301301d013010f131f13020f130f13010b12150103003f333f1239392f2f5d5d715d11333310e132113310e132012f5e5d5d7133335de13232395d322f5d5d33393911332f5d3312
39395d313001013301331521152115211123112135213521353301330233015abffe7feffed3012dfed3b2fed3012dfed3eafe85c002d302e3fd00899e89fefa0106899e89030000000201e9fe14027f
061400030007003940250009013009400970098009040206aa030007100740078007c00705070704030403061b0000003f3f39392f2f012f5e5d33e1325d713130013311231133112301e99696969606
14fcf4fe19fcf30000020079fff6035e061f0045005a0079404b055608514621264c463c46292910ff2101a021b02102215cc05c01bf5c014646001746080831000001000010003000c000d000f00006
08005b264c055617564c29040d37502e1514500d01003fe13fe1121739113311330110c65e5d7132322fe110e15d5d10ce5d5d32322fe112393910e1113939313013343e0237262635343e0233321617
07262623220615141e02171e0315140e0207161615140e0223222627351e0333323e0235342e02272e0337141e0217173e0335342e02270e03891a2d3a1f4b5537648c56619d4838418c47636618395f
4648714e2a1829341c454c3b6c9b606c9c3b1f4c54592b455d371711335e4c497350299a1c3f654823142921151a416c52192f261703293353402d0f2672543d62442528208b1c273b391b2e2c2f1d1c
414e613e3455443110266d4e476f4d2821209e0f1e170e1827331b1d2d2d311f1f3e4e6459253f3a371e0f0d242e382226403b391e081f2d3a0000020133050c036a05d9000b0019003540210c86af14
0114c0068600001000400050000406000f0391179f09cf090230090109002f5d5d33e532012f5e5de11adc5de1313001343633321615140623222625343633321e0215140623222601333828273a3a27
28380177382813231a103a2628380573363031353532323536300c19261b3532320000030064ffec064405cb002500410055006a404305c51a0f0f221a221a22264cc3003401c03401345742c3260ac9
1500c91f0f151f152f157f158f159f150608001f101f601f701f801f05151f151f2d47c83b51c82d04003fe12fe11139392f2f5d5e5d10e110e1012fe110de5d71e11139392f2f11332f10e131300122
0e0215141e0233323e0237150e0323222e0235343e023332161707262601343e0433321e0415140e0423222e0437141e0233323e0235342e0223220e02037b3d5e40211d3d5f4317363938191831353c
2366986533366999643f843b3e3461fcbe36618aa7c06868c0a78a613636618aa7c06868c0a78a61366d65afea8585eaaf6565afea8585eaaf65041d2c53784b4e78522b070c1109830b120e07427aaa
6765a77843211d7f1a1cfebe68c0a78a613636618aa7c06868c0a7896235356289a7c06885eaaf6565afea8585eaaf6565afea00000200440310024205c7001e002d004e402f2d010fe0001d101d021d
2f0f2f1f2f4f2f7f2faf2f0524e00b1717600b010b2e2de40f0f1a0127e40006c013e41ade003fe11adcc4e13911392fe10110c65d322f10e15d10d65de13232313001270e0323222e02353436373735
3426232206072736363332161511030e0315141633323e02353501e71c12272f38232b48341d8d8f633d38305a2a3033753c7d77c933442912322a223a2b19031d521623190d1a334d33666c05041f48
391d16641a246a7afe3a013903121e2b1d332d152c412c31000200520073039303c70006000d0060401102040deb0a5004600402040a040a060b09b8ffc04021090c48090f0f0f9f0faf0f0306eb9f03
01030600030d070a0a050303010c050801002f332f3312393d2f123911333311333301182f5de15d10c62b321139392f2f5d10e111333130130117031307012501170313070152013575eeee75fecb01
97013674eded74feca0229019e4efea4fea44e019b1b019e4efea4fea44e019b0001006601060402031d00050039402402aa01071007019604018b04017904015604014b040138040112040109040104
04ad05b3003fe1012f5d5d5d5d5d5d5d5d5d10dee13130011123112135040295fcf9031dfde901819600ffff005201d10242027902060010000000040064ffec064405cb0008001e003a004e00c2407d
a416b416c41603b417c4170217160152150e170e16c5150e1415150e0e090019c51a09c5041504001a01001ac01ad01a03078f04011a041a041f45c3002d01c02d012d503bc31f0e18c90000161b1615
1a08c91b001a010f1a1f1a2f1a7f1a8f1a9f1a0608001b101b601b701b801b051a1b1a1b2640c834134ac82604003fe13fe11139392f2f5d5e5d7110e111333311123910e132012fe110de5d71e11139
392f2f5d5e5d71113910e110e132113987102b1000c187052b10c4015d31305d01333236353426232305140e020716171e02171723032311231133321601343e0433321e0415140e0423222e0437141e
0233323e0235342e0223220e0202e7485b4f53594601921b2d391f4335172a210a0ab3ce5f9de9a89efbeb36618aa7c06868c0a78a613636618aa7c06868c0a78a61366d65afea8585eaaf6565afea85
85eaaf65030048454a3b81304b39280d6e5725473811110160fea0037d82fec368c0a78a613636618aa7c06868c0a7896235356289a7c06885eaaf6565afea8585eaaf6565afea000001fffa06140406
06a000030012b60000050101ba02002fe1012f11332f3130012135210406fbf4040c06148c000002007b035602f205cb001300270043402c1eab0a299f290114aa30004000020019ae100f200f02e00f
f00f026f0f01000f100f200f03060f0f23ae0504003fe1332f5e5d5d5d71e1012f5de15d10d6e1313013343e0233321e0215140e0223222e0237141e0233323e0235342e0223220e027b325573414173
563232567341417355327b1e3446282846351e1e3546282846341e048f417356323256734141725531315572412745341e1e3445272847351f1f3547000200660000040204a2000b000f003a40211011
010f08080609aa0c010103ef000120006000a00003000dad0c0900ad0603b3003f33e1322fe1012f5d5d33331133e1323211335d31300121352111331121152111230135211501e9fe7d0183960183fe
7d96fe7d039c0287960185fe7b96fe7ffefa969600010031024a026d05c9001e0040401508e10017204f207f20022040060a481de1010f0f01b8ffc0400e15184801081d0be512de1de501dd003fe13f
e11239012f2b332f10e12b5d10de32e13130012135373e033534262322060727363633321e0215140e02070721026dfdc4d13948280f4236335d2d4e3685523c6144251d364f3394018c024a70e43e55
433b22414032265e3041213f5b393256555b379d0001001f0239026805c900300061403c030019190e061ee1000015e106325f328f32023240060a4827270e401920480e0319e40f1a1f1a2f1a5f1adf
1a05081a1a122623e52cde12e50f0bdf003f33e13fe13312392f5e5de139012f2b332f2b5d10dee1332fe11112392f1239313001140607161615140e0223222627351616333236353426232335333236
35342e0223220607273e0333321e02024e5145585828537e56467b393f843562586b6062625c5414232f1b3b6133451d3d444c2c4569462304e74e6a18176a4e3c644728191f85222653494a43714f40
202f1e0e29256017251a0f223c530001018904d903120621000d0016400a060005800f0c5f0c020c002f5d1acd012fcd3130013e033733150e0307230189162f2f2a10db103f4d51237904f41d4c5151
22151b5158531c00000100aefe140412044a001d003740220d09470a551f101f201f601f701f801f05141d471c541e1a1b035011160b151c090f003f333f3fe13f0110f6e1325d10f6e1323130011416
33323e023511331123272306062322262716171616151123113301646a6f526e431cb6931b0a309067486a2301020201b6b60187828234659460023afbb69353542e2a262823552afec0063600010071
fefc0466061400130037402104990005300540055005040605050d01990015101501000d100d020d0312000500002f323fc1012f5d5d10d6e112392f5e5de13130012311231123110623222e0235343e
023321046678cf793d555f9b6d3c4177a6640233fefc0679f9870333123376c08c93c57832000001009302480191035e00130033401a101580159015a015040a96c000d0000234004400640074000400
b8ffc0b6070a4800059b0f002fe5012f2b5d5ded5d313013343e0233321e0215140e0223222e029314222e1b1a2f221414222f1a1b2e221402d32635210f0f213526253422101022340000010023fe14
0198000000190039401f141313157f128f12021212060d84001b061a128c1540090e481515130a8d03002fe12f392f2be10110c610d6e111392f5d333311333130011406232226273516163332363534
2e02273733071e0301988d96162d0f0f311047501a2e3f255a7939223a2b19fee1616c06036c03032b3118231a1309b073081a293a000001003f024a01ba05b6000e003440214f107f10021040060a48
0e0e02e1007f038f030220033003020302dd0d09e500dc003fe1cd3f012f5d5d33e1332f2b5d31300133112311343e0237060607072701338791010303010e26165e4a05b6fc940204193c3c38161128
11496000000200420310028b05c70013001f002eb21ae000b8ffc04014090f4800210f210114e00a2017e405c01de40fde003fe11adce10110d6e15d10d62be1313001140e0223222e0235343e023332
1e02051416333236353426232206028b294d6d443f6a4e2b294c6d443e6b4e2cfe3a4b56554b4b55564b046d5382592f2f5982535381582e2e5881537779797778737300000200540073039603c70006
000d0056402f0f0f9f0faf0f0307eb04020a020a02030b090e00eb9f030110032003400303030d070a0600030a030a03010c050801002f332f331239393d2f2f11333311333301182f5d5de110c63211
39392f2f1133e15d313001012713033701050127130337010396feca74eded740136fe68fecb75eeee750135020efe654e015c015c4efe621bfe654e015c015c4efe6200ffff003f0000058b05b60026
007b000000270217024a00000107023c02fcfdb70030401d030216180302bf16018f16013f16011601401101001101110040000100115d35115d5d35115d5d5d3535003f3535ffff002c000005a005b6
0026007bed000027021702350000010700740333fdb700284018021418020014011401b01101401101110070000140000100115d5d35115d5d35115d35003f35ffff001f000005ce05c9002600750000
0027021702a800000107023c033ffdb7003c40270302381803027038015038013801b43301a43301843301643301503301303301203301330f4c015d115d5d5d5d5d5d5d35115d5d3535003f35350002
0044fe770344045e0027003b00444012329a2828274600000b143d0f3d01080b461cb8ffc040100f1b481c0b1727272d9b371013105117002fe1333fe5322f1239012f2be15e5d10ce11392fe1332fe1
31300115140e02070e0315141e023332363717060623222e0235343e02373e03353513140e0223222e0235343e0233321e0202501027413230442b151e3955375496454052bc615d9567381b35513634
42260eba13232e1b1a2e231414232e1a1b2e231302a4253a5b514c2a2943454f35304f391f3323922a3a33608a5844685a542f2d433e432b13012f2635210f0f21352625342210102234ffff00000000
04dd077302260024000001070043ffbd01520015b40215052602b8ff9cb41b15040725012b35002b3500ffff0000000004dd077302260024000001070076008d01520013400b02210526026c151b0407
25012b35002b3500ffff0000000004dd07730226002400000107014b001f01520015b40215052602b8ffffb41d15040725012b35002b3500ffff0000000004dd07350226002400000107015200060152
0013400b021d052602011e2c040725012b35002b3500ffff0000000004dd072b0226002400000107006a002101520017400d03021e05260302011529040725012b3535002b353500ffff0000000004dd
0704022600240000010601501f7d003140200302ef1a01df1a01501a01401a01201a01101a01001a011a0302001f15040725012b353500115d5d5d5d5d5d5d3535000002fffe0000065605b6000f0013
0084402a06130a0e5a01110110030413a9130124133413541303100101140c0113010c0c01130305080067150405b8fff040200509135f06035f100d5f0a4f0a010f0aaf0a0208100a100a0603040e5f
050112003f33e12f3f39392f2f5e5d7110e110e110e132012f383310e6321117392f2f2f5d5d5d5d7d87c4c411013310e1321133313021211121032301211521112115211121012111230656fd08fe25
cbba028f03c9fdc30216fdea023dfb7501936c01c5fe3b05b6a4fe3ca2fdf801c602a800ffff007dfe14049805cb0226002600000107007a01fc0000000bb6014f2a24182025012b3500ffff00c70000
03be077302260028000001070043ffb701520015b4010c052601b8ffc2b4120c010025012b35002b3500ffff00c7000003be077302260028000001070076003f01520013400b01180526014a0c120100
25012b35002b3500ffff00c7000003be07730226002800000107014bfff101520015b4010c052601b8fffdb4140c010025012b35002b3500ffff00c7000003be072b0226002800000107006afff50152
0017400d02011505260201010c20010025012b3535002b353500ffff003e0000026407730226002c000001070043feb501520015b4010c052601b8ffa8b4120c010025012b35002b3500ffff00520000
028a07730226002c000001070076ff7801520013400b01180526016a0c12010025012b35002b3500ffff0011000002a907730226002c00000107014bff0f01520013400b010c05260102140c01002501
2b35002b3500ffff004000000277072b0226002c00000107006aff0d01520017400d02011505260201000c20010025012b3535002b3535000002002f000004fc05b60010001f005d403a1a1a0e115b08
6721202101181c5a0e1010010e64201b105f180f00010f003f006f00af00df00ff00060800401a1d480000021c600e1217600203003fe13fe111392f2b5e5d7133e1320110e632322f10e1325d10f6e1
11392f3130133311213216161215140206042321112325342e02232311211521113320002f98019799f8ae5f60b6fef7a8fe92980408427eb875c90150feb0a20108010c032502915cb5fef4b0b9fee9
bb5e02836092d58a43fe0ea2fe1d0124ffff00c70000050e073502260031000001070152008b01520013400b01200526010a212f0a0025012b35002b3500ffff007dffec057107730226003200000107
0043005401520015b40228052602b8ffabb42e280a0025012b35002b3500ffff007dffec0571077302260032000001070076010201520013400b023405260258282e0a0025012b35002b3500ffff007d
ffec057107730226003200000107014b00ae01520013400b02280526020530280a0025012b35002b3500ffff007dffec0571073502260032000001070152007d01520015b40230052602b8fff0b4313f
0a0025012b35002b3500ffff007dffec0571072b0226003200000107006a00aa01520017400d0302310526030201283c0a0025012b3535002b3535000001008d012d03dd047b000b0087b90006fff0b3
14174806b8ffe040180f1248001014174800200f1248091014174809200f124803b8fff0b314174803b8ffe040300f1248400d01070505030b010150030103080a0a0402022000010000200050007000
8000a000c000d000f000090600b300193f5e5d71321133321133012f5d331133113311335d3130002b2b2b2b012b2b2b2b01013701011701010701012701cbfec269013d014268febf013f66febefec3
6702d3013f69fec2013e67febffec066013dfec567000003007dffb4057105fc001a00260031005c403a291f2a1e041b275b01190b0e0411046733c03301bf33017033012f335f33021b5b1166321f29
1e2a042d225f19010e0b04091a16042d5f0c0913003f33e13f33121739e11117390110f6e15d5d5d5d10f6111739e1111739313001071612151402060623222707273726023534123636333216173701
14161701262623220e0205102701161633323e0205145c5b5e51a0ed9bbd854e895a615b4c9ef0a35ea14250fcb72e30024330724772a66c34036a58fdbe2f724572a56b3205ae9563fedeb7a9feeac6
6c477f4e9164012abeaa0115c46b2a267ffce183d14e03b11d205197da8a010197fc541c1e5199dbffff00b8ffec04dd077302260038000001070043003d01520015b40118052601b8ffc0b41e180b00
25012b35002b3500ffff00b8ffec04dd07730226003800000107007600c501520013400b012405260148181e0b0025012b35002b3500ffff00b8ffec04dd07730226003800000107014b007901520015
b40118052601b8fffdb420180b0025012b35002b3500ffff00b8ffec04dd072b0226003800000107006a007d01520017400d0201210526020101182c0b0025012b3535002b353500ffff000000000437
07730226003c000001070076003101520013400b011505260163090f070225012b35002b3500000200c70000043305b60010001b003c4021175b00671d9f1d01101d01110b075a08641c1160061b600b
060b060b0709030712003f3f1239392f2f10e110e10110f6e132325d5d10f6e1313001140e022323112311331533321e020133323e0235342623230433377ecf9896babab086c27e3cfd4e815d8b5b2e
a4aea0030e5ba8814dfec305b6fc396da0fe67204771518f8800000100aeffec0475061f004b006d404807462e3547000f191f192f190319400d13485f2e6f2e020f001f002f000308192e00002e1903
41244711574d104d204dc04d03404741544c24073503163a50470141151f501a1616003f33e13f3fe11217390110f6e15d10f6e11217392f2f2f5e5d5d2b5d10e110e1313001140e0415141e02171e03
15140e0223222627351e0333323e0235342e02272e0335343e0435342e0223220e0215112311343e0233321e0203f22b3f4b3f2b0e27463938583d2138658d55618b351a41484c2538513418112b4838
3f553516293e483e29213c533331584227b63f719c5c5c986c3c04ec3959493c37361e1521273126254852603e577e51272322a6101f180f192d4028243b383a23284443462a364f3f363a432c2a3e29
1313305341fb4e04b0688d5525264c74ffff005effec039c06210226004400000106004394000015b40233112602b8ffe5b439330c2225012b35002b3500ffff005effec039c06210226004400000106
007635000013400b023f1126028533390c2225012b35002b3500ffff005effec039c06210226004400000106014be2000013400b0233112602333b330c2225012b35002b3500ffff005effec039c05e3
02260044000001060152bd000013400b023b112602293c4a0c2225012b35002b3500ffff005effec039c05d90226004400000106006ade000017400d03023c112603022f33470c2225012b3535002b35
3500ffff005effec039c068702260044000001060150de000017400d03023811260302303d330c2225012b3535002b3535000003005effec0644045e003800470050007c401a2f144b1e48044242004c
48261d57524f525f529f52033948000eb8ffc0402e0d11480e0e0056510042104202421e500004100402040f4b1f4b02074b4b11213c502f2c3416480d0a5017141110003f3333e132323f3333e13211
392f5e5d335de1325d0110e6322f2b10e15d10f632e111392f33e13239393130133436373735342e022322060727363633321617363633321e02151521161633323e0237150e03232226270e0323222e
0237141633323e023535070e030122060721342e025ee7ecb81d375134538f42404ab66483a62b33a667619a6c39fd6005939331554e4a25274b4f55318aca3e224c5f744a477b5a34bd614f3d684c2b
8f5a7a492003856e7f0b01d71a37540133a4b0080745435a37183022892838555d555d4781b56e71c1b60a131d12a2131c1208727336553b1f27517b525c56264d754f63070420395102639c95447150
2c00ffff0071fe14036f045e0226004600000107007a01420000000bb6012f2620050d25012b3500ffff0071ffec03e106210226004800000106004394000015b40228112602b8ffb9b42e28050f2501
2b35002b3500ffff0071ffec03e106210226004800000106007652000013400b023411260276282e050f25012b35002b3500ffff0071ffec03e106210226004800000106014bde000013400b02281126
02033028050f25012b35002b3500ffff0071ffec03e105d90226004800000106006ada000017400d0302311126030200283c050f25012b3535002b353500ffffffde000001670621022600f300000107
0043fe5500000015b40104112601b8ff9ab40a04010025012b35002b3500ffff00ae000002420621022600f3000001070076ff3000000013400b011011260174040a010025012b35002b3500ffffffbd
000002550621022600f300000107014bfebb00000013400b0104112601000c04010025012b35002b3500ffffffee0000022505d9022600f300000107006afebb00000017400d02010d11260201000418
010025012b3535002b3535000002006fffec042d062300270039007440461228480023201619041c22181c22221c18030a00573b403bd03be03b030f3b010632480a563a20192316041721211d2d500f
120faf0fbf0f02300f01170f170f1d0137500516003fe13f39392f2f5d5d113310e11133111217390110f6e15e5d5d10e61117392f2f2f1112173910e132313001140e0223222e0235343e0233321617
37262627052737262627371616173717071e0307342e0223220e0215141e02333236042d437db26f68af7f473f76a869669a2b081f785aff004ad928552f46417a3be34ac3436f4f2cbc22466e4b4d6d
462121476d4c9a87023d8edc984f427fb97777b87e413b3c0476c0519972831c371a7b20482c8a7175419cbbddb0386b52322e5883554c7d5a31c700ffff00ae0000041205e302260051000001060152
f9000013400b01211126010222300b1725012b35002b3500ffff0071ffec042d062102260052000001060043d8000015b40220112602b8ffd7b426200a0025012b35002b3500ffff0071ffec042d0621
0226005200000106007650000013400b022c1126024e20260a0025012b35002b3500ffff0071ffec042d06210226005200000106014bfb000015b40220112602b8fffab428200a0025012b35002b3500
ffff0071ffec042d05e302260052000001060152e2000015b40228112602b8fffdb429370a0025012b35002b3500ffff0071ffec042d05d90226005200000106006af9000019b603022911260302b8ff
f9b420340a0025012b3535002b3535000003006600f8040204ac00030017002b00604015302d0122aa18180eaa560366030228033803020300b8fff04028090d48000427ad101d010f1d011d1d0109ad
0013101320136013b013c013d0130707131300ad01b3003fe1332f5e5de111332f5d71e1012f332b335d5de1332fe15d31301335211501343e0233321e0215140e0223222e0211343e0233321e021514
0e0223222e0266039cfdbf121f2918172a201212202a1718291f12121f2918172a201212202a1718291f1202879696feee232f1e0d0d1e2f23212f1f0e0e1f2f02db232f1e0d0d1e2f23212f1f0e0e1f
2f0000030073ffb4042f0491001a0024002d005c403b271f281e041b25481718161507080a09080d00572f402fd02fe02f030f2f01061b480d562e281e271f042b2250070a181504051612102b500805
16003fc6e13fc6121739e11117390110f6e15e5d5d10f6111739e1111739313001140e02232227072737262635343e0233321617371707161605141617012626232206053427011616333236042f437d
b26f7d624483503f46437cb36f3f71314483503e45fd001316018d1d4b2d9a87024427fe721f482d9a87022789d5914c356d4a8348d58988d3914b1d1c6c498149d18654833302871112cfd19f63fd7b
1110d300ffff00a4ffec0408062102260058000001060043a3000015b4011b112601b8ff9bb4211b0c1925012b35002b3500ffff00a4ffec040806210226005800000106007660000013400b01271126
01571b210c1925012b35002b3500ffff00a4ffec040806210226005800000106014b08000013400b011b11260100231b0c1925012b35002b3500ffff00a4ffec040805d90226005800000106006a0200
0019b602012411260201b8fffbb41b2f0c1925012b3535002b353500ffff000afe1403df06210226005c0000010600760e000013400b012f112601672329000f25012b35002b3500000200aefe14043f
0614002000310038401f2f480a573310330127201f151b471c54321d001b1b2c50150f162150000510003f33e13f33e13f3f0110f6e1323232325d10f6e13130013e0333321e0215140e0223222e0227
23161716161511231133110725220e020715141e023332363534260164173a4d603c5e9a6d3c3c6d9a5e3b604d3b170c03030204b6b608011f4c69411f021b416c51877f7f03b6233d2d1b488fd48c8d
d590491a2b3a20221f1a3710fe2b0800fe3694112e5e8c5f29659d6b37daccd0ce00ffff000afe1403df05d90226005c00000106006ab1000017400d02012c112602010b2337000f25012b3535002b35
3500ffff0000000004dd06c10226002400000107014d003301520015b40217052602b8ffffb41516040725012b35002b3500ffff005effec039c056f0226004400000106014de6000013400b02351126
022333340c2225012b35002b3500ffff0000000004dd07400226002400000107014e002101520013400b021a052602001f15040725012b35002b3500ffff005effec039c05ee0226004400000106014e
d4000013400b0238112602253d330c2225012b35002b3500ffff0000fe4204dd05bc02260024000001070151036200000014400d02802701302701002727000025012b5d5d35ffff005efe4203b8045e
02260044000001070151025a00000020401602f04501a04501904501804501304501004545000025012b5d5d5d5d5d35ffff007dffec0498077302260026000001070076010a01520013400b01300526
01cd242a182025012b35002b3500ffff0071ffec036f0621022600460000010600764a000017400e012c112601102001a72026050d25012b5d35002b3500ffff007dffec049807730226002600000107
014b00b401520013400b0124052601772c24182025012b35002b3500ffff0071ffec038f06210226004600000106014bf5000017400e0120112601102801532820050d25012b5d35002b3500ffff007d
ffec049807370226002600000107014f020001520013400b012f05260180242c182025012b35002b3500ffff0071ffec036f05e50226004600000107014f013300000017400e012b1126011020014d20
28050d25012b5d35002b3500ffff007dffec049807730226002600000107014c00a801520013400b01290526016b2f24182025012b35002b3500ffff0071ffec03a006210226004600000106014c0600
0017400e0125112601102b01642b20050d25012b5d35002b3500ffff00c7000004fc07730226002700000107014c004a01520015b4021d052602b8ffb7b42318060025012b35002b3500ffff0071ffec
055206140226004700000107023802d90000000bb6025e3e3e1d1d25012b3500ffff002f000004fc05b602060092000000020071ffec049e061400270038005540311f1b002d47261d1d0b2226553a10
3a0136480b56392715251d4f221e1e1020003350160010101020100310102850000616003f33e13f5d33e13f12392f33e1323f0110f6e15d10e63211392f10e1323232313025230e0323222e0235343e
0233321e021733262726263535213521353315331523112325323e023735342e02232206151416035408163b4d603c5d9a6e3c3c6e9a5d3b604d3c160c03030204fe85017bb69c9c93fec64c69411f02
1b416c51877f7f93223d2e1a468acc8687ce8b47192c3a20211f1a37109e89b6b689fb2b832c59855a2761956634cfc3c6c3ffff00c7000003be06c10226002800000107014d000a01520013400b010e
052601020c0d010025012b35002b3500ffff0071ffec03e1056f0226004800000106014d00000013400b022a112602112829050f25012b35002b3500ffff00c7000003be07400226002800000107014e
fffb01520013400b011105260106160c010025012b35002b3500ffff0071ffec03e105ee0226004800000106014eea000013400b022d1126020f3228050f25012b35002b3500ffff00c7000003be071a
0226002800000107014f013f01350013400b0117052601070c14010025012b35002b3500ffff0071ffec03e105e50226004800000107014f012b00000013400b02331126020c2830050f25012b35002b
3500ffff00c7fe4203be05b60226002800000107015102210000000eb90001ffc1b41212000025012b35ffff0071fe6103e1045e02260048000001070151023b001f0027401302f03b01e03b01a03b01
903b01503b01003b01b8ffcab43b3b1a1a25012b5d5d5d5d5d5d3500ffff00c7000003be07730226002800000107014cfff901520013400b011105260104170c010025012b35002b3500ffff0071ffec
03e106210226004800000106014ce4000013400b022d112602093328050f25012b35002b3500ffff007dffec04f207730226002a00000107014b00d701520013400b012c0526016d342c0c0125012b35
002b3500ffff0025fe1403fc06210226004a00000106014bc6000013400b035f11260303675f270025012b35002b3500ffff007dffec04f207400226002a00000107014e00c901520013400b01310526
015f362c0c0125012b35002b3500ffff0025fe1403fc05ee0226004a00000106014eb1000015b40364112603b8ffefb4695f270025012b35002b3500ffff007dffec04f207370226002a00000107014f
020c01520013400b01370526015f2c340c0125012b35002b3500ffff0025fe1403fc05e50226004a00000107014f00f200000015b4036a112603b8ffecb45f67270025012b35002b3500ffff007dfe3b
04f205cb0226002a00000107023901170000000bb601512c320c0125012b3500ffff0025fe1403fc06210226004a00000106023a2d000013400b03641126031c655f270025012b35002b3500ffff00c7
000004d507730226002b00000107014b007f01520015b4010c052601b8ffffb4140c050025012b35002b3500ffff00ae0000041207aa0226004b00000107014b002b0189001f4014700d01700c010170
1a011a02260119221a0b1825012b35002b5d355d5d00000200000000059c05b6001300170056402f14040c5a070b6519b01901af190110190117030f5a001064180e5f17160a12601307031317131713
01100b12050103003f333f331239392f2f11333310e1323210e10110f632e132325d5d5d10f632e132323130133533152135331533152311231121112311233501352115c7ba029abac7c7bafd66bac7
041bfd6604c3f3f3f3f396fbd302aafd56042d96fe8bdfdf00010012000004120614002100554034130b0047215523102360238023031716120a470f0b5422150d4f12bf0ecf0e020e0e10170450001d
101d201d031d1010000b0015003f323f3f5de13312392f5d33e1320110f632e13232325d10f6e1123931302111342623220e02151123112335333533152115211507333e033332161511035c6970516e
431db69c9cb6017bfe85080a1945525c30b7b9029a828234669460fdf004d589b6b689b8902b3f2a14bfd2fd5c00fffffff5000002c407350226002c000001070152fef301520013400b011405260101
1523010025012b35002b3500ffffffa30000027205e3022600f3000001070152fea100000013400b010c112601010d1b010025012b35002b3500ffff003d0000027c06c10226002c00000107014dff22
01520013400b010e052601010c0d010025012b35002b3500ffffffeb0000022a056f022600f300000107014dfed000000013400b0106112601010405010025012b35002b3500ffff00300000028a0740
0226002c00000107014eff0f01520013400b011105260102160c010025012b35002b3500ffffffde0000023805ee022600f300000107014efebd00000013400b0109112601020e04010025012b35002b
3500ffff0052fe42026405b60226002c000001070151009c0000000bb601001912010025012b3500ffff0044fe42018305e50226004c00000106015125000010400a026f2501002525000025012b5d35
ffff00520000026407370226002c00000107014f005001520013400b0117052601000c14010025012b35002b3500000100ae00000164044a0003001a400e10052005020047015404020f0015003f3f01
10f6e15d3130212311330164b6b6044affff0052fe7b042905b60026002c00000107002d02b600000038400e01187f18014f1801184007074818b8ffc04017060648bf2101a021018f21015021010f21
0121400707482b5d5d5d5d5d2b2b5d5d1135ffff00a0fe14038705e50026004c00000107004d02120000002e402003021ecf1e01df35019f35018035015f350140350120350100350135400606482b5d
5d5d5d5d5d5d5d113535ffffff48fe7b026407730226002d00000107014bfeca01520013400b0114052601001c140d0e25012b35002b3500ffffffbcfe14025706210226023700000107014bfebd0000
0013400b0114112601001c140d0e25012b35002b3500ffff00c7fe3b04a205b60226002e0000010602397300000eb90001ffb1b40d13050025012b35ffff00aefe3b03f006140226004e000001060239
2100000eb90001ffc4b40f150a0525012b35000100ae000003f0044a00110049400a0110100e0f0f44110111b8ffc0401c070a4811101111130f132f1302070703470454120c010e050f040015003f32
3f3339390110f6e1325e5d11332f382b5d33113339113331302101071123113311140607060737013301010323feac6db4b405030404830133cdfe6f01ac01e951fe68044afee73c66262c24b00181fe
16fda000ffff00c7000003be07730226002f000001070076ff6501520015b40112052601b8ff70b4060c000425012b35002b3500ffff00ab0000023407ac0226004f000001070076ff22018b001f4014
700301700201017010011002260166040a010025012b35002b5d355d5d00ffff00c7fe3b03be05b60226002f0000010602392d00000eb90001ffddb4060c000425012b35ffff0066fe3b016c06140226
004f000001070239fef70000000eb90001ffe0b4040a010025012b35ffff00c7000003be05b70226002f0000010702380100ffa30016400d010b030110060160060c020425012b5d35003f35ffff00ae
000002b406140226004f0000010602383b00001840110104400e104804400609485e0404030325012b2b2b35ffff00c7000003be05b60226002f00000107014f01e3fd65000bb601ab060e000425012b
3500ffff00ae000002a206140026004f00000107014f012dfd38002b4016ff1301ef13019f13018f13017f13015f13011f130101b8012eb4040c010025012b355d5d5d5d5d5d5d000001001d000003be
05b6000d006140320b06050c050003040d040405050a08080faf0f01100f010c0d0d0603075a0b000a640e05040d0c040c040c01075f0a120103003f3fe11239392f2f113311330110f63232e1323233
2f335d5d11331112392f33107d8704c4c41087c4c431301311331137170511211521110727c7bae34efecf023dfd09614902a40312fd638f7dbefe39a601f83c7d000001fff60000021d0614000b0069
403b400d500d020906050a050003040b0a0b0b0804dd0501bb05cb05020f051f052f050306050506030747090008540c0b0a05040a040a040107150100003f3f1239392f2f113311330110f63232e132
32322f5e5d5d5d3311332f337d8704c4c41087c4c4015d3130131133113717071123110727aeb66b4eb9b66a4e02f6031efd59457978fd3f024a4679ffff00c70000050e077302260031000001070076
00f201520013400b012405260155181e0a0025012b35002b3500ffff00ae000004120621022600510000010600766f000013400b01251126015c191f0b1725012b35002b3500ffff00c7fe3b050e05b6
0226003100000107023900d90000000eb90001ffe1b4181e0a0025012b35ffff00aefe3b0412045e022600510000010602394c00000eb90001ffdeb4191f0b1725012b35ffff00c70000050e07730226
003100000107014c009c01520013400b011d0526010023180a0025012b35002b3500ffff00ae0000041206210226005100000106014c0c000015b4011e112601b8fffab424190b1725012b35002b3500
ffffffff000004a105b600270051008f000001060207e8000029401d00185527ef2701bf27019f27015f27012f2701274008084827400606482b2b5d5d5d5d5d10f63400000100c7fe7b050e05b60025
004a402a180c1f5a20040415206527b027018f2701002710270217130e145a1564261f0d16031512180c12075f00002fe13f333f3f33330110f6e13232325d5d5d10e611392f10e13232313001222627
35161633323e0235012316171616151123113301332627262635113311140e02038b334d1b224e2d254b3d26fd080806040405acd502cc0703040305ae37648ffe7b0d0ba0090b1332584404ba4d4c41
8e39fce705b6fb754141387d340320fa5e699a653100000100aefe140412045e0028003a40220303170c4724552a102a602a802a031a16471754291a10502010180f17150750001b003fe13f3f3fe133
0110f6e1325d10f6e111392f31300122262735161633323e023513342623220e02151123113317333e033332161503140e0202ee303f171a36231b2e2313026970516e431db6941a0a1945525c30b7b9
0222486dfe140e0b940a0b0f274133036d828234669460fdc7044a942b3f2a14bfd2fc954d7b572fffff007dffec057106c10226003200000107014d00bc01520013400b022a0526020028290a002501
2b35002b3500ffff0071ffec042d056f0226005200000106014d14000013400b02221126020020210a0025012b35002b3500ffff007dffec057107400226003200000107014e00aa01520013400b022d
0526020132280a0025012b35002b3500ffff0071ffec042d05ee0226005200000106014e00000015b40225112602b8ffffb42a200a0025012b35002b3500ffff007dffec057107730226003200000107
015300ec01520017400d0302340526030243283c0a0025012b3535002b353500ffff0071ffec042d06210226005200000106015346000017400d03022c112603024520340a0025012b3535002b353500
0002007dffec06a405cd0019002a005b403614160116160014185a2727091200672c1f2c011f5b09662b175f4f14010f14af140208141419125f11031a5f0e04245f0413195f0012003fe13fe13fe13f
e112392f5e5d71e10110f6e15d10e63211392fe13211392f5d3130212106062322262602353412363633321721152111211521112101220e0215141e023332363711262606a4fd0a2b5b30a3ef9d4c4c
9ef0a3625402f4fdc20217fde9023efc5672a66c34346ba572345a262659090b6cc60117aaaa0115c46b17a4fe3ca2fdf804855197da8a89db9951120f045810110000030071ffec06e1045e002a0036
003f0064403b13033f1d48d0310131310b3748251c57410f4101ff41014041012b480b56401d501b3f2b3f020f3f01063f3f2e3c345016131010202e5003000616003f3333e1323f3333e13212392f5e
5d5de10110f6e15d5d7110f632e111392f5de1323939313005222627060623222e0235343e0233321617363633321e02151521161633323e0237150e0301141633323635342623220605342e02232206
07056082cc413fc68067ae7f47437cb36f79c33f3cb975639e6e3bfd4c0599973357514c27284d5157fb9a899a968b8c979a8704f01b39583e72850b14706d6d704c91d58988d3914b6f6a69704781b5
6e71c1b60a131d12a2131c1208023bd1d3c9d1dccecf624471502c9c9500ffff00c7000004a0077302260035000001070076005401520015b40229052602b8ffeeb41d23020d25012b35002b3500ffff
00ae00000308062102260055000001060076ce000013400b012311260140171d0e0325012b35002b3500ffff00c7fe3b04a005b6022600350000010602397700000eb90002ffb6b41d23020d25012b35
ffff0060fe3b0308045e02260055000001070239fef10000000eb90001ff08b4171d0e0325012b35ffff00c7000004a007730226003500000107014c000e01520015b40222052602b8ffa9b4281d020d
25012b35002b3500ffff00720000030a06210226005500000107014cff7000000015b4011c112601b8ffe3b422170e0325012b35002b3500ffff0068ffec03c9077302260036000001070076003b0152
0013400b014005260170343a080025012b35002b3500ffff005affec033f062102260056000001060076dc000013400b01421126015d363c080025012b35002b3500ffff0068ffec03c9077302260036
00000107014bffed01520013400b0134052601223c34080025012b35002b3500ffff005affec033f06210226005600000106014b9b000013400b01361126011c3e36080025012b35002b3500ffff0068
fe1403c905cb0226003600000107007a01350000000eb90001fffab43a34080025012b35ffff005afe14033f045e0226005600000107007a00f00000000bb601013c36080025012b3500ffff0068ffec
03c907730226003600000107014cffe601520013400b01390526011b3f34080025012b35002b3500ffff005affec033f06210226005600000106014c97000013400b013b112601184136080025012b35
002b3500ffff0014fe3b041205b602260037000001060239f900000eb90001ffd8b4080e030525012b35ffff0021fe3b028f054602260057000001070239ff5d0000000eb90001fff7b41e2412052501
2b35ffff00140000041207730226003700000107014cffc801520013400b010d052601031308030525012b35002b3500ffff0021ffec02e80614022600570000010602386f00000bb601612424181825
012b3500000100140000041205b6000f0073404c1f1101e011015f11df11022011301140110308bf05ff050205050a065a010f015002f002021f020102020d7701870197010331010100011001200103
07010a0d60070000030b1206025f0303003fe1323f12392f33e132012f5e5d5d5d33332f5d5d123910e132322f5d395d5d5d7131300111213521152111211521112311213501b6fe5e03fefe5f0127fe
d9bbfed7033301dfa4a4fe2195fd62029e9500010021ffec028f05460025006ab6141f01201c1c05b8ffc04037080b480505272f273f27021f1b2347130f161f160216181400101010a010b010c01005
071011224f141f1f0b151e4f1a181b0f00500b16003fe13f3333e13212392f33e132012f5e5d3333c45d32e132325d11332f2b331133005d313025323e0237150e0323222e0235352335333523353737
33152115211521152115141601fa122d2a23090d283034193e6a4d2c8b8b9b9b4e690114feec0102fefe3f81040608038a060c0905204e8565fb8af8514ee6fc89f88afb6162ffff00b8ffec04dd0735
02260038000001070152006001520015b40120052601b8ffffb4212f0b0025012b35002b3500ffff00a4ffec040805e302260058000001060152f3000013400b01231126010624320c1925012b35002b
3500ffff00b8ffec04dd06c10226003800000107014d008f01520015b4011a052601b8ffffb418190b0025012b35002b3500ffff00a4ffec0408056f0226005800000106014d21000013400b011d1126
01051b1c0c1925012b35002b3500ffff00b8ffec04dd07400226003800000107014e007d01520013400b011d0526010022180b0025012b35002b3500ffff00a4ffec040805ee0226005800000106014e
0c000013400b012011260104251b0c1925012b35002b3500ffff00b8ffec04dd07d902260038000001070150007b01520017400d02011d052602010022180b0025012b3535002b353500ffff00a4ffec
04080687022600580000010601500c000017400d0201201126020105251b0c1925012b3535002b353500ffff00b8ffec04dd07730226003800000107015300b201520017400d0201240526020136182c
0b0025012b3535002b353500ffff00a4ffec040e06210226005800000106015350000017400d02012711260201481b2f0c1925012b3535002b353500ffff00b8fe4204dd05b802260038000001070151
02190000000bb6010d251e0b0025012b3500ffff00a4fe420416044a0226005800000107015102b80000000bb6010e21211a1a25012b3500ffff0014000006fe07730226003a00000107014b013b0152
0013400b012b05260100332b1e0e25012b35002b3500ffff0014000005e306210226005a00000107014b00ae00000013400b0130112601003830112e25012b35002b3500ffff00000000043707730226
003c00000107014bffd001520013400b0109052601021109070225012b35002b3500ffff000afe1403df06210226005c00000106014ba9000013400b0123112601022b23000f25012b35002b3500ffff
000000000437072b0226003c00000107006affce01520017400d0201120526020101091d070225012b3535002b353500ffff0052000003fe07730226003d000001070076003901520013400b01160526
015e0a10010025012b35002b3500ffff00520000033506210226005d000001060076d6000013400b0116112601600a10010025012b35002b3500ffff0052000003fe07370226003d00000107014f0129
01520013400b01150526010b0a12010025012b35002b3500ffff00520000033505e50226005d00000107014f00cd00000013400b0115112601140a12010025012b35002b3500ffff0052000003fe0773
0226003d00000107014cffe001520013400b010f05260106150a010025012b35002b3500ffff00520000033506210226005d00000106014c86000013400b010f11260110150a010025012b35002b3500
000100ae000002be061f0013002540150909150015101520150313470054140d5006010015003f3fe10110f6e15d11332f31303311343e023332161707262623220e021511ae2d557c4e3b63262f1f48
28283a271304b06b8d5423170e8d0b1113305341fb4e000100cbfe1403e905cb002b004a402a302d01291f1f01471216121414120a0a00121012301240120407121300601629290d23501c070d50061b
003fe13fe112392f33e132012f5e5d332f11332f113310e1322f325d31300111140e022322262735161633323e02351123353735343e023332161707262623220e0215153315028f2d557b4e203d1c1a
391f283b2613c2c22d557c4e3b63262f1f4828283a2713f60344fc3f6b8d5423090699080a1330534103c34b44896b8d5423170e8d0b111330534191890000040000000004dd07aa001600230031003e
0101405e89290129be2a2a3031311d38830f0d1f0d02100d200d020d0d562366230223070859176917020617051d32830f001f006f00030000d60a01370a470a570ac70a040ad90301380348035803c8
0304031d1d090510040180049004d0040304b8ffc04018060a4804100404400f401f402f408f409f40df4006070809b8fff0403109075f231d200a0e481d0a033c193501358c12103c018f12ef120260
12011031203102233c123131123c23040429090412003f332f1217392f2f2f2f5d5d5d5d10e15d113333332b10e1012f38335e5d11332f382b5d713312393d2f335d5d335d5d33182f5de1111239395d
1239395d332f5d71e111332f33332fed5d31300114060701230321032301262635343e0233321e0203032e03270e030703133e033733150e0307231334262322061514161733323603503c3301fcbe9a
fdd39cbc01f8333a203b523231543e230c9406111212080712121106918515302f2a10db103f4d512379bc3f32313f33310c323f059c466019fb230187fe7904db196046334f381d1d374ffc5f017d10
2e363b1c1c3c362e0ffe83048b183d41401c101641474216fef6343c3c34333b033c0005005effec039c07aa000d0031004000540060009c402355830f411f412f410341415b834b060d4b0d4b0d1a1e
0f37473155623f62013e481a28b8ffd0403a0d11482810090c4828281a5661588c50500d600d700d030d0d5050055e8c404650460246462724502b38521e1e2b1032500f15160e150f050105002f5d3f
3f33e13f392fe110e132332f5de111392f332f5d10e10110e6322f2b2b10e15d10f6e132321139392f2f10cd10e1332f5de13130013e033733150e0307230127230e0323222e02353436373735342e02
2322060727363633321e02151125323e023535070e0315141601140e0223222e0235343e0233321e0207342623220615141633323601c715302f2a10db103f4d5223780152250821424e603f45745530
e7ecb81d375134538f42404ab66466956130fe2f3d684c2b8f5a7a4920610186233d543132523b21213b523230543e23753f31323f3938313f06b8183d41401c101641474216f95c982d412a1427517b
54a4b0080745435a3718302289283829598a62fd107f264d754f630704203951335c5605083351381d1d384f33334f381d1d374f34353c3c35353c3cfffffffe00000656077302260088000001070076
022501520015b40220052602b80148b4141a050025012b35002b3500ffff005effec06440621022600a8000001070076017500000013400b035d112603715157001c25012b35002b3500ffff007dffb4
057107730226009a000001070076010201520013400b033e052603583238110425012b35002b3500ffff0073ffb4042f0621022600ba00000106007650000013400b033a1126034c2e340d0025012b35
002b3500ffff0068fe3b03c905cb022600360000010602390000000eb90001ffdab4343a080025012b35ffff005afe3b033f045e02260056000001060239bb00000eb90001ffe1b4363c080025012b35
0001010204d9039a06210014002540110f0e040400c008040e80000f085f080208002f5d331acc32012f1acc393d2f33333130012326262706060723353e0337331e0317039a79336c34366a33791a44
433b10c0103b43451904d92261373761221b1d4c5151222251514c1d0001010204d9039a06210014002540110605101000c00b130d80100f065f060206002f5d331acd32012f1acc393d2f3333313001
0e0307232e0327353316161736363733039a1945433b10c0103b43441a79336a36346c337906061d4c5150232350514c1d1b2261383861220001011b04d9035a056f00030015400a0100008f0f035f03
0203002f5de1012fcd313001211521011b023ffdc1056f960001012104d9037b05ee0015002f401c1583ff000100c00b830a007f0a8f0a9f0a030a80108f0f055f050205002f5de11acd5d32012fe11a
dc5de13130010e0323222e0227331e0333323e0237037b042c4d6d47496d4927036c041c30432c244133220405ee3d654a292749663f2b321907091b3128000100a00500017505e5000d001c40100887
0003919f0bcf0bef0b03300b010b002f5d5de5012fe1313013343633321e02151406232226a03d2d16271d113f2c2d3d05733c360d1c2b1e3a3938000002016d04d9033106870013001f0040402d1483
0f003f004f005f0004001a83300a010a178c0f0f1f0f3f0f4f0f5f0faf0fff0f07060f1d8c0f055f050205002f5de1d45e5de1012f5de1d45de1313001140e0223222e0235343e0233321e0207342623
22061514163332360331233d543132523b20203b523230543e23753f31323f3938313f05b23351381d1d384f33334f381d1d374f34353c3c35353c3c0001001ffe42015e000000140016400906800084
0d12038e0a002fe12f012fe11acc31301714163332363715060623222635343e02373306b43422192d0e1a401d64641e2f381a818bee2d2b0504710808685a2a4b40341385000001010204d903d105e3
001b003840230f172f1702170009200902070916058f0e401013480e40070b480e0e138f090f000100002f5d32e1332f2b2be133012f5e5dcc5d313001222e0223220607233e0333321e023332363733
0e0302fe284f4c46202d300e680521354a2e2a514c451d2d2e0f690521354a04db232b23353e3c624525232a23343e3c61452600000200df04d903be0621000d001b002d4019140e0e07405000013f00
4f000200130592801b0f0c5f0c020c002f5d331aed32012f5d5d1acd392fcd3130133e033733150e030723253e033733150e030723df162f2f2a10c7103f4d512365016b15302f2a10c6103f4d512364
04f41d4c515122151b5158531c1b1d4c515122151b5158531c00000101f804d903040621000d0027401605100c11480506400c000001000592800f0c5f0c020c002f5d1aed012f5d331acd322b313001
3e033733150e03072301f80a14131004c706212d34186c04f41e4d51502115184e57562000030114050c038906b4000d001b0029006640191c840f241f24020f241f24cf24032440090c4824240d1684
0eb8ffc04027090d480e0e05100c114805060c0d05920c0c1f119127ff19016019b019d019030f191f19020619002f5e5d5d5d33e532392fed012f33cd322b332f2be111332f2b5d71e13130013e0337
33150e03072327343633321e0215140623222625343633321e0215140623222601fe0b19181608cf1230383d2052ea382913231a103a26293801b5382813231a103a26283805871e4b4f512414204d50
51250636300c19261b3532323536300c19261b353232ffffffe9000004dd05bc02260024000001070154fdf1ff970018b6021a0302302201b8ffe9b42222040425012b5d35003f350001009302480191
035e00130033401a101580159015a015040a96c000d0000234004400640074000400b8ffc0b6070a4800059b0f002fe5012f2b5d5ded5d313013343e0233321e0215140e0223222e029314222e1b1a2f
221414222f1a1b2e221402d32635210f0f2135262534221010223400ffffffe70000044d05b800270028008f000001070154fdefff97002eb40111030112b8ffc0b2080f48b8ff9d400f121202022500
00671bbf1b010f1b01015d5d10fe34012b2b35003f35ffffffe70000055005b80026002b7b0001070154fdefff970031b40111030112b8ffc0b2081148b8ffb1401212120606250000656f1b012f1b01
101b011b01105d5d5dfe342b2b35003f3500ffffffe70000036005b80027002c00fc000001070154fdefff970066401601110301c01201a01201301201201201101201001201b8ffa5402f1212060625
00f00001d00001c00001af00017000016000015000014000013f000100201b011b400707481b40070948012b2b5d115d5d5d5d5d5d5d5d5d352b5d5d5d5d5d5d35003f35ffffffe7ffec05c305cd0026
0032520001070154fdefff9700474030022d0302a02e01802e01702e01502e01202e01102e01002e01242e2e0a0a2501005000015000010067bf37010f37013701105d5dfe5d5d34342b5d5d5d5d5d5d
5d35003f3500ffffffe70000055405b80027003c011d000001070154fdefff97004bb4010e03010fb8ffc0b6161748a00f010fb8ffc0b2091148b8ffd6401c0f0f07072500c00501b005017f05012005
01053f18012f18010f1801015d5d5d115d5d5d5d352b2b5d2b35003f3500ffffffe70000060005cd002601765a0001070154fdefff97003540230135030120360110360100360116363613132500501d
01501d011d67bf3f010f3f013f01105d5dfe5d5d342b5d5d5d35003f3500ffffffe4ffec027706b402260186000001070155fed000000018400f030201c0240140240120243a150025012b5d5d353535
ffff0000000004dd05bc020600240000ffff00c70000048705b6020600250000000100c7000003be05b600050043400ac400f40002b000010200b8ffc0401f0813480000070f072f074f076f07040740
111848035a046406025f05030312003f3fe10110f6e12b5d11332f2b5f5d5d313001152111231103befdc3ba05b6a6faf005b600000200250000046805b60005000e0058401105040606020b5b600170
01b001f0010401b8ffc04027060a480101102f105f106f107f109f10bf100610400609480a5b0206200a11480604030a5f0212003fe13f332b012fe12b5d11332f2b5de112393d2f3333313025152135
01330706060703210326260468fbbd01c2bb5e112a1dfe02aefc1f2e7d7d850531cd4aa85bfd0a02f061a800ffff00c7000003be05b6020600280000ffff0052000003fe05b60206003d0000ffff00c7
000004d505b60206002b00000003007dffec057105cd00030017002b00534034000100010e225b04672dd02d01cf2d01802d013f2d6f2d02185b0e662c03600f00af00df00ff00040800001d275f1304
1d5f0913003fe13fe112392f5e5de10110f6e15d5d5d5d10f6e11239392f2f313001211521251402060623222626023534123636333216161205141e0233323e0235342e0223220e0201ec0216fdea03
8551a0ed9ba3ef9d4c4c9ef0a39beba051fbd1346ba57272a56b32326aa47272a66c340339a145a9feeac66c6cc60117aaaa0115c46b6bc5feebab89db99515199db898ada97515197daffff00520000
026405b60206002c0000ffff00c7000004a205b60206002e0000000100000000048b05b6000c005f401c2f0eef0eff0e030e401215480e400d10480e400609480c0b0505090ab8fff040220a012f004f
005f009f00af00cf00ef000710000100100005200a0e48050b030a0012003f323f332b012f385d5d322f3833393d2f33332b2b2b5d313021230126262706060701230133048bc6fedb1f2e0f112a1dfe
d9c501e7bb039a61a84b4ba85bfc6005b600ffff00c70000062f05b6020600300000ffff00c70000050e05b602060031000000030052000003ee05b600030007000b0063403e1400011b010100010001
0b0606000801c008013f0801080d4f0d6f0d02300d010707700b800b024f0b010b035f0f00af0002080000040b5f0a12075f0403003fe13fe111392f5e5de1012f5d5d332f5d5d10ce5d5d71322f1139
392f2f5d5d3130132115210321152101152135cd02a6fd5a52034afcb60373fc64034ea2030aa4fb92a4a400ffff007dffec057105cd020600320000000100c7000004c105b600070031401e015a0065
09b009016f099f09af0903100901045a056408035f0603050012003f323fe10110f6e15d5d5d10f6e13130212311211123112104c1bbfd7bba03fa0512faee05b600ffff00c70000043305b602060033
00000001004e0000041205b6000b005a400d085b062014174802060206010ab8ffc040260615480a0a0d0f0d2f0d6f0d8f0d040607095b032f013f01020108020209075f0403095f0012003fe13fe112
393d2f3301182f5d33e1325e5d11332f2b1239392f2f2be131303335010135211521010121154e019efe6e037dfd580185fe6702f7980266022593a4fdeefda4a400ffff00140000041205b602060037
0000ffff00000000043705b60206003c000000030068ffec05ba05cb0021002e003b008140502202671101115a3b219a12018712017a120112121b285b08673d2f3d01003d01d03d01af3dbf3d02903d
015f3d01303d010f3d1f3d0206355b1b663c223b601310132e2f602102211321132111130004003f3f39392f2f113310e132113310e1320110f6e15e5d5d5d5d5d5d717110f6e111392f5d5d5d3333e1
5d3232313001331533321e0215140e04232315233523222e0435343e0233331333323e0235342e022b03220e0215141e02333302b4bb4986c27e3c183454789f652fbb2f659f785435183d7ec2854abb
1a5d8b5b2e29537f5739bb39577f54282e5b8b5d1a05cbb45e99c4663d817b6e5230e1e130526e7b813d66c4995efc533b6994584e8b683c3c688b4e5894693bffff00000000046005b60206003b0000
00010068000005ba05b60025007040471f670901095a1c9a0a01870a017a0a010a0a13235b0067272f2701002701d02701af27bf27029027015f27013027010f271f270206165b1366261f1c60080b0b
09241d14030912003f3f333312392f33e1320110f6e15e5d5d5d5d5d5d717110f6e111392f5d5d5d33e15d32313001140e04232311231123222e0435113311141e02333311331133323635113305ba18
3454789f652fbb2f659f78543518c12e5b8b5d1abb1abab7c003d73d7f776a4e2efe4201be2d4e68777e3d01e3fe21588d6234035afca6c6b101e3000001004e000005a605cd002f007c40510e5b0b22
5b25200b300b02040b012f253f25df25ef25040b2501070b250b25132b5b0623162302231d6731d03101cf31018031013f316f3102090d190d02080d055b136630005f180426220a0d5f250c12003f33
e13232323fe10110f6e1335e5d5d5d5d5d10f6325de11139392f2f5e5d5d5d5d10e110e1313001220e0215141e0217152135212e0335343e0233321e0215140e0207211521353e0335342e0202fa72a4
6a322351835ffdb00162406f502e51a0ec9a9beba0512e506e410162fdb05f835123326aa405294481ba7564bbab974193a43087a8c76f96f4ac5e5eacf4966fc7a88730a4934197abbb6475ba814400
ffff004000000277072b0226002c00000107006aff0d01520017400d02011505260201000c20010025012b3535002b353500ffff000000000437072b0226003c00000107006affce01520017400d0201
120526020101091d070225012b3535002b353500ffff0071ffec049106210226017e00000106015419000013400b024a112602163e44162f25012b35002b3500ffff005affec035c0621022601820000
01060154ca000013400b01461126016d3a40182d25012b35002b3500ffff00aefe14041206210226018400000106015444000013400b012511260162191f0b1725012b35002b3500ffff00a4ffec0277
062102260186000001070154fece00000015b40122112601b8fffdb41622150025012b35002b3500ffff00a4ffec043d06b40226019200000106015510000012b2030201b8ffeeb42c42051b25012b35
353500020071ffec0491045e0010003d0038401e381e05472f283f103f010e4816563e220f1e0b501b0f382f2c0050331116003f33e13232323fe1333f0110f6e15d10d432e13232313025323e023735
342e0223220615141617222e0235343e023332161733363637330e03151114163332363715060623222e0227230e0302354c69411f021b416c51877f7f665d9a6e3c3d71a2647090310c0a21198f0b13
0d0832220e25080f4122263f32240c0c163b4d60832f6298680f659d6b37daccd1cd97488fd48c8dd59049535523521f2168797e37fe5d3c33070385091110274030223d2e1a000200aefe140475061f
001b003d0059403439480508051f332f330233053305152c470c573f203f303f0222154716543e151b0832500f33010833330022275011161c500001003fe13fe13211392f5e5de1393f0110f6e1325d
10f6e11239392f2f5d123910e1313001321e021514060715161615140e0223222627112311343e0217220e0215111e0333323e0235342e0223233533323e0235342e02027760a37643988fb0b93f78ad
6d60a43cb64579a86338644b2c204e525224506f461f3560844f664d52774e2526445f061f3162946295ad170615caba6ca26d37201ffde906347bb27337961f4c7f60fc92121e150b284d6f4750754d
25982849663d3f5e3e1f0001000afe1403df044a001b006340161d40121548501d010f1d2f1d4f1d03071a0501054806b8fff0401506060b0013130c1a001b201b401b03081b101b0d0cb8fff0400a0c
1a0c0f13000b15051b003f3f33333f33012f38332f385e5d3312393d2f333333182f38e15d5e5d5d2b3130250e031523343e02370133131e0317333e0337133302601622180cbe0f1b2515fe60bdd90e
1e1a1404060514191b0bc7bc123e88867e342a79898d3f043efdba285d5a501b1a525d5a21024c000002006fffec042d061f003200440051402e1a46003805052e33480f2457464046d046e046030f46
01063d482e56450024421a0a38380a425029161550100a01003f33e13fe111392f12391239330110f6e15e5d5d10f632e111392f3939e13130012e0335343e0233321e0217072e0323220e0215141e02
171e0315140e0223222e0235343e0201342e02270e0315141e023332360214385d4325335c7d4a44716051234a2049515b332a3c27121b3c624755865c31457fb26d67ae7f4743729901b41e3952353a
7e6844264969438e9903aa204b56623948694622141f27139114261e121725321b253e3d4129316c7e93587fc183433d75aa6e6aa37a53fe51456f59491f103a629167426f512db40001005affec035c
045e0039005940371d23010118102d573ba03bb03bc03b033f3b5f3b02103b0134462323054718563a1e0250bf3901793989390239390a315028100a501316003fe13fe112392f5d5de1390110f6e133
2fe15d5d5d10e63211392f12393130011523220615141e0233323e023715060623222e0235343e0237352e0335343e0233321e021707262623220615141e0233029c818a8529466037335c51471f3b9e
6d71a66c35263f522c2b46331c3a699056355a524d283f4b81476c732748643d0287995b5e33452a120f181f10a02229315675433e583e290f0b0e2b3e5032466d4a2609131c149322264d4d2d402712
00010071fe6f036a061400330039401f2e03031f461a20140114351035c035020a48290000295634192e0300500100003fe132322f0110e6322f10e15d10d65d32e1332f333130133521150e0515141e
02171e0315140e0207233e0335342e02272e033534123636370607060623ae02b680b87e4c280d2d54784b475f3a1915222b16aa182b22140e305b4d59875c2e5c9ac96c2b2f28602f057b998d76cdb3
9a856f30596c4023110f2e3b482a2d58524b1f204848431d1425221f0f11426c9866940101e8d96c03030204000100aefe140412045e00180031401d004718551a101a601a801a030e0a470b54190e04
5014100c0f0b15001b003f3f3f3fe1330110f6e1325d10f6e131300111342623220e02151123113317333e033332161511035c6970516e431db6941a0a1945525c30b7b9fe1404af828234669460fdc7
044a942b3f2a14bfd2fb470000030071ffec0417062b0013001e0027005b403a2519470057290f2901d029017f299f29024029010f290106241a470a56281a50cb2401ba240189249924020f24010824
24141f500f0114500516003fe13fe112392f5e5d5d5d5de10110f6e1325e5d5d5d5d7110f6e1323130011402060623222626023534123636333216161201323e0237211e0313220e0207210202041734
70b37e76af7438336fb17e76b0753afe2b4968452303fdcb022144684a476644230502330984030cbcfed7ce6d6dce0129bcbc0129ce6c6bcdfed7fcb94a94df9593df954b05104589cd890111011300
000100a4ffec02770448001500354023300901090917001710172017601770179017c017d0170808014714541604500f16000f003f3fe10110f6e15e5d11332f5d31300111141633323e0237150e0323
222e023511015a3f48122d2a24090d293034183e6a4d2c0448fcfc6162040608038a060c0905204e85650304ffff00ae000003f0044a020600fa00000001fff2ffec04170621002e0068402a2215151a
121201290a290a2900501a01341a441a02001a101a201a031a1a3060309030020f3001062e00b8fff0400f002e152901010e17501e1607500e01003fe13fe111392f333f012f38335e5d5d11332f5d5d
5d1239393d2f182f113333111239113331302301272e032322060735363633321e02170116163332363715060623222e0227032e032723060607030e01b8350d1f2b3d2b2232141a42234a694f3f1f01
48122e260e2608173725273b2f250f870b1d1b1605060d351fe50433a02943311b070591070a2a59875dfc3636390703850c0e1128402e01a2215e6157194daa4ffdc100000100aefe140412044a001d
003f40260d0c09470a551f101f201f601f701f801f0514001a471b541e1a1b140d035011160b151c090f003f333f3fe133333f0110f6e132325d10f6e13232313001141633323e023511331123272306
062322262716171616151123113301646a6f526e431cb6931b0a309067486a2301020201b6b60187828234659460023afbb69353542e2a262823552afec0063600010000000003cf044a0014003b4016
14130707000d470e5716101601b016010f1601060100b8fff0b6000714150d000f003f323f33012f38325e5d5d7110f6e112393d2f333331301133131e0317333e02123533140202060723bcc90c1f1e
1905065a724219b6255c9a75c0044afdb02163675c1960cde7010597a3fee0fef5fd7f0000010071fe6f036a06140046007e404e323b3b112a014146382d2d241c000110012001030801011c46201101
11483f485f487f48ef480410480107482456473c323850392a00491c591c691c03381c0107241c0316014f000016390016fa003f3f12392fe11117395d5d113910e132320110f6e15d5d10d65df1c02f
5e5d1112392f33e1123911332f333130011523220e0215141e02171e0315140e0207233e0335342e0227262635343e023735262635343e023706070606232335211523220e0215141e02330323a2517f
572d2d54784b475f3a1915222b16aa182b22140e305b4db2b82f4e663765722d4f6b3e222721552e3e0281374991734720467251037789315571414e603b22110f2e3b482a2d58524b1f204848431d14
25221f0f22c7b54d7f6343110c1c8675486649311303030204998d264e754e3551371c00ffff0071ffec042d045e02060052000000010019ffec04c9044a0018005840380a478f160116160e03141400
0310030203031a2f1a9f1aef1a03100d472f0eef0eff0e030e401015480e5419150c1050120f0e1500500716003fe13f3fe132320110f62b5de1335d11332f5d332f1112392f5de13130253236371506
0623222635112111231123353721152311141604501c2f0f0f4a307171fe6db6dd890427d32f810d08900911848202befc5003b04a509afd4e463700000200a4fe14042d045e001a0027003940212548
005729cf29014029010f2901061f090f471054281b5016100f1b2250080516003f33e13f3fe10110f6e132325e5d5d5d10f6e1313001140e0223222627231617161615112311343e0233321e02012206
07111616333236353426042d3f75a7684b8f360601020201b64178a96961a37743fe3b898203368f4b8c7b7d022789d5914c2d2d2b2e27612efedd041388d3914b4b91d30118c4c8feac3331d3d1d1cf
00010071fe6f036f045e0031005140362727154610000a100a200a030a333f335f337f33ef330410330100481f5632282d51370a0128153815481503150a1f00040f24100ffa003f3f1217395d5de132
0110f6e15d5d10d65d32e1332f313001141e02171e0315140e0207233e0335342e02272e0335343e0233321617072e0323220e02012d1c497e61475f3a1915222b16aa182b22140e305b4d4d84623749
83b36a4e95323617383c3a1a50724922020660794e2f160f2e3b482a2d58524b1f204848431d1425221f0f0f4176b4829ae2954922199a0a130f093970a400020071ffec0485044a0016002900364021
242148121000572b102b902ba02bb02be02b0517480a562a1225500f0f1c500516003fe13fe1320110f6e15d10f63232e132313001140e0223222e0235343e02332115211e0305141e0233323e023534
262723220e02042d3e7ab4756baf7d444d90ce8001e9fef62541301cfd0021476e4d4c6d47214f4b3b5f8b5a2b01f86fc08d50468acb859cda8a3e9a295d6c7c365590693b3762864e90d7592c63a100
00010012ffe50366044a001c003a4024010f0d1f0d02060d571e101e012b1a3b1a021a03471018e01802180219501c0f08501316003fe13fe132012f5de1c65d5d10e65e5d32313001152111141e0233
323e0237150e0323222e0235112135370366fe8c192d3e24122a28240d0d272e351b40755934fed786044a9afda23b5233170306070487060c0b061d4d866802734a5000000100a4ffec043d044a001d
0029401810471b571f101f601f801f03084705541e15060f0d500016003fe13f330110f6e15d10f6e1313005222e0235113311141e0233323635342e0227331e03151002025c81aa6429b62042664692
8d0910170fb60f181008f414508cbd6d0258fdb2568c6336e8f7477d75743d3d7277814bfec1fed300020071fe140525045e00250035005e403c312547169600a6000200000626481f57372037010f37
01e037016f377f379f37034037010f371f37020611470656362b500b1a10311650240116001b003f3f33e1323f33e10110f6e15e5d5d5d5d717110f6e111392f5d33e132313001112e0335343e023717
0e0315141e021711343633321e0215140e02071101342e0223220e0215113e0302666db7864b20384e2d8d253f2d1933587440a5915a9266375690be68014f1e36492b1b312516487b5933fe1401da05
4389d59753988a7d37603368727d476f99613007025ebcc15090c97a95d9904a05fe2604256294653317355741fda0073b6b9f000001ffecfe140425044e002800a0401a681f781f021f67097709c709
030906761c01371c011c1c1e0708b8ffc0400e13164834080108100808c0120112b8ffc04019060b4812122a0f2a3f2a4f2acf2a0406b025c0250225251d1eb8fff0401f1e1e291e1b1706270602181c
281c021c091f0604000f50161b070f2350000f003fe13f3fe11217395d5d3f1101332f3833332f5d5e5d11332f2b5d332f385d2b3312393d2f5d5d33335d335d313013321e021713013301131e033332
363715060623222e022703012301032626232207353636bc2d493a31157b011fb2fe73b20e1d2634251a2e10163928435f47331783feb6c201c6a01b4735241c163e044e1f3e5c3dfea8024afcf8fe20
2642331d05038d060b26466641016afd83033e01be4f590a8f070a00000100a4fe14057106120027004c402f0117472618181e0647115729302940299029b029c029050f291f29020621471e54280b1f
0f012650161916171b0000003f3f3f33e1323f330110f6e15e5d5d10f6e111392f33e132313001113e0335342e0227331e0315140e02071123112e0335113311141e02171103564e835f34080f1810b6
101810085793c46db26fbc894cb6365a78420612fa73093c6c9c6747807e834948837e7e449ddc8c4507fe2601da044188d69a021ffdd9729a612e05058f00010071ffec0596044a003b007040483867
1d011d47d51a01b71a01a91a019a1a01031a1a05254830573d043d143d02c43df43d027b3d9b3d02403d0102303d010f3d1f3d0206104805563c1b1b2a0a0f20155039350016003f3232e1323f33392f
0110f6e15e5d5d5f5d5d5d7110f6e111392f5f5d5d5d5de15d39313005222e0235343e0237330e0315141e0233323e0235113311141633323e0235342e0227331e0315140e022322262723060601e95a
8c60320f1f2f20ba202f1f0f1d364b2d2e442c16b263512d4b361d0f1f2f20bb202f1f0f32608d5a6b8b1f0a1f8b145292cb79518f8787484986878e5262966534294862390132fece818b3465966252
8e8786494887878f5179cb9252575b5b5700ffff0015ffec027705d90226018600000107006afee200000017400d02011f1126020132162a140025012b3535002b353500ffff00a4ffec043d05d90226
019200000106006a00000019b602012711260201b8ffdeb41e32051b25012b3535002b353500ffff0071ffec042d0621022600520000010601540e000013400b022c1126023d20260a0025012b35002b
3500ffff00a4ffec043d06210226019200000106015410000013400b012a1126011d1e24051b25012b35002b3500ffff0071ffec059606210226019600000107015400c3000000214009014811260160
3c013cb8ffc04009090b483d3c42053025012b2b5d35002b3500ffff00c7000003be072b0226002800000107006afff501520017400d02011505260201010c20010025012b3535002b35350000010014
ffec050605b600250085405a0404130c5a0f2101df21ff21022155270f276f277f2703061af41801a018b018027418011818125a130015301540155015c015051515b0130167137713025e13013f134f
130200130113115f1a1a0719155f16031312075f0013003fe13f3fe13212392fe1012f5d5d5d5d5d332f5d10e1322f5d5d5d325e5d10f65d71e112392f31300522262735161633323e02353534262321
11231121352115211121321e021515140e02039e2f46191d442720403321697bfebbbbfeac03b1fe5e015a5d926535355f84140d0ba0090c13335745857374fd230512a4a4fe6f315e8b5989699a6531
ffff00c7000003be077302260161000001070076003301520013400b01120526013e060c040025012b35002b35000001007dffec049805cb0026005f40131140131848071107111bca23017023802302
23b8ffc04024070a48232328bf280105085b1b6627085f0f05af05020805050d24005f20040d5f101613003f33e13fe13312392f5e5de10110f6e1325d11332f2b5d5d1239392f2f2b313001220e0207
2115211e0333323637150e03232226260235341236363332161707262603195fa0784d0d0274fd86054076ab7159a04e274e55613ba4f09d4c57a9faa26cc44f4e3f940527417aaf6fa282c98a482317
a20f170e076cc60116a9a60114c66e2c2a9c202effff0068ffec03c905cb020600360000ffff00520000026405b60206002c0000ffff004000000277072b0226002c00000107006aff0d01520017400d
02011505260201000c20010025012b3535002b353500ffffff48fe7b017305b60206002d000000020000ffe906df05b6002a0035008db5252b5a060d08b8fff84053141748085a23991e011e101e2300
0690060206e02301b423c423d42303002350239023a023040706230623152f5b006737cf37013740060948151536355f0f25af25020825252b085f2303196012132b600612003fe13fe13fe112392f5e
5de11101332f2b5d10f6e11139392f2f5e5d5d5d5e5d1133385d10e12b3210e132313001140e02232111210e03070e032322262735161633323e02373636121237211133321e020133323635342e0223
2306df3c7ec286febffec20f1f21221014354f6e4e234a1c173a20303e2a1a0c0c20272b1602936d98cf7e37fd7777aea42e5b8b5d5801ac619e703d051271f4ecd45167a26f3b0e0b9a0d103f627536
39c80106013aaafd98427198fe9d848846613c1b000200c70000070c05b6001600210053403211175a0e06060a1b5b0067230f23010f231f237f23ff23040d095a0a642221085f110f0daf0d02080d0d
0f0b0317600a0612003f33e13f33392f5e5d33e1320110f6e1325d7110f6e111392f33e132313001140e022321112111231133112111331133321e020133323635342e022323070c3c7ec286febffdb8
baba0248ba6d98cf7e37fd7777aea42e5b8b5d5801ac619e703d02aafd5605b6fd980268fd98427198fe9d848846613c1b00000100140000050605b600150073404d085a0755170f176f177f170306f4
1401a014b014027414011414000e5a0f0011301140115011c011051111b00f01670f770f025e0f013f0f4f0f02000f010f15115f120d5f00000712030f0712003f333f12392fe110e132012f5d5d5d5d
5d332f5d10e132322f5d5d5d5e5d10f6e131300121321e02151123113426232111231121352115210223015a5d926535ba697bfebbbbfeac03b1fe5e0381315e8b59fdf201f67374fd230512a4a4ffff
00c7000004a20773022601b4000001070076008901520013400b0117052601220b11040025012b35002b3500ffff0019ffec04ae0769022601bd000001070236002301520013400b01270526010d2c22
130025012b35002b3500000100c7fe7f04c105b6000b00634041c602d60202025ac503d50302a8030196030103100e11484703013a0301030305095a00650db00d016f0d9f0daf0d03100d01085a0564
0c0a0603085f0502020105002f33332f10e13f330110f6e15d5d5d10f6e111392f5d5d2b5d5d5de15d313021211123112111331121113304c1fe5ab1fe5dba0285bbfe7f018105b6faf00510ffff0000
000004dd05bc020600240000000200c70000043305b60010001b004b402e5f0801080811155b00671d0f1d5f1d7f1dcf1d04060b115a06641c1b5f0f0baf0b02080b0b110a5f070311600612003fe13f
e112392f5e5de10110f6e1325e5d10f6e112392f5d313001140e022321112115211133321e020133323635342e02232304333c7ec286fe960314fda69698cf7e37fd4ea0aea42e5b8b5d8101ac619e70
3d05b6a4fe3c427198fe9d848846613c1b00ffff00c70000048705b6020600250000ffff00c7000003be05b60206016100000002000efe7f050a05b60011001b008b400b181b281b021b0737140114b8
fff8400b141748145a0e0f07010707b8fff04010300ec00e02070e070e05125a10015a00b8ffc04025101548000010651d201d010f1d0108045a05051c145f0e030951081b10065f030005050312003f
332f3310e132322fe23fe11101332fe15e5d5d10e4322f2be110e11139392f2f5d385e5d10e12b5d11335d313001231121112311333e05372111332111210e0507050ab0fc64b0712f564d41301d0402
65c2fe83fefa041f2e3d464d27fe7f0181fe7f022755c8d9e6e8e369faf0046a4cb9cad1c8b74b00ffff00c7000003be05b6020600280000000100040000068105b6001100cb404e6609760986090337
094709570903090606470df70d020d5a6900790089000338004800580003000303a80eb80e02990e01860e01570e670e770e030e0e110a0708080b000a01700a800ac00a030ab8ffc04026070a480a10
0a0a13f01301bf13cf13df1303a013018f13014013010f131f1302080201011011b8fff0400f11111206030304110a0e1207010403003f33333f3333123911331101332f38333311335e5d5d5d5d5d5d
11332f382b5d71333311331112392f5d5d5d5d3311335d5de15d3211335d5d31300101330111331101330101230111231101230225fdedcd020ab3020acdfded0221d3fdeeb3fdeed302f202c4fd3c02
c4fd3c02c4fd3cfd0e02e5fd1b02e5fd1b0000010048ffec03ec05cb00390068403f275b0005210030210000213003131c5b0b673b9f3b0170138013023f134f135f130313133a052060aa2101782101
0f2101082121192f2c6035041960141013003f33e13fe13312392f5e5d5d5de1391101332f5d5d5d10f6e11217392f2f2f11123910e1313001140e0207151e0315140e0223222627351e033332363534
2623233533323e0235342e0223220607273e0333321e0203d1335b7d4b578a5e324388cd896ec0552b6063632eb2b0cfbabfb05c8e613225445f3a6ea94b5c26627483476da671390460497858390c06
0b3959774860a07440222daa1724180d94878781972748653d36533a1e43367d1f36291836618500000100c90000051005b60017006b401b140b0e5a0d6519d01901af190120193019021609025a1764
181709b8ffe8b314184809b8ffe040230a134806091609260903090d12141814184814200a13480914191429140307140b0003003f32325e5d2b2b3f335d2b2b330110f6e132325d5d5d10f6e1323231
30133311140e020706073301331123113436373637230123c9ae0102020204040702ccd5ac0604040508fd31d705b6fce01a3e4343204a4c04b4fa4a0319398e414c4dfb4600ffff00c9000005100769
022601b2000001070236008901520015b4011d052601b8ffebb42218000c25012b35002b3500000100c7000004a205b6000a005e4021080909100901000010000207001000000cb00c012f0c01100c01
970a01560a010ab8fff84014080c480a0707035a04640b070204080503040012003f323f331239390110f6e13211332b5d5d5d5d5d11332f385e5d33333811333130212301112311331101330104a2db
fdbababa0235cffdcb02e5fd1b05b6fd3c02c4fd420000010000ffe9049305b6001f0062b10803b8fff8403b141748035a891901191019e01e01b41ec41ed41e03001e501e801e03071e1e10015a0065
210f211f217f210308101020035f1e031460110d130012003f3f33e13fe11101332f5e5d10f6e111392f5e5d5d5d33385de12b323130212311210e03070e032322262735161633323e02373636121237
210493bafe850f1f21221014354f6e4e234a1c173a20303e2a1a0c0c20272b1602d0051271f4ecd45167a26f3b0e0b9a0d103f62753639c80106013aaa00ffff00c70000062f05b6020600300000ffff
00c7000004d505b60206002b0000ffff007dffec057105cd020600320000ffff00c7000004c105b60206016e0000ffff00c70000043305b6020600330000ffff007dffec049805cb020600260000ffff
00140000041205b602060037000000010019ffec04ae05b60021006f401012011b1b132160007000b000f0000400b8ffc04010060a4800100000230f237f2302140913b8fff0400c131322e71af71a02
d61a011ab8ffe040100a0e4812011a03000d5f0a0613130003003f323f33e11117392b5d5d1101332f3833335d11332f382b5d331239113333313001010e032322262735161633323e02370133011e03
17333e03370104aefe542653709c6f335a25255934355242381cfdeecc017f050a09090402030b0b0b03013705b6fbfa5da47b480f0fb914191735563f043ffcd70a181a1709091f211d07031800ffff
0068ffec05ba05cb020601730000ffff00000000046005b60206003b0000000100c7fe7f057105b6000b003b4021035a0202095a000db00d01af0d01100d01085a05640c0a060300085f050202051200
3f332f10e1323f330110f6e15d5d5d10d4e1332fe1313025331123112111331121113304c1b0b0fc06ba0285bba6fdd9018105b6faf00510000100a60000048105b600150048401d13015a0065177017
a017022f17011017010d5a600a700aa00ab00a040ab8ffc0400e070a480a13105f0505140b030012003f3f33392fe133012f2b5de15d5d5d10f6e1323130212311060623222e02351133111416333236
3711330481ba73c3625d926535ba697b5ab970ba02562c2e315f8a590247fdd17374282802c6000100c70000073305b6000b00604042560801085a17050186059605a605036705770502050501095a00
650d000d100d02200d500d700d800dc00dd00de00d070f0d0107045a01640c0a06020308045f0112003fe1323f33330110f6e15e5d5d7110f6e111392f5d5d71e15d3130212111331121113311211133
0733f994ba021fba021fba05b6faf00510faf0051000000100c7fe7f07e305b6000f0071404b560c010c5a17090186099609a6090367097709020909050d5a00035a0202006511001110110220115011
70118011c011d011e011070f110107085a0564100e0a06030c00085f0502020512003f332f10e132323f33330110f6e15e5d5d7110e4322fe110e111392f5d5d71e15d31302533112311211133112111
33112111330733b0b0f994ba021fba021fbaa6fdd9018105b6faf00510faf005100000020014000004d505b60010001b005240330b115a900601060608155b00671d0f1d010f1d2f1d3f1d5f1daf1d05
0608081c1b5f0f0baf0b02080b0b11085f090311600612003fe13fe112392f5e5de11101332f5e5d7110f6e111392f5de132313001140e022321112135211133321e020133323635342e02232304d53c
7ec286fe95feac020f9598cf7f37fd4ea0aea32d5c8a5d8101ac619e703d0512a4fd98427198fe9d848846613c1b000300c7000005cf05b6000e0019001d0049402b135b0000010000061a5a1d651f0f
1f9f1f0206090f5a06641e195f0f09af09020809091b07030f601a0612003f33e13f33392f5e5de10110f6e1325e5d10f6e111392f5de1313001140e02232111331133321e020133323635342e022323
0111331104333c7ec286fe96ba9698cf7e37fd4ea0aea42e5b8b5d810393bb01ac619e703d05b6fd98427198fe9d848846613c1bfd5605b6fa4a000200c70000043305b6000e00190048402e135b9000
0100671b0f1b5f1b02061b401518481b400d1348090f5a06641a195f0f09af090208090907030f600612003fe13f392f5e5de10110f6e1322b2b5e5d10f65de1313001140e02232111331133321e0201
33323635342e02232304333c7ec286fe96ba9698cf7e37fd4ea0aea42e5b8b5d8101ac619e703d05b6fd98427198fe9d848846613c1b0001003bffec044405cb00240051402f1d205b0c161e161e040c
6726bf26cf26df26030404251e5faa1f01781f010f1f01081f1f071a5f17111303005f0704003fe1333f33e111392f5e5d5d5de11101332f5d10e61139392f2f10e13231300122060727363633321616
12151402060623222e0227351616333212132135212e0301ba5e943f4e4fc46ca2f4a25252a5f7a43a61564e274ea059edf706fd8b02710947739d05272e209c2a2c6dc1fef69db3fee0ca6d070e170f
a2172301170106a26fb07a40000200c7ffec079e05cd001a002e005e403c1b5b0a90110111110d255b0067300f30013f306f307f30ef30ff30053040111448100c5a0d642f2a5f16040b5f0f10010810
100d0e030d12205f0513003fe13f3f12392f5e5de13fe10110f6e1322b5d7110f6e111392f5d33e131300114020606232226260227211123113311213e03333216161205141e0233323e0235342e0223
220e02079e4f9be59697e1985006feaebaba01560b5598db9195e59a4ffbfa31679d6c6d9d663030659d6c6d9e673102dda9feeac66c64b70103a0fd5605b6fd9892eca75a6bc5feebab89db99515199
db898ada97515197da0000020019000003f205b6000f001c009b4009060001e600f6000200b8fff840240b0f481c0e5a030000080d651e3f1e8f1e9f1ebf1edf1e05201e01060101e601f6010201b8ff
f8400d0b0f48014b02010240080b4802b8fff040260202165bdf08ef08ff080308400f134807081708020808661d1c60030f0f0111600b030e0112003f333fe112392f33e10110f65e5d2b5de1332f38
2b5d332b5d715d5d10f611391133e1322b5d713130010123012e0335343633211123111123220e0215141e0233330242feb2db017d37634b2cfdfb0175bbbc4a734f29274e7751b4025cfda4028f143e
61885dc6c9fa4a025c02bb1d3e614442694a2800ffff005effec039c045e02060044000000020075ffec042106230027003b0049402c2d48091e573dd03d017f3d01403d010f3d010613374800563c32
50131919082850231609080b104809510801003fe12b3fe111392f33e10110f6e1325e5d5d5d5d10f632e13130133412363637363637170e03070e0307333e0333321e0215140e0223222e0201323e02
35342e0223220e0207141e02753168a2717df26621327b7d752c3e644829040d123e546b4063966532457cae686bad7b4201e13c6246261a3a5a413d6a53390d1941720293b50119cb7e191b3114a008
1618180a0e4271a5711d3c31204983b46c89ce8a4559adfdfe9427588f6954865f333044491a64bc9359000300ae00000419044a00150020002b005c403a031b214600000f164709572d202d01402d90
2db02dd02df02d050f2d0106261b470f542c031a50bf2601792689260226261b2550100f1b500f15003fe13fe112392f5d5de1390110f6e1325e5d5d7110f6e112392fe11239313001140607151e0315
140e0223211121321e0203342623211121323e0203342623211133323e0203f67b6b386147292f659f71fe3901c5538e673b908280ff000104385d43261e6d6cfef5ec3e5c3f1f03356875120707243d
5b3f43745630044a1c406bfdc75c48fea8122a45020c4d40fed70f243c00000100ae0000030a044a0005002f401c0000100002000007a0070181070150070102470354060150040f0215003f3fe10110
f6e15d5d5d11332f5d3130012111231121030afe5ab6025c03b0fc50044a00020029fe830437044a000f001700794010170712460c2f073f07020b071b070207b8fff04039a20cb20c02040c140c0207
0c070c0510470e01460000200e010e5519af19cf19023019012f19010446050518124f0c0f170e065003150500fb003f323fe132323fe11101332fe15d5d5d10e45d322fe110e11139392f2f5d5d385d
5d10e11133313001231121112311333e0212372111332111230e03070437aefd4eae564163432201021797feb2c007243a5134fe83017dfe8302175fdff301007ffc50032467d7d1c253ffff0071ffec
03e1045e02060048000000010004000005be044a0011009c401f0603030a460f0000d60b01b90bc90b02a60b01970b010b0b0e070405050807b8ffc0b312154807b8ffc04024070b480f070106071007
07130013101302df1301301370139013030f1301071110100d0eb8fff0400f0e0e12030000010d080a151104010f003f33333f3333123911331101332f38333311335e5d5d5d7111332f385e5d2b2b33
3311331112392f5d5d5d5d331133e13211333130011133110133010123011123110123010133028fa401a0c5fe5801cecefe43a4fe44cf01cffe58c502350215fdeb0215fdebfdcb022dfdd3022dfdd3
0235021500010044ffec0352045e0039005f403a0546161b16391639160c344721573bb03bc03bd03b036f3b01203b01290c563a1b3850cf390189399939020f3901083939112f50261608501110003f
e13fe111392f5e5d5d5de1390110e6325d5d5d10f6e11239392f2f123910e1313001323e0235342623220607273e0333321e0215140e0207151e0315140e0223222627351e0333323e02353426232335
01713d644827736c47814b3f284d525a355690693a1c33462b2c523f26356ca6716daf3a1f4c57613337604629858a8102871227402d4d4d262293141c1309264a6d46324d3b290e0b0e2c425a3e4375
56312322a6101f180f122a45335e5b99000100ae0000043b044a000b0051401b031009184803064605550d000d100d200da00dc00dd00df00d070db8ffc0b30e114809b8fff040110918480900460a54
0c08030b0f06020a15003f33333f33330110f6e1322b2b5d10f6e1322b3130011107013311231113012311015a0c0204e9ac0bfdfeea044afd64fc0398fbb60287010ffc6a044a00ffff00ae0000043b
0617022601d200000106023625000015b40111112601b8ffffb4160c0a0425012b35002b3500000100ae000003e5044a000a00414023000101040003100302031003030c0f0c010a02064707540b0502
0a0a0700080f040715003f333f3312391133330110f6e132325d11332f385d333311333130013301012301112311331102fac4fe6101c6cefe4bb4b4044afdf1fdc5022dfdd3044afdeb00010010fff2
03b0044a0014004c40310346041301f4130180139013c013d0130454130113130b01470055160f162f16ff1603060b0b150350130f0e4f08160115003f3fe13fe11101332f5e5d10f6e111392f5d5d5d
71e1313021231121020206062322262735163332363612372103b0b6fefe14405f82561c3110171b3759453312025403b0fefdfe94e6690606830675f1016ffa000100ae00000510044a0020005e403f
1f0146005522cf220140225022020f220106131046115421020f3b0f4b0f5b0f03090f190f290f030f1f120f34194419541903061916192619031909011115003f3333335d5d3f33335d5d11330110f6
e1325e5d5d5d10f6e13231302123110e03070123012e032711231133011e03173e033701330510a10611141207fef887fef70511141207a2dd010b0d16130f04060e13160d010ed9038115393b3612fd
5002b00d313b3e1afc7f044afd4722433c321011313b412102bd000100ae0000043b044a000b005b401702064705550d000d200d02300d400d500dd00df00d050db8ffc040230e11480109470a540c08
50eb0101a901b901020f011f012f01030601010a051503000f003f323f33392f5e5d5d5de10110f6e1322b5d7110f6e132313001112111331123112111231101640221b6b6fddfb6044afe3901c7fbb6
01e9fe17044affff0071ffec042d045e020600520000000100ae00000412044a0007002d401c0547045509100920096009700980090500470154080750020f050015003f323fe10110f6e15d10f6e131
3021231121112311210164b60364b6fe08044afbb603b000ffff00aefe14043f045e020600530000ffff0071ffec036f045e020600460000000100290000035e044a00070058403c200901df09017009
8009025f09010f0001cc00010f00bf000206000002470360057005d005031f0501050500031003b003c0030403010550060f0215003f3fe132012f5d332f5d5d10e1322f5e5d5d715d5d5d7131300121
112311213521035efec1b7fec1033503b0fc5003b09affff000afe1403df044a0206005c000000030071fe14052306140019002200290074404b27000b461d170c0d1349b90cc90c02970ca70c020c0c
12234805572b202b010f2b019f2bef2b02802b016f2b01402b010f2b1f2b02061a4812562a1800261e50001710271d500a0d160b1b003f3f33e1323f33e1323f0110f6e15e5d5d5d5d5d717110f6e111
392f5d5d2b3333e132323130011e0315140e02071123112e0335343e0237113301141617110e0305342627113636032171bd884c4786be77aa72bf894c4785c179aafe0c9cae587d502503399baaac99
045a0c558fc67d7dc891560bfe2401dc0a5690c97e7dc78f550b01bafc13bbd213033c093d67915ebacc14fcc614d100ffff0023000003db044a0206005b0000000100aefe8304b2044a000b0044402b
01470f000106000007478f0a010a0d100d200d600d700d800dc00d06064703540c08040f0a0650031501fb003f3fe1323f330110f6e15d10d45de1332f5e5de1313001231121113311211133113304b2
b6fcb2b601f8b6a0fe83017d044afc5003b0fc500001009a000003fe044a00180031401c080c470b551a101a601a801a03014717541908035012120b1509000f003f323f392fe1330110f6e15d10f6e1
32313001111433323e023711331123110e0323222e0235110150b82d504c4d2ab6b62c50555e3a4e7a522b044afe66ae0f1c2c1c01d5fbb601e91e3020112f54734401a6000100ae00000646044a000b
006640430047b609c60902a90901037409840994090367090106090905014704550d200d010f0d01800df00d026f0d01400d010f0d2f0d0206084705540c00085005150a06020f003f33333fe1320110
f6e15e5d5d5d5d717110f6e111392f5f5d5d5f5d5de1313025211133112111331121113303d501bab7fa68b601bbb69a03b0fbb6044afc5003b0000100aefe8306e5044a000f0077404e0a47b607c607
02a907010374078407940703670701060707030b470e01470e00010000bf0e010e112011010f110140118011e011f011040f112f11020606470354100c08040f0e0a0650031501fb003f3fe132323f33
330110f6e15e5d5d717110c45d322f5de110e111392f5f5d5d5f5d5de131300123112111331121113311211133113306e5b6fa7fb601bbb601bab79ffe83017d044afc5003b0fc5003b0fc5000020029
000004fc044a000e001b005c403e0f4704571d501d601d901de01d043f1d010016470a000c100c02b00cf00c020c0c000a100a500a600aa00a05080a1550bf00010000160c500d0f16500a15003fe13f
e112392f5de1012f5e5d332f5d7110e1325d5d10f6e131300121321615140e0223211121352101342e0223211121323e0202480114d4cc2f659f71fe39fe98021f020221416040ff000104375d442602
879c9b4d7c582f03b09afcf831422811fea811294200000300ae00000550044a000c0010001d0061404018470004100420047004d004050604040a0e470d551f0f1f01bf1fcf1fdf1fff1f04a01f010f
1f7f1f02070012470a541e1150bf000100000f0b0f12500e0a15003f33e13f33392f5de10110f6e1325e5d5d5d7110f6e111392f5e5de131300133321615140e022321113301231133011133323e0235
342e02230164ecd4cc2f659f71fe62b603ecb6b6fc14db385d43262141604002879c9b4d7c582f044afbb6044afda4fea8112942303142281100000200ae00000419044a000c00190040402914470457
1b001b01001b201b701b901bb01bd01b0608000e470a541a0d50bf000100000b0f0e500a15003fe13f392f5de10110f6e1325e5d7110f6e131300121321615140e0223211133111121323e0235342e02
2301640115d4cc2f659f71fe39b60104385d43262141604002879c9b4d7c582f044afda4fea8112942303142281100010037ffec0342045e0026005e403e0e0e040c0f482257288f28af280240280118
0f041f042f040304400c11480458270d50ef0e01a90eb90e020f0e1f0e2f0e03060e0e0712511d1007510016003fe13fe112392f5e5d5d5de10110e62b71325d5d10f6e13211392f3130052226273516
1633323e0237213521262623220e0207273e0333321e0215140e020142597c363683524974523004fe2301db0d928c193b3c3817351840484e2666b3844d518cba141e1da219252854845c9aa697090f
130a9a0c1610093e89db9d92d48a4300000200aeffec0608045e001a00260072404b1b480a2011014f110111110d21480057280f282f2802ef28ff28024728010f282f2802100c470d5427245016100b
50ef1001a910b910022c10010b101b10020610100d0e0f0d151e500516003fe13f3f12392f5e5d5d5d5de13fe10110f6e1325d5d5d7110f6e111392f5d7133e1313001140e0223222e02272111231133
11213e0333321e0205141633323635342623220606084078ab6a62a2754607feefb6b601130a4876a06362a77944fd29808e8f7e808f8e7e022789d5914c4281be7cfe17044afe3974b1783e4b91d388
d1d3d3d1d1cfcf000002002300000391044a0011001c0053401402111107180f470e551e0f1e6f1e8f1e03060001b8fff0401901011246074012154807561d02105018180019500d0f0f0015003f323f
e112392fe1320110f62be1332f38335e5d10f6e1321139113331303323012e0335343e0233211123112303141e02333311232206e9c601232d54412838648a5301bcb6f4dd25415a36dbfe6b6801cd0c
304c6c474e79512afbb601b0015430462e1601665f00ffff0071ffec03e105d90226004800000106006ada000017400d0302311126030200283c050f25012b3535002b35350000010012fe1404120614
0031005340321f170c472d55331033603380330323221e16471b17543221194f1e1a1a1c23105000291029202903290f1c0017150750001b003fe13f3f3f5de13312392f33e1320110f632e13232325d
10f6e1123931300122262735161633323e023511342623220e02151123112335333533152115211507333e033332161511140e0202f0303f171a36231b2e23136970516e431db69c9cb6017bfe85080a
1945525c30b7b922486dfe140e0b940a0b0f2741330344828234669460fdf004d589b6b689b8902b3f2a14bfd2fcbe4d7b572f00ffff00ae0000030a0621022601cd000001060076d4000013400b0112
11260145060c030025012b35002b350000010071ffec036f045e0022004f40311717161f0d248f24af24024024011619480556231950ef1601a916b916020f161f162f16030616161c13510a101c5100
16003fe13fe112392f5e5d5d5de10110f6e1325d5d10ce3211392f313005222e0235343e0233321617072e0323220607211521161633323637150606025265b0824a4c85b2664e95323617383c3a1a8b
920e01dbfe2309928a518336367b143f89d5969ddb893e22199a0a130f0997a69ab8a42519a21d1effff005affec033f045e020600560000ffff00a00000017505e50206004c0000ffffffee00000225
05d9022600f300000107006afebb00000017400d02010d11260201000418010025012b3535002b353500ffffffbcfe14017505e50206004d000000020010fff20612044a001d002a006b40450c461c00
25470ad41c01901ca01c02241c01001c011c0a1c0a141e4704572c0f2c2f2c3f2c5f2c7f2cbf2cef2c0714142b2450bf00010000250c501c0f174f111625500a15003fe13fe13fe112392f5de1110133
2f5d10f6e11139392f2f5d5d5d5d10e13210e131300133321615140e0223211123020206062322262735163332363612372101342e0223231133323e020387ecd4cb2e659f71fe62d914405f82561c31
10171b3759453312022b01d921416040d7db385d432602879c9b4d7c582f03b0fefdfe94e6690606830675f1016ffafcf831422811fea8112942000200ae00000675044a0014002100534030051c4702
0f0f131547095723ef23018023013f23011023010112471354221b115005bf01010101001c50130f1503000f003f323f33e111392f5d33e1320110f6e1325d5d5d5d10f6e111392f33e1323130011121
11331133321615140e022321112111231101342e0223231133323e02016401cfb6ecd4cc2f659f71fe62fe31b605152141613fd8dc375d4426044afe3901c7fe3d9c9b4d7c582f01e9fe17044afcf831
422811fea811294200010012000004120614002100534032681401140b0047215523102360238023031716120a470f0b5422150d4f120e0e10170450001d101d201d031d0f10000b0015003f323f3f5d
e13312392f33e1320110f632e13232325d10f6e111395d31302111342623220e02151123112335333533152115211507333e033332161511035c6970516e431db69c9cb6017bfe85080a1945525c30b7
b9029a828234669460fdf004d589b6b689b8902b3f2a14bfd2fd5c00ffff00ae000003e50621022601d400000106007608000013400b01171126010c0b11070325012b35002b3500ffff000afe1403df
06170226005c000001060236b1000013400b01281126010a2d23000f25012b35002b3500000100aefe830412044a000b0059403dd60601b706c706020647b507c507d5070376078607024a075a070207
0709014704550d100d200d600d700d800d05004709540c050050091506fb0a020f003f333f3fe1330110f6e15d10f6e111392f5d5d5de15d5d3130252111331121112311211133016401f8b6feb3b7fe
a0b69a03b0fbb6fe83017d044a00000100c7000003be06e300070044402c005adf03ef03ff03030003100302030309b009d009020f09af0902055a066408045f07200101010107030612003f3f332f5d
10e10110f6e15d5d11332f5d5de131300111331121112311030eb0fdc3ba05b6012dfe2dfaf005b6000100ae0000030a058900070030401c05470000100002000009024703540810062006020606014f
040f0215003f3fe1332f5d0110f6e111332f5de131300121112311211133030afe5ab601a6b603c1fc3f044a013fffff0014000006fe07730226003a00000107004300e701520015b4012b052601b8ff
acb4312b1e0e25012b35002b3500ffff0014000005e306210226005a0000010600434c000015b40130112601b8ff9eb43630112e25012b35002b3500ffff0014000006fe07730226003a000001070076
01a001520013400b0137052601642b311e0e25012b35002b3500ffff0014000005e306210226005a000001070076011700000013400b013c112601693036112e25012b35002b3500ffff0014000006fe
072b0226003a00000107006a013901520019b602013405260201b8ffffb42b3f1e0e25012b3535002b353500ffff0014000005e305d90226005a00000107006a00ac00000019b602013911260201b8ff
ffb43044112e25012b3535002b353500ffff00000000043707730226003c000001070043ff7201520015b40109052601b8ffa4b40f09070225012b35002b3500ffff000afe1403df06210226005c0000
01070043ff4b00000015b40123112601b8ffa4b42923000f25012b35002b35000001005201d103ae02790003001db90002ffc0400b060c480202050000b901bd003fe1012f11332f2b31301335211552
035c01d1a8a80001005201d107ae02790003001db90002ffc0400b060c480202050000b901bd003fe1012f11332f2b31301335211552075c01d1a8a80001005201d107ae02790003001db90002ffc040
0b060c480202050000b901bd003fe1012f11332f2b31301335211552075c01d1a8a80002fffcfe31034effd300030007002a401804000009050105baef06ff06020602ba0001100120010301002f5de1
2f5de1012f3311332f3331300121352135213521034efcae0352fcae0352fe318b8c8b000001001703c1015005b6000c002540175f0e01060f075f076f07bf07cf0705070c98010c9c0603003fe5012f
e12f5d335d313013273e0337330e0307250e0e272e3419890f1d1a160803c116367a7c7b383d84837c350001001703c1015005b6000c002540175f0e0106070c980f015f016f01bf01cf010501069c00
03003fe5012f5de12f335d313001170e0307233e033701420e0e272f3319890e1d1b160805b61637797d7a383c84847c35000001003ffef8017900ee000c0035b9000effc040140a18480c980f015f01
6f017f01cf010501010607b8ffc0b710154807069c00a8003fe5012f2b33332f5de12b313025170e0307233e0337016a0f0e272f33198a0f1d1b1608ee17367a7c7b383d84837d350001001903c10152
05b6000c002b401c5f0e01060f055f056f057f05bf05cf05df0507050500980b069c0003003fe5012fe1332f5d335d3130131e0317232e032737ee08161a1d0f8919342e270e0e05b6357c84843c387a
7d7937160002001703c102d105b6000c001900624048bf1b01901b010f1b5f1b6f1b03130f145f146f147f14bf14cf14df1407141419980e0c980001500160017001b001c001d001070101060f075f07
6f07bf07cf070507190c9c130603003f33e532012f5d33332f5de12fe1332f5d335d5d5d313001273e0337330e030721273e0337330e030701a60e0e272e3419890f1d1a1608fdb80e0e272e3419890f
1d1a160803c116367a7c7b383d84837c3516367a7c7b383d84837c350002001703c102d105b6000c001900624048bf1b01901b010f1b5f1b6f1b03130014501460147014b014c014d01407141419980f
0e5f0e6f0ebf0ecf0e050e0c980f015f016f017f01bf01cf01df01070101060713069c0d0003003f32e532012f33332f5de12f5de1332f5d335d5d5d313001170e0307233e033721170e0307233e0337
01420e0e272f3319890e1d1b160802480e0e272f3319890e1d1b160805b61637797d7a383c84847c351637797d7a383c84847c350002003ffef802fa00ee000c0019007e4051d01be01bf01b03a41bb4
1bc41b03901b0102201b301b401b601b701b801b06130014501460147014c014d0140614141998900ee00ef00e030f0e5f0e020e0c980f015f016f017f01cf01df010601010607b8ffc0400a10184807
13069c0d00a8003f32e532012f2b33332f5de12f5d5de1332f5d335d5f5d5d5d313025170e0307233e033721170e0307233e0337016a0f0e272f33198a0f1d1b160802480e0e272f3319890e1d1b1608
ee17367a7c7b383d84837d3517367a7c7b383d84837d35000001007b000003680614000b007c4052900da00de00df00d046f0d01100d300d400d030706c004080803e40bf40b02d60b01770b016a0b01
540b01450b01260b010bc0090a01090936020102be00031003a003030304010005bf0b0a0706c2080003002f3ff6323232e1323232012f5de15d3211333310e65d5d5d5d5d5d5d11332f33e6335d5d5d
31300125132313053505033303250368feb537d937fec9013737d937014b03dd1ffc0403fc1fb41e01a1fe5f1e000001007b0000037d0614001500b04071001701a017b017f017037f17012017401750
1703080dc00e0b0f0f0501041201f41201e612018712017a1201641201551201361246120212c0101411101015030004be050a0a09060005010005100520055005a005b00506080514130b0cbf12110e
0dc20f0009010008bf06030207c205002ff6323232e13232323ff6323232e1323232012f5e5d713333332f10e13232323211333310e65d5d5d5d5d5d5d713211332f3333e6325d5d5d71313001251525
1323130535050313053505033303251525130231014cfeb437d938feb4014c2f2ffeb4014c38d937014cfeb42f01f01eb41ffe8701791fb41e012201131fb41e0178fe881eb41ffeed000001009601e5
026d03f20013004640242f155f156f157f15cf15ef15ff15071015015f0a6f0a9f0aaf0adf0aef0a060ad0000100b8ffc0400c070a48001f0f010f10050105002f5dc55d012f2b5dc55d5d5d31301334
3e0233321e0215140e0223222e0296243f5632315640252540563132563f2402ec47643f1c1c3f644746643f1e1e3f6400030093ffe305db00fa00130027003900a84076143b243b02fb3b01e43b01bb
3bcb3b02a43b018b3b01643b743b024b3b01343b010b3b011e9666147614021414002896243201fb3201e03201d43201bb3201a432018b32017232016632014b3201303201022032010f320106320a96
e000f0000254006400020000100020000307002d19059b37230f002f3333ed3232012f5e5d5d5ded2f5e5d5d5f5d5d5d5d5d5d5d5d5d5d71ed11392f5ded5d5d5d5d5d5d5d5d5d71313037343e023332
1e0215140e0223222e0225343e0233321e0215140e0223222e0225343e0233321e0215140e022322269314222e1b1a2f221414222f1a1b2e2214022514222e1b1a2f221414222f1a1b2e221402251323
2e1b1a2f221414222f1a36496f2635210f0f21352625352210102235252635210f0f21352625352210102235252635210f0f213526253522104200070066ffec08f405cb0009001d0027003b003f0049
005d0089405c40b454b54a1eb432b523b428a03e01303c403cb03c0330284028023e3c28283c3e031445b44a5f3f5f4f5f5f5f7f5faf5f0505b40ab500b4101420143014031447b659b743b64f193f06
3e1825b637b721b62d1903b60fb707b61907003fe1f4e13fe1f4e13f3f3fe1f4e1012f5de1f4e15d10dee11217392f2f2f5d5d5d10e1f4e110f4e131301314163332111023220605140e0223222e0235
343e0233321e020114163332111023220605140e0223222e0235343e0233321e02010123010114163332111023220605140e0223222e0235343e0233321e02fa47509c9c504701c7244a734f49704c26
2349714e4b714d2701ac47509c9c504701c6234a734f4a704b262349714e4b714c27ff00fcd59e032c029747519b9b514701c7234a734f4a704b262349714e4a724c270402a5a5014a0148a3a56cac76
3f3f76ac6c6caa753e3e75aafd4aa5a401490148a3a56cab763f3f76ab6c6caa753e3e75aa0392fa4a05b6fc02a5a401490148a3a56cab763f3f76ab6c6caa753e3e75aaffff008503a6014a05b60206
000a0000ffff008503a602b205b602060005000000010052007301fc03c70006003cb10402b8ffc0401f090c4802083f089f08af08df08ef08ff080606eb9f03010306000303010501002f2f12393d2f
333301182f5de15d10c62b3231301301170313070152013575eeee75fecb0229019e4efea4fea44e019b00010052007301fc03c70006003f402800ebdf03ef03ff03031003200302033f089f08af08df
08ef08ff0806043f02010206000303010501002f2f12393d2f333301182f5d335d2f5d5de131300101271303370101fcfecb75eded750135020efe654e015c015c4efe62ffff0093ffe3036205b60027
000401d100000106000400000009b52f31011031015d5d000001fea00000026805b60003001db10102b8fff04009020300100001120003003f3f012f38322f38333130010123010268fcd59d032b05b6
fa4a05b60001006a031d029305c700140041401700e000140114166016c016e016f016040f16010e0ae00bb8ffc0400e0a0f480b0a00c00e04e411de0cdc003f3fe1331acc32012f2be1325d5d10d65d
e131300111342623220e0215112311331733363332151102143f3f2d412a157f6910094082e5031d01a6514415345741fea6029d5865fafe5000000100600000040005b6001100764048021002100500
0b100b200b030b0b133013010e00045a050905df0701070700051005c005030705030760080008115f0e0f0e3f0e6f0e03080e40101548080e080e040d5f0a030412003f3fe11239392f2f2b5e5d10e1
113310e132012f5e5d332f5d113310e132325d11332f5d1239392f2f313001211521112311233533112115211121152101c30124fedcb3b0b002f0fdc30216fdea018f89fefa0106890427a4fdfca400
000100440000042305c9003000874010110d156f2b27230f13231323131e031bb8ffc0403e090e481b1b3210320129251e400b0e481e1425752611261029752a0d2a6f2601d02a010f2a1f2a3f2a4f2a
af2abf2a06262a262a001e1a741d1807730007003fe13fe1321139392f2f5d5d5d113310e132113310e132012f2b33335d11332f2b331239392f2f1133113333e1323231300132161707262623220e02
15152115211521152115140e0207211521353e0335352335333523353335343e02029a6aae4242388d4b30523c210178fe880178fe881727331b02ecfc212c49351ec6c6c6c63b699205c92d23901d2b
1b3b5e42c0899e89133e59402b10a69a0b2944614315899e89dd57895f3200030096ffec05b605b6001b002a003500794018316f1c12100f6e1815181c181c1824ff0501054012154805b8ffc0402c09
0e480505370f370137400b10482b236e247c360f18751512152b7322152215222435732506241800750919003fe13f3fe11239392f2f10e1113310e1320110f6e1322b5d11332f2b2b5d1239392f2f11
3310e1323210e1313025323e023715060623222e02351123353737331533152311141601140e02232311231133321e020133323e023534262323053b0f24231e0717503f355840249c9c416bd1d134fe
8c347ccc982fb2fb86bf7b3afdbd1a5d8b5b2ea4ae3981040608038a0c1422466a4801bf524dbdd389fe564c4e03895ba8814dfdc705b6396da0fe67204771518e890001003fffec044a05cb00390086
b41207283619b8ffc0404b090d4819193b103b012c232313050d6f2e2263280128283a132360241024082c602d052d5f247f248f24ef24ff24050f2d1f2d2f2d5f2d9f2dcf2d06242d242d1637007433
07165f191d19003f33e13fe1331239392f2f5d5d113310e132113310e1321101332f5d3333e132323311335d11332f2b33123939313001220e0207211521140706141514141721152116163332363715
060623222e02272335332634353436352335333e0333321617072626030845765e431201b0fe410101020181fe9222b9954b873b3b855b73b6875815a494020294a0125887b87261a04f503377052734
638f5b890f0e0c1a0913291689afb8201aa21c1f4987c17989171e1d162e08897dca904e2b31921f2b000004008dfff805db05c100030017002b00480086400a0102394618b40e030002b8fff0404a70
469046a046d04604df0e01001002460e00000e4602043122b40f0401ef04ff0402044a40b4003101003110312031e031f03105083142fc2cfd3dfc360727fc13fd1dfc091903060118003f3f3fe1f4e1
3fe1f4e1012f5e5d71e110de5d71e11117392f2f2f2f385d5d38113310e11133113331300101230101140e0223222e0235343e0233321e0205141e0233323e0235342e0223220e0201222e0235343e02
33321617072626232206151433323637150606050afcd59d032b016e2d5070443f6e512f2c5071443e6e522ffe3311263c2b2a3c251111253c2a2b3c2611fdcb45795a34355c7d48336420211f552267
5fc2335a23236305b6fa4a05b6fb98537f572d2d577f53537f572d2d577f5333563e23233e563334553d21213d5501922652805a5f845226160f6b0d147476e511106b12130000020077ffec037b05cb
002d003a005540322e70234010144823231105063c35286e191410112011301180119011051118153676141128280030761e0700750b06060b19003f332f10e13fe112392f3333e13232012f5dc533e1
3210d6c112392f2be1313025323e0237330e0323222e0235350606073536363711343e0233321e0215140e020711141e02133423220e0215113e03026f20392e1e036404264669463a6a51302e613134
5f2d1c40684b36573d21355c7c47112437636620291709364e3318771632523b53835a3025548863e7111c0c790e1e0f01ee3b6c53312a4f6f4563a6866826fed330513b210421bc1b32452afe6a214e
6279000400c70000078905b60017002b0037003b00ba40720e01155a0039392ce122100001b000019f00013000400002002200220a32e1180f3a1f3a2f3a033a3aef180118673d6f3dbf3dcf3d03403d
010c0803095a0a643c2fe51d35e527cf1ddf1def1d031d40060b480f271f275f276f277f27ef2706061d271d27160310061848030b0339e5380eb8fff0b60618480e010a12003f33332b33e03f332b33
39392f2f5e5d2b5d10e110e10110f6e13232325d5d10e65d322f7110e11139392f2f5d5d5d7110e1332f10e13232313021230123161716161511231133013326272e0335113301140e0223222e023534
3e0233321e020514163332363534262322060335211504a8cdfd870806040405a6cf027606030401030201a402e1294d6d443f6b4d2c294d6d443e6b4e2cfe4248515147475151486301f004ba4d4c41
8e39fce705b6fb4c4c4a2043433e1a0320fcb95382592f2f5982535381582e2e58815371737371726d6dfd1f93930002002502e5055005b60007002000a3405d00c401130a10c411c006d006e006036f
0601300640065006031f06010406012011d0110201061111060103031e191518c49f17af170217220f222f223f2203224015184822400f1248cf03ef0302031f0909200c1848090703c8041814b8ffe0
400c0c1848141108010115120403003f3333332f3333332b3310e132332b1133012f5d2b2b5d10d65de13232321117392f2f2f5d5d5d5d5d5d10e1323210e1313001231123352115230103231e031511
231133131333112311343e0237230301687fc4020ac70240ba07010201017bbab4bfb27f0101010106c302e502656c6cfd9b022510221e1806fe4902d1fdd90227fd2f01ac091e23230cfddbffff004e
000005a605cd02060176000000020066ffdd048b04480022002f003b4021234a1e11312f134a100501051d1d18124e2b2f3b2f022f2f18294e0c0f184e0016003fe13fe112392f5de111392f012f5de1
3210de32e1313005222e0235343e0433321e021521111e0333323e0237170e0313112e0323220e020711027982c686452c4c6676803f71c28e51fcc516404d582e4a745e4d224824536e8fcc133b4d5d
353357493c18235e9dcc6e63a07e5c3c1d4f93d183fe9c182c2215203c57372939654d2c028b0115142a231613212a17fee9ffff0053ffec05d305b60026007b140000270217025e0000010702400362
fdb30030401f040302271901f41101e41101d01101b01101a01101301101112f59010f59015d5d115d5d5d5d5d5d35003f353535ffff0033ffec05d305c900260075140000270217028b000001070240
0362fdb3003c4028040302491901f03301d03301c43301b43301a03301943301603301303301243301332f7b010f7b015d5d115d5d5d5d5d5d5d5d5d35003f353535ffff004fffec05d305b60026023d
140000270217028b0000010702400362fdb3003c40280403023d1901f02701d02701c42701b42701a02701942701602701302701242701272f6f010f6f015d5d115d5d5d5d5d5d5d5d5d35003f353535
ffff0071ffec05d305b60026023f42000027021702390000010702400362fdb3003440220403021f1901f40901eb0901cb0901b409019b09017f09012b0901092f51010f51015d5d115d5d5d5d5d5d5d
35003f35353500020062ffec043b05c5002b0043003b40213319470057454045010f4501063f48210c5644384f13132c211c5027042c500716003fe13fe13312392fe10110f632e15e5d5d10f6e13231
3001140e0423222e0235343e0433321617363435342623220e0207353e0333321e0201323e04372e0323220e0415141e02043b2041658ab16d6a8c53221732517399625b932d028b891f4443411b1843
4b4f257da6632afd9e355e514335250a0a293b492a3e664f3a2512152d4603a66ae1d4bc8d52426e8f4c3c8d8d83653d4f45132a0bbec90c161f13ae0c17110a5996c4fc732c4d687a85432845331e30
51686f6e2f3256402400000200290000046205b60005000e0058401102010909050e5b60047004b004f0040404b8ffc04027060a480404102f105f106f107f109f10bf100610400609480d5b050d5f05
1209200a1148090103003f332b3fe1012fe12b5d11332f2b5de112393d2f333331303701330115210126262706060703212901bebb01c0fbc70277202d0f112a1dfa02a6710545fab96f039a61a84b4b
a85bfd04000100c7fe14052705b6000700374011005a076509000930094009700980090509b8ffc0400f141848035a046408025f050304001b003f323fe10110f6e12b5d10f6e1313001112111231121
11046dfd14ba0460fe1406fcf90407a2f85e0001004afe1404e305b6000b005c400b085b70060102060206000ab8ffc0b7090e480a0a0d0709b8ffe040090f1848090300000c0db8ffc0400f13184808
020209075f0403095f001b003fe13fe112393d2f33012b1133182f33332b3211332f2b1239392f2f5de131301335010135211521010121154a0270fda00448fcbc023afdb0039bfe14730392032b72a4
fd09fc9da4000001006602870402031d00030032401f02059600018b00017900015600014b00013800011400010900010000ad01b3003fe1012f5d5d5d5d5d5d5d5d10ce31301335211566039c028796
960000010025fff204c506a00008002c40130100060502060206070810080303ae04040700002f2f392fe1012f2f38333939192f182f331133333130052301233521130133027385feebb40129e50200
920e030a8ffd6905ac000003007701910531040e0023003300430076404f491701480d01f045012f455f45023a274a276a27032a3701371a0827043f772f972f022faa201230120212783f013fab0034
2aae173a24ae0837271a041705000d100d02070d0d1f9f170120170117002f5d5d33332f5e5d33121739e13210e132012fe15d2f5de15d1217395d5d5d5d31305d5d01140e02232226270e0323222e02
35343e02333216173e0333321e0205323637262623220e0215141e0201220607161633323e0235342e0205312b4d6d425d9b411d464e532b416e4f2d2b4e6f43559e3e1d444f5730426d4d2bfc7b3f6c
34316b4528402c18152c41027c3f6b37336c4427402d18192e4002cd3f7257346973304f381e2c527548417356316b70304e381f2d5275f957615e5a1d3344262442321e016a57615d5c1d3344272643
311c00010010fe14030606140023003740231025d02502140401060401040dac1b150109150115101e201e021e19ae121c07ae0000003fe13fe1012f5d335d5de1325d5d5d3130013216171526262322
0e021511140e022322262735161633323e023511343e020283224b16123d243342271032597c4a244b17143e2333452a122f5779061409099309112741542dfad75e8656290b08930810254054300527
5e86562800020066017b0402042500230047004b402e411d491049012e100a200a020a4029ad31af4317ad0a1faf1c05addf0def0dff0d030d400f12480d0d3bad2e43b3003f33e1332f2b5de133f532
e110f5e133012f5d335d10ce323130012e0323220e0207353633321e02171e0333323e0237150623222e02032e0323220e0207353633321e02171e0333323e0237150623222e02021225372d29161c3c
3b381964941d3237432f25372f28161c3c3b381863951d3237432f25372d29161c3c3b381964941d3237432f25372f28161c3c3b381861971d32374301bc10160d0513212c19a26c050d191410160d05
13212c19a26c050d1901ae10150d0513202c1aa26d050e191410150d0513202d19a26c050d190001006600a404020504001300a640110a0d0e1112091212000304070813080913b8fff0404f09101309
130906100b1540150101c60601bb0601a906018606017b0601680601420601390601060908080d04ad071312121100ad0e1f032f03027f030100031003020603030af007010f076f070207002f5d5d33
332f5e5d5d7133e13232113310e132331133012f5d5d5d5d5d5d5d5d335d10ce321139392f2f383811337d87c4c4c4c401331087c4c4c4c431300123352113213521131707331521032115210327015e
f8013e79fe4901fc858a69fafec17b01bafe00818901ba96010495011b3be095fefc96feea39000200660000040204dd0006000a0052403209000c400c0108400101010201050503066f007f00023000
01000004200301500370038003d003f003053f03010003010603002f5e5d5d5d7133332f5d5d3212393d2f333301182f5d335d10ce323130250135011509023521150402fc64039cfd2102dffc64039c
ee01a86601e1a0fe94febefe71969600000200660000040204dd0006000a0052403209050c400c0108400601060504010103006f067f0602300601060602200301500370038003d003f003053f030100
03010603002f5e5d5d5d7133332f5d5d3312393d2f333301182f5d335d10ce32313013010135011501153521156602e2fd1e039cfc64039c018f0142016aa2fe1f66fe58ee9696000002006d0000043f
05c300050009005d403602010905040709070306ac000bff0b01400b500b800bb00bc00b050f0b2f0b0208aa1003010303080006080608060207ad040709ad02002fe13fe11239393d2f2f1133113301
182f5de15d5d5d10dee112393911333311333331300101230101330903043ffe3c4cfe3e01c24c010cfecffecf013102e1fd1f02df02e4fd1e0200fe00fdfe00ffff001d00000417061f002600490000
0107004c02a20000002440180201af1d01501d011f1d010f1d011daf2f01402f010f2f015d5d5d115d5d5d5d3535ffff001d00000406061f0026004900000107004f02a200000022401701af1d01501d
011f1d010f1d011daf21014021010f21015d5d5d115d5d5d5d35000100cf04d903cd06170015003f40290017201702af17cf170230177017020f00010000000a300a02060a0a7f000100400609480000
108e05002fe1332f2b5d33012f5e5d332f5d5d5d713130010e0323222e0227331e0333323e023703cd06315d8d64678c582905aa051b324c352c4936220606174e7651292750775039492910132b4835
0001ffbcfe140164044a00130023401310152015020f470c03030c54140d0f0750001b003fe13f0110e6322f10e15d31301322262735161633323e0235113311140e0242303f171a36231b2e2313b622
486dfe140e0b940a0b0f27413304f4fb184d7b572f000001018704cd02790614000d0019400c05853f0601060c000c920500003fe5012f33dd5de13130013e033733150e03072301870912110e04b406
1f292e166004e71e4d51502114194e5656200001016ffe3b0275ff83000d001640090c00000585060c9205002fe5012fe1322f333130013e033733150e030723016f0b1a181304b2061f2c361d62fe56
194c54532114194d575621000001017d04d902830621000d0017400a0c000005850605920c00003fe5012fe1322f333130010e030723353e03373302830c1a181204b2051f2d371c620606194c545321
15184e565621000200250239027f05c7000b001d002a401906e10c1f4f1f7f1f021f40060a4800e11609e51bde03e511df003fe13fe1012fe12b5d10dee1313013141633323635342623220605140e02
23222e0235343e02333216b847514e4e4e4e514701c7234a734f4a704b262349714e959a0402a5a19fa7a59f9fa56caa753e3e75aa6c6ca9743cec000002000c024a028f05bc000a00150046402a0902
e10b070303175f178f17021740060a4815e1050104e5090f0b1f0b2f0b03080b0b020fe507dc02dd003f3fe112392f5e5d33e132012fe12b5d12392f3333e13231300123152335213501331133213534
36370e030707028f7d8ffe8901798d7dfef4030305141618099b030ac0c06f0243fdcdc32a63310b252a280ff0000001003b0237026605b60024004a40172122221e1d1d0b2014e105264f267f260226
40060a480bb8ffc040111218480b17e500001121e51edc11e508df003fe13fe112392fe1012f2b2b5d10dee13312392f33331133313001321e0215140623222627351e0333323635342623220e020727
1321152107363601423d6b4f2da6a13f792c1a3c3d3b18555f5f590d2527250e432101bafebe121439046d234465418c9d1c1a8d121b130a4c584d55040607022b01a87bd7030600000200250239027f
05d5001d002d00384020100a21e1002f4f2f7f2f022f40060a481529e10a24e4151919100fde1ee505df003fe13f33392f33e1012fe1322b5d10dee11239313001140e0223222e0235343e0237170e03
0733363633321e0205323635342623220e0215141e02027f2a4e6f44406e522f457eb26e2d4f7f60400f0b1b634a365a4024fedb44544d47273f2d19192d3d0366416f502d2d5880536eb38f6d276d1b
404f603b2b31254869f75c56525a1929371e2c49351d0001002f024a026405b600060038b90000fff0401f00000201e105084f087f08020840060a48d002e002f002030202e503dc00dd003f3fe1012f
5d2b5d10dee111392f383130130121352115018d0140fe620235febf024a02f17b64fcf8000300310239027105c70025003400440077402238e12142e1050a3d321c040521052105172ce10f464f467f
46024640060a4826e117b8ffc04027151848170a1c3d324b325b326b329b32ab3205321435b63dc63dd63d033d0029e514df35e500de003fe13fe111395d1112395d11123939012f2be12b5d10dee112
39392f2f12173910e110e1313001321e0215140e02071e0315140e0223222635343e02372e0335343e0203141633323635342e022727060613220615141e02173e033534260152345e462a1627341e22
3e301c2a4d693f8b96192a381f1b2e23132b485f624a484a4b162737210f414294383f12202d1a182c20134105c71b375438253e322811132d37452a395d422483732a45382b11142b353e253853371c
fd683b46463b1e3027200f06224d01e837361b2a241d0d0c1c232c1c3637000200210239027b05c900250035003b40223111e0091c00374f377f37023740060a4829e11c2ce41117170526e521de0ce5
05df003fe13fe112392f33e1012fe12b5d10de1139e132313001140e022322262735161633323e0237230e0323222e0235343e0233321e0225220615141633323e0235342e02027b215aa28119401515
3619536c401d03090d24303d263c604424294b6c4341725430fecb44564a4c263f2d18152a3e044260bb935b08077d0d0a3557723d12241b1125476641426f512d2f6193ac5e584c581929331a24483b
240000160054fe8107c105ee0005000b00110017001b001f00230027002b002f00330037003b003f004300470053005f006f00780081009000e44059342c201c381857517a706f74676464827e6b7051
c051d05103516f6b6b6f51035d894f8c5f8c028c8c44282409175d504b604b024b4b413d31000c2a42463e2632868f8f6f796770707a607a6f544848706f806f02616f016fb8ffc04025070c486f6f60
5a4e4e8a781f602f603f606f60046060012c1c18120c393521070f01010701002f5e5d333333332f3333333312392f5d3333332f3311332f2b5d5d332f3311331112392f333311332f332f332f332f33
012f33333333332f5d332f33333333332f5d331217392f2f2f5d113333332f333311333311332f332f332f33313013112115231525352111233501113315331521353335331121352115213521150135
2115012311331123113301352115012311330135211533352115012311333523113301231133012226353436373216151406273236353426232206151416013332161514060715161615140623231333
3236353426232315153332363534262305351616333236351133111406232254012fc005ce01306df9006fc0050ec36dfd490111fbe1010efef2010e04b76d6d6d6dfbc20110fc306f6f02c001107701
11faa86f6f6f6f06fe6d6dfa997f87877f7f87877f483f3f48454242019fac6d702d2d38336d5ecf7b422e2429303b4a31252634010e10201425317d685f3d04be01306fc1c16ffed0c1f902012fc26d
6dc2fed16d6d6d6d06fe6f6ffaa8010e0202010ffa3b6d6d01a6010e044a6f6f6f6ffc2f011079010ffd680110fe8a9f8e919b019c918e9f68675e5e66665e5e6701ea4353314408040b443a51590162
2220221de39a2b25202afc66030524320192fe725e6400030054fec107aa061400030023002f0050402804232324202a302a402a032a2a4f24012424021c0b0b021515022323302d012d2d2727031910
100300192f33182f3311332f332f5d332f01192f33182f11332f3311332f5d332f5d11332f33313009030535343637363635342e0223220e020717363633321615140607060615150314163332363534
2623220603fe03acfc54fc5603eb214c634d315b83512b5a57522252447e383f3e2752454a1b47464447474446470614fc56fc5703a9fb2f2c3e3a4c8359456b4a27101b2314b2222e3a2f3144413579
503bfeed3e49493e40494900ffffffbcfe14025506210226023700000007014cfebb0000ffff001703c1015005b60206020700000002000affec04b2062b000c004f007540474d0d43103e003b084743
431a3b474f001001101051a051013f51013347001a101a201a031a2250293e4f4f4e004e002910293029402950290506294e294e380550480138501516003fe13fe11239392f2f5e5d113310e13210e1
012f5de15d5d11332f5d33e111392fe1113939111239393130012e0323220615141e02051616151402060623222e0235343e023534262322060727363633321e0215140e0215141e0233321211342627
2e0335343e0233321e02173315036a0d394d5b304c562e6aab013c02024081c4836f9256220a0b0a1f1f1836102525643632452c130a0b0a15304e3aa4a40202aef2964327517f5968a1754b128f03d1
6caa743d5848386952338a163a1f9ffeeec973375f7d45285d594b182d210f0a7f111a1c32442823565d612d2a4a361f01320130173e14024b789a514370522e549cdf8b8900000100000000044805c3
0022008f401baf24ef24022440090c4822ab21019821014021011b21010f210121b8fff040442121001f1b0d010f0d010d201216489f0daf0d027d0d016b0d015a0d014f0d012b0d3b0d020d0d1e5a77
1f871f971f034f1f01001f101f02071f1d001f21031f12110a04003fc13f3f123939012f5e5d5d5de1322f5d5d5d5d5d5d2b71711139332f385d5d5d5d5d332b5d3130013e03373e0333321617152626
23220e02070e05071123110133021d193d3f3c18162f354029232b110d200d0b1c2023130d283236363213bbfe42cb02db4ba8a0892c283d2714090891030507172a2318556e8085853cfde3022f0387
00020012ffec0660044a001c003c008f404719672f012f47d52c01b72c01a92c019a2c01032c2c033748ab12bb120212123e143e01543e643e843ea43eb43e05403e0102303e010f3e01224800031003
300340035003050603b8ffc04015121548031a002d2d271d0e08500b0f322750160016003f32e1323fe1323212392f1239012f2b5e5de15d5d5f5d5d7111332f5de111392f5f5d5d5d5de15d39313005
222635343e02372135372115231e0315140623222627230606030e0315141e0233323e0235353315141633323e0235342e02270229b6c312202b1afeeb8605c8f318261b0fc4b56a8b1f0b1f8bba1628
1f121d364b2d2e442c16b362522d4b361d0f19241514eaf3397d7e7b384a509a387b7e7d39f3ea575b5b5703c43676797a3a62834e2129486239b0b0828a214e83623a7a79763600ffff00c70000062f
077502260030000001070076017901540013400b01270526014b1a20090f25012b35002b3500ffff00ae0000068706210226005000000107007601c700000013400b013a1126017a2d33162b25012b35
002b3500ffff0000fdd504dd05bc0226002400000107025b011b0000000db70302011f15040725012b353500ffff005efdd5039c045e0226004400000107025b00bc0000000db70302143d330c222501
2b353500fffffed3ffec05c305cd0026003252000107025cfe400000003fb603022d03030235b8ffc0b2090f48b8ff21401a35350a0a250100b000018000017f00015000014000011f000100115d5d5d
5d5d5d35352b2b3535003f35350000020073fdd50237ff830013001f003a402814830f003f004f005f0004001a83300a010a1d8c00051005300540055005a005f005070605178c0f002fe1d45e5de101
2f5de1d45de1313001140e0223222e0235343e0233321e020734262322061514163332360237233d543132523b20203b523230543e23744031323f39383140feae3351381d1d384f33334f381d1d374f
34353c3c35353c3c00020093046802d905c7000d001f0042400915300e400e020e111ab8ffc0401c0910481a1a2b0d01040d140d020d06050c050c3f114f115f1103111d002fcc5d39392f2f012fcd5d
5d322f2b33cd5d393130013e033733150e0307232534363715060615141e0215140623222601b00b19181608cf1230383e1f52fee3787a3c391f251f322f3a4504891e4b4f512414204d505125784e73
1f4c162e181312101a1c25274600ffff001d000006b9061f0026004900000027004902a200000107004c054400000038402603028f39017039014039012f39010f39013901e01f01b01f011faf4b0170
4b01404b010f4b015d5d5d5d115d5d35115d5d5d5d5d3535ffff001d000006a8061f0026004900000027004902a200000107004f0544000000364025028f39017039014039012f39010f39013901e01f
01b01f011faf3d01703d01403d010f3d015d5d5d5d115d5d35115d5d5d5d5d350002007dffec0625061400200034004b402c1b061309205b01012b5b096736c03601bf36017036012f365f3602215b13
6635061b0e305f201804265f0e13003fe13fcee11239390110f6e15d5d5d5d10f6e1332fe111123939313001170e03071616151402060623222626023534123636333216173e033501141e0233323e02
35342e0223220e0206170e0c283f5a3f2b2d51a0ed9ba3ef9d4c4c9eefa49eec5129311908fbf2346ba57272a56b32326aa47272a66c340614163c70614c1859d87faafeebc66c6cc60117aaaa0115c4
6b6f660c354a5d34fcc989db99515199db898ada97515197da0000020071ffec04f404f20020002c004a402c174a19191e120a274800001000020700002e102e902ea02eb02e0421480a562d121e052a
50170f1024500516003fe13fc6e11239390110f6e15d11332f5e5de1123939332fe1313001140e0223222e0235343e02333216173e033533170e03071616051416333236353426232206042d437db26f
67ae7f47437cb36f64a93f2e341906c60f102d3f56392024fd00899a9a87899a9a87022789d5914c4c91d58988d3914b47440d374b5d33174a785e4314419e5ed1d3d3d1d0d0d000000100b8ffec064e
0614002400514032065b0808235a019f0e01b00e01af0e010e0e2670268026f026035f260100261026021b5a186425010d0d240619031e5f1313003fe13fce33332f330110f6e15d5d5d11332f5d5d71
33e1332fe1313001153e033533170e030711140e0223222e0235113311141633323e02371104dd343e200ac60f0f3255805b4285c98880c48544bbacb0598052280105b6c406324e6438164782694a0f
fd9172c490524d8ec77a03aefc48b0bf3662885103b6000100a4ffec057904f20027005440331f4a21210117471a2727291029018029a029e029036f290100292029302903070f470c54281a26261f18
0d0f12500207160015003f3f33e13f33c6332f330110f6e15e5d5d5d7111332f33e132332fe131302127230e0323222e0235113311141633323e02351133153e033533170e03071103751b0a1945525c
305b8a5c2fb66a6f516e431db6343e200ac60f0f3255805b932b3f29142e62986902cdfd3d828234659460023a7b06324e65381747886d4807fcb0000001fc8f04d9fe190621000d0016400a00060880
0f005f000200002f5d1acc012fcd313001232e032735331e0317fe197923514d4010db102b2e301604d91c5358511b152350514c1d000001fd4804d9fed10621000d0016400a060005800f0c5f0c020c
002f5d1acd012fcd3130013e033733150e030723fd4815302e2b10db103f4d52227904f41d4c515023151b5158531c000001fc4604d9ff1405e3001b003840230f172f1702170009200902070916058f
0e401013480e40070b480e0e138f090f000100002f5d32e1332f2b2be133012f5e5dcc5d313001222e0223220607233e0333321e0233323637330e03fe42284f4c471f2d300e680521354a2e29514d45
1d2d2e0f680521354904db232b23353e3c614526232a23343e3c614526000001fd0404b8fe79068f001b002640120508080d0015050808120f191f1902198006002f1acc5d32391133012fcc32391133
313001140e02070723273e0335342e0223220607353636333216fe791b2d3c220a710e253c2a171524321d1c3410103229858505d3273a291b076fb006101821181820130703036c03055b000001fd31
fe98fe06ff7d000d000fb50887000b9103002fe5012fe1313005343633321615140e02232226fd313e2d2b3f111d27152d3ef63c37373c1d2b1c0e37ffff00c7000003be077302260028000001070043
ffb701520015b4010c052601b8ffc2b4120c010025012b35002b3500ffff00c9000005100773022601b2000001070043004201520015b40118052601b8ffa3b41e18000c25012b35002b3500ffff0071
ffec03e106210226004800000106004394000015b40228112602b8ffb9b42e28050f25012b35002b3500ffff00ae0000043b0621022601d2000001060043ca000015b4010c112601b8ffa3b4120c0a04
25012b35002b35000001007dffec075e05c90045007140481404395a25c536019636a6360289360136361d415b0c674700471047020047204750477047d047e04706072e5b1d664637373300295f0326
260722041420090e48143c335f111813003f33e132322b3f33331133e13212392f0110f6e15e5d7110f6e111392f5d5d5d33e13239313001220607273636333216161215140206062322262723060623
2226260235341236363332161707262623220e0215141e0233323637113311161633323e0235342e0205683c5e2a4c3b8f5177b77d403f81c6886faa49024ba86e88c6813f407db777518f3b4c295f3c
4571502b2e5c8a5c39762ebb2e763b5c8b5c2e2c507105252e209c2a2c63b8fef8a6aafedfd2773230303277d20121aaa60108b8632c2a9c202e468acf888de5a459242601bdfe43262459a4e58d88cf
8a4600010000000005f4044a002c00e74009651001c922f9220221b8fff0b30a10482cb8fff0b3121b482cb8fff040190a0f4800100a1b48260d220e2122152c0006560f660f020f0eb8fff040481f0e
016f0e7f0e9f0edf0e04590e014f0e017415016b1501241534150224063406440674069406050e0615150e0603011b47001c101c02071c1c2e102e202e302ed02ee02e050201b8fff0401e012640151d
4826200c1448260d10031b0e010f1506260636060206220015003f32325d11333f333317392b2b012f38335d11332f5e5de11217393d2f2f182f5d5d5d5d5d5d5d7138335d113333113333111239392b
2b2b2b5d31305d21013313161617333e0337130333131e0317333e033533140202060723032626350e0307030177fe89bce01c26090707151a1d0f77a2bcc90c1f1e1805075a724119b7255c9a75c077
0c0d0106090a0498044afd56579d33163a414520010c01cffdb02163675c1967d0e2ff98a3fedffef5fc7f015420350301131b1d0afeaa0000020014000004bc061400160021009b400a1300175a0c15
1506100cb8ffc0400d1a2748200c0102900ca00c020cb8ffc0b31318480cb8ffc040400b0e480c0c0e1b5b0667230023302350236023802305e023019f23015023010e215f00160d5f101310b0000189
00014f00010f10010700100010110017600c12003fe13f39392f2f5e5d5d5d5d113310e13210e1012f5d5d5d7110f6e111392f2b2b5d5f712b3312392f10e1323231300133321e0215140e0223211121
35211133112115211133323635342e022323020a9698cf7e373c7ec286fe96fec4013cba0179fe87a0aea42e5b8b5d81034e42719857619e703d0460a40110fef0a4fc40848846613c1b000200140000
046d052700140021008140091302164710000c010cb8ffc040470d1a480c000c000e1c47065723002310230270239023c023e023040e155002010d501013100f021f020202400c1e480f10010f101f10
0207021002107f11013011011116500c15003fe12f5d5d39392f2f5e5d712b5d113310e13210e1012f5d7110f6e11239392f2f2b5d33e13232313001211121321615140e022321112335333533152101
1121323e0235342e022302e3fed50115d3cd2f659f71fe39eeeeb6012bfed50104375e43262141604003b0fed79c9b4d7c582f03b09addddfda4fea81129423031422811000100c7ffec06ec05cb002d
0058402f08055b1b220711110722031e2a2a2f211d5a1e642e2b005f27041b081c5f22050f21010821211f031e120d5f101613003f33e13f3f392f5e5d3333e132323fe1330110f6e13211337d2f1217
39182f2f2f33e132313001220e02072115211e0333323637150e03232226260227211123113311213e0333321617072626056d5fa0784d0d0274fd86054075ac7159a04e274e56613a9eeb9d5205feb0
baba01560e63a7e9956cc44f4e3f950527417aaf6fa282c98a482317a20f170e0764b801039ffd5605b6fd988feba75c2c2a9c202e00000100aeffec0571045e002900644039201d48054f0c010c1f0c
1f082714142b0b074708542a1a51151110200506501d0ca90bb90b020f0b1f0b2f0b03060b0b090f08152351260016003f32e13f3f392f5e5d5d3333e132323f33e10110f6e13211337d2f3312393918
2f2f5d33e132313005222e0227211123113311213e0333321617072e0323220607211521161633323637150606045461a9814f07fef1b6b601130c5581a55e4e95323617383c3b198c910e01dbfe2309
928a528236357c143a7bc187fe17044afe3980b5723422199a0a130f0997a69ab8a42519a21d1e00000200000000052f05b6000b001800ae4049080601d806e80602070701d707e70702a607019b0101
7700a700020d0a090c03040706131308a70001000101050900080100081008200870088008c00806081008081a901aa01a021ab8ffc0400f1518488f1a01401a010f1a01080405b8fff04013050b035f
13100a0e48130c0c06040009120603003f3f333312392f392be132012f38335e5d5d5d2b5d11332f385d713312392f335d11393d2f33331239391239395d5d5d5d715d71313021231123012301330123
01232721272e03270e030702eca67ffefec50239bb023bc7fefe7ae8012335121c161107091115191102aafd5605b6fa4a02aaa48f304c434125254143492d000002000a0000045a044a000b001700cf
40383902590279028902041b022b0202b505019605a60502870501090319030206081608020912191202061116110207120302110809b6000100b8fff8401b0a0f48b90b010b080a0f48000bb40c010c
0c050506060a01100201b8ffc0b312154801b8ffc04018070b48010119c019e01902af19015019010f191f1902090ab8fff0400f0a0408500c0b1111010b0f0a060115003f33333f12392f1239e13201
2f38335d5d5d5d11332f2b2b333812392f3312393d2f5d33332b5d2b5d1239391239395e5d5d5d5d5d5d5d31305d5d010123032311231123032301170e0307332e032702a601b4bcbb60a260babd01b4
71051b23250df00e25231b04044afbb601e9fe1701e9fe17044a791955605e22225f60541900000200c70000072905b60013002000bf401b9b01017700a700020d060e0515121114031b04040500010f
0e1b05b8fff0405d800101cb1b01841b012b1b7b1b020f051f052f057f05cf05df05ff05070805011b1b010503111010001010107010b010f0100510102230225022c022d022e022050c085a09642113
06035f0d1b200910481b14140e09040111120a0e03003f333f33333312392f392b33e132320110f6e1325d11332f5d383317393d2f182f2f5e5d5d5d5d5d381133331133113311123939113939111239
395d5d313021231123012301211123113311211333012301232721272e03270e030704e7a581fefdc40108fe7fbaba01c1f1bb023bc7fefe79e9012335131c151108081115191102aafd5602aafd5605
b6fd980268fa4a02aaa48f304c434125254143492d00000200ae00000608044a0013001f00eb4031a505018605960502770501060916090219022902490269027902050b02011a030219081409120b13
0a110d470ec6000100b8fff840170a0f48c9130113080a0f480013c414011414050506090ab8fff040132f0a3f0aaf0a030a060a06010e542002011001b8ffc0b312154801b8ffc0402c070b48010121
d02101bf2101602170219021034f2101002101080b0408501214131919020f130f0e09060215003f3333333f3312392f123933e13232015e5d5d5d5d5d11332f2b2b383310e61139392f2f5d38331133
12393d2f5d33332b5d2b5d10e13211123939111239391139395d5d5d5d5d5d31300101230323112311230323132111231133112113170e0307332e0327045401b4bcbb60a260babdc3fee9b6b60154b5
70051b23250df00e25231b04044afbb601e9fe1701e9fe1701e9fe17044afe3901c7791954605f22225f60541900000200140000056a05b60023002600c04011862201861a0159128912022510090c48
26b8fff0402c090c48250a0b0624241a5a1b2607000a100a200a030a1b07071b0a0300121011201102d01101801190110211b8ffc0403b070a481111281f2801f02801cf28df2802402850287028030f
283f2802072300191c61240b4f06010f06af0602080606000a07265f08031b120012003f32323fe1323212392f5e5d713333e132012f335e5d5d5d5d7111332f2b5d5d71331217392f2f2f5d113310e1
393d2f333311332b2b5d5d31305d33133e033701352115011e03171323032e03232311231123220e020703010121147b1836547c5ffe900449fe8561805637187abe7b1426364f3d18bb183d4f362614
7b01eb014afd6f01c55588623a0601e78b8bfe190638628956fe3b01c1485f3917fd4802b817395f48fe3f035a01b8000002000a000004e5044a0023002600c7400d761a861a961a032510070c4826b8
fff04018070c480912010623010b0624241a461b250a2607a71b010ab8ffc040101619480a1b07071b0a03001220110111b8ffc0b312154811b8ffc0403f070b48111128102801cf28df280260289028
021f284f280223000a07265008191c52162426243624a62404240b0f061f064f065f0604070606080f1b120015003f32323f392f5e5d33335de13210e13232012f335d5d5d7111332f2b2b7133121739
2f2f2f2b5d1133113310e1393d2f33335d5d2b2b5d313033133e033701352115011e03171323032e03232311231123220e0207030113210a7b162c426753fece03cffec95368432c177bba7b14243044
3504a60435463025147a01b4fcfe0a01543d654d310901646969fe9c08314c673dfeac015036482b11fdf6020a112b4836feb00283012d00000200c70000078905b60028002b00ca4015861f01591789
1702100b29291f5a202a10090c482bb8fff04033090c482a0f2b0c000f100f200f03070f0c20200c0f031628b000c000020f001f000200000717001610160270168016c0160316b8ffc04034070a4816
162de02d019f2dbf2d02402d012f2d010a065a07642c0f0c2b5f0d1e05216129100f0b01080b0b080d03071720030012003f17323f33392f5e5d3333e1323210e132320110f6e1325d5d5d5d11332f2b
5d713312392f5d5d331217392f2f2f5e5d113311332b2b10e1393d2f33335d5d313021133636372111231133112101352115011e03171323032e03232311231123220e02070301012102337b13291cfe
7bbaba02a4fe95044afe8561805537187bbe7b1426364f3d19ba193c4f3626147b01eb014afd6f01c544732efd5605b6fd9801dd8b8bfe190638628956fe3b01c1485f3917fd4802b817395f48fe3f03
5a01b800000200ae00000698044a0028002b00cf400d761f861f961f032a10070c482bb8fff0402c070c48091701100b29291f46202a0f2b0ca720010f200c0c200f03161f002f0002ff000100000717
20160116b8ffc0b312154816b8ffc0403f070b4816162dcf2d01b02d019f2d01602d014f2d01002d302d020a064707542c0f0c2b500d1e052152a6290129100f0b1f0b02070b0b080d0f071720030015
003f17323f33392f5e5d33335de1323210e132320110f6e1325d5d5d5d5d5d11332f2b2b713312392f5d711217392f2f2f5d1133113310e1393d2f33335d2b2b5d313021133636372111231133112101
352115011e03171323032e03232311231123220e02070301132101bc7b101d13feedb6b6020bfed503cefec95368432d177bbb7b142430443504a60435453124147b01b4fcfe0a01542a4b20fe17044a
fe39015e6969fe9c08314c673dfeac015036482b11fdf6020a112b4836feb00283012d0000010039fe4603ec06d50076009f40272118090e48596259514d4d47513b5b676c5167443567145114675135
4405242e5b726778055a24b8ffc04036141848245d524d8f569f56af56035640070b485656476c34604f35010f35af3502083535294351620340614703150a5f1ffb29600013003fe13fe1333fe11733
12392f5e5d71e13911332f2b5d33e1012f2be110f6e11217393d2f182f2f2f2f11123910e11133332f1133332f3130002b05220e0215141e0233323e0233321e0217152e0323220e0223222e0235343e
0233323e0235342e0223233533323e0235342e0223220607273636372e032735331616173e033332161715262623220e02071e0315140e0207151e0315140e0201e1475a34140e243d2f41615351322b
4231230c081f364d3531474a5c4668884f2031669d6c5980512639679158bfb05c8e613225445f3a6ea94b5c3ba6671837363214793361361a3b434b2a1f2f0e0b22111630302e14527e552b335b7d4b
578a5e324383c4141220291816241a0e040404070a0f07b60712100c030302294860373c6246262648694447653f1d972748643e36533a1e43367d304b111e413f3a171b226138244638230905770507
172938210d3f5c76454a7758390c060b3959774860a0744000010017fe7303520552007000b740714018090e486868626c030c036c584611166c11e05f015f5211336c33116c525f05434d471c5772c0
72d072e072037f72013072010f720107264643400c1148431616515089529952020f5201085252624868075200401014480040070c48005e6c00035b500c6210342b503efb48502116003fe13fe1333f
33e117332f2b2be1331112392f5e5d5de1393d2f01182f2be15e5d5d5d5d10f6e11217393d2f182f2f2f2f5d11123910e11133332f1133332f3130002b0132161715262623220e02071e0315140e0207
151e0315140e0223220e0215141e0233323e0233321617152e0323220e0223222e0235343e0233323e0235342623233533323e0235342623220607273636372e032735331616173e0302cf1f2f0e0b21
11152e2e2d1337593f231c33462b2c523f26356ca6714b592f0f0e223a2c32635d532142470d0922282c132055646f39587040182256906e37644c2c858a81613d644827736c47814b3f396b41173130
2c12793362361a3a434b055209057705071526331f0e31455835324d3b290e0b0e2c425b3d447456310e1a26171623180d0304031613970a0f0a060303032e4756292f59462a132b45335e5b99122740
2d4d4d2622931c22081c3b3732151b22613824463823ffff0068000005ba05b6020601750000ffff00a4fe14057106120206019500000003007dffec057105cd0013001e0029004f403225195b00672b
d02b01cf2b01802b013f2b6f2b02241a5b0a662a1a604f24010f24af24df2403082424141f5f0f04145f0513003fe13fe112392f5e5d71e10110f6e1325d5d5d5d10f6e1323130011402060623222626
023534123636333216161201323e0237211e0313220e0207212e03057151a0ed9ba3ef9d4c4c9eefa49aeca051fd876a9e6b3906fc9a063a6b9e6d699c6c3d080364083b699b02ddaafeebc66c6cc601
17aaaa0115c46b6bc5feebfd074786c27a7ac28647049a4380b87575b880430000030071ffec042d045e0013001a0021004f40331f17480057234023d023e023030f23011e18480a56221850ef1e01a9
1eb91e020f1e1f1e2f1e03061e1e141b500f1014500516003fe13fe112392f5e5d5d5de10110f6e1325d5d10f6e132313001140e0223222e0235343e0233321e02013236372116161322060721262604
2d437db26f67ae7f47437cb36f67ae7f47fe238d880afdbe098c8c89880e023e0d8a022789d5914c4c91d58988d3914b4b91d3fdd4b2b2b2b20344a2a4a4a200000100000000050205c3001c005a4029
07060b0d01fb0d010d0d089019d0190219191e001e01b01e010f1e1f1e2f1e4f1e5f1e7f1e06070908b8fff0400c08006016040908030d060712003f33333f333fe1012f38335e5d5d7111332f5d1239
3d2f5d713333313001220e02070123013301161617363637133e0333321617152626049624352a2412fed7cdfe19c501271d2e110e2a1a8d1f3d4c674b23441a1735052d25486742fbe905b6fc615baf
4f4ebc610200719c622b0d0897090d00000100000000040e0452001f006db9001efff840160a0e481f080a0e481f1e07200d1148070700e0160116b8ffe0401a070a48161621bf21cf21ef2103502101
0f212f214f2103070100b8fff0400c001e071f151a4f130f01000f003f323fe13f3333012f38335e5d5d5d11332f2b5d12393d2f2b33332b2b31301133131e0317333e0337133e033332161715262623
2206070323bcc90b201f1a0506041418190956193543583c1a22110b26122f3a18e0f3044afda2216a6e6119195f6c672101405e75421805058703055058fcdfffff0000000005020773022602800000
0107037604b801520019b602011d05260201b8ff82b4311d081925012b3535002b353500ffff00000000040e062102260281000001070376045000000019b602012011260201b8ff94b4342000162501
2b3535002b3535000003007dfe14095a05cd00130027004a00a1400a005b1e384a2f2f372928b8fff040232828400040104002f040011f402f403f4003001e011e401e400a363710d037e0370237b8ff
c04034070b4837374c0f4c010f4c1f4c2f4c4f4c7f4cbf4cdf4cef4c0807145b0a664b382f4a4a2844503d1b36280f235f0f04195f0513003fe13fe13f333fe111391133330110f6e15e5d7111332f2b
5d38c11239392f2f5d5d5d7111332f38c112393d2f333310e13130011402060623222626023534123636333216161205141e0233323e0235342e0223220e022533131e0317333e03371333010e032322
262735161633323e023737051f4c96dd9198e093474793e19990dd954cfc232f6196676796602e2e6094676897612f0443bdd70d1d1913040604171b1d0bc7bcfe4e1d40567450344c1b154023304634
250f3902ddaafeebc66c6cc60117aaaa0115c46b6bc5feebab89db99515199db898ada97515197dae3fd9b28585853221956615e210263fb2751815a310b06910507172c4029a000ffff0071fe14084e
045e0026005200000107005c046f00000011b10202b80414b4202f0a0025012b350035000002007dff8305c30631001f003f00524033305b006741b441c44102904101023041804102205b1066401918
010b180104081408020738353b5f1b18150328255f08050b12003f3333e1323f3333e132325e5d5d5d0110f6e15d5f5d5d10f6e1313001140e02070606232226272e0335343e02373636333216171e03
05141e02173636333216173e0335342e02270606232226270e0305c34989c57c0e4a373b480d81c787454587c7810d483b374a0e7bc5894afb7f2d577f531143353145115280572d2d57805210463135
4311537f572d02dd95fcc07d173f36363f167dc0fe9696fbbf7b173f31333d177dbffb9674c09262153026282e156292c07473bf92621632292930166191bf0000020071ff9104a804b6001f00370054
b52c4800573939b8ffc0b30e144839b8ffc04024090c4820481056381918010b1801040814080207322f35501b02150f2926235008050b16003f3333e132323f3333e132325e5d5d5d0110f6e12b2b10
f6e1313001140e02070606232226272e0335343e02373636333216171e0305141617363633321617363635342627060623222627060604a8396b995f093c383a3b085d996e3d396c9a62083b3a373d09
5b986d3cfc856f7b0b393432390c796d6e780c393234390b7b6f022776bf8e5a12392e2d38125a8ec07775bf8d5a1239292a3a125b8dbc75accd1f2c20202a1fccaba9c8202b20202d1fc8000003007d
ffec0735083d0045005b006d00da40923714240414041424031c415b0c676f006f106f02006f206f506f806f906fa06fd06fe06f082d5b1c666e636847475c68514052505260520352525f0068106820
6880689068050768e04d014dbf56cf56025652ef46014647474f52015252606bc06bd06be06b046b6f5f7f5faf5fef5f04105f015f323c5f11147f378f37023740070a48373717111325030328005f21
0704003f33e1323311333f33332f2b5d3310e1322f5d5dcc5d322f713310cd5d10dd5dcd5d012f5e5d33332f5dcd10cd322f113910f6e15d7110f6e11217393d2f182f2f113331300122060727363633
321616121514020606232226270606232226260235341236363332161707262623220e0215141e0233323e02371e0333323e0235342e02031523222e0223220615152335343633321e02330514060735
363635342e02353436333216053f3c5e2a4c3b8f5177b77d403f81c68867a24547a06788c6813f407db777518f3b4c295f3c4571502b2e5c8a5c2a564e421717424e562a5c8b5c2e2c5071031056907a
642a2e3a857c6d3a7077854efeef78793c381f241f322e3b4405252e209c2a2c63b8fef8a6aafedfd2772a2c2c2a77d20121aaa60108b8632c2a9c202e468acf888de5a45913232f1c1c2f231359a4e5
8d88cf8a4602a681242a242d3510236b65242a24e24d731f4c162d191312101a1c25264600030071ffec05df070800430059006c00b2406b606745455a674f405050500250505d6f679f670210670167
24150b353f03030b2c483f576e1e480b566d004b014bdf540154500f44014445456f50015050006a01e06af06a026a0f5d7f5d02075d2f1b513416163a1010212951000324401015482440070c482424
060016003f32322f2b2b3310e1323f33331133e1322f5e5dcc5d71322f713310cd7110dd5dcd710110f6e110f6e112393d2f1239123933182f5d5d33332f5dcd10cd322f113931300522262706062322
2e0235343e0233321e0217072e03232206151416333236371e0333323635342623220e0207273e0333321e0215140e02131523222e0223220615152335343633321e023305140607353635342e043534
363332160410516d2b2b6a505fa97f4a3f6d93541f403c34143f11292b2b1270698d884973291631394229858c6970122b2b29114014353c3f2054936d3f4a7ea93a1056907a642a2e3a867c6e397078
854efeef7879740f151a150f322e3b4414232020233f89d5969ddb893e0910160c9a0a130f09cad3d3c33420101e180ec3d3d3ca090f130a9a0c1610093e89db9d96d5893f06aa82242b242e3411236c
65242a24e24d731f4c29330d0f0c0a0f1712262646000002007dffec075e0704000d0053009b40622312475a33c544019644a6440289440144442b4f5b1a000d000d2b1a675500551055020055205550
557055d055e05506073c5b2b66544545410e375f30113409040400800a063f027f02cf02df02ef020502341530044a2320090e4823415f1f2613003f33e1322b323f33332f5d33331acd392f33113310
e13212392f0110f6e15e5d7110e61139392f2f10e111392f5d5d5d33e1323931300115072327230723272307232735012206072736363332161612151402060623222627230606232226260235341236
363332161707262623220e0215141e0233323637113311161633323e0235342e02058b522232b8312331b8322250031c3c5e2a4c3b8f5177b77d403f81c6886faa49024ba86e88c6813f407db777518f
3b4c295f3c4571502b2e5c8a5c39762ebb2e763b5c8b5c2e2c507107041bac67676767ac1bfe212e209c2a2c63b8fef8a6aafedfd2773230303277d20121aaa60108b8632c2a9c202e468acf888de5a4
59242601bdfe43262459a4e58d88cf8a460000020000000005f405a4000d003a0123400d000d000d2a0fb930018230012fb8fff0b30a10483ab8fff0b3131b483ab8fff040190a0f48a90e010e10131b
480e100a0f48301b1c34661d011d1cb8fff040530f1c1f1c3f1c034f1c6f1c7f1c9f1cbf1c051c1c34302f239023017423016b230124233423023a0e1490340124143414441474149414052334141434
23030f2947002a102a02072a2a3c103c203c303c03100fb8fff040350f09040400800a060f027f028f029f02040240141748023440151d4834200c14481b341e030e291c0f0f2314261436140214300e
15003f33335d11333f33331217392b2b2f2b5d33331acd392f33012f38335d11332f5e5de11217393d2f2f2f5d5d1133335d5d5d5d1133331133182f5d7138335d87c0c0012b2b5d2b2b2b5d5d111239
392f2f3130011507232723072327230723273503013313161617333e0337130333131e0317333e033533140202060723032626350e03070304ba522232b8312331b831235004fe89bce01c2609070715
1a1d0f77a2bcc90c1f1e1805075a724119b7255c9a75c0770c0d0106090a049805a41bac67676767ac1bfa5c044afd56579d33163a414520010c01cffdb02163675c1967d0e2ff98a3fedffef5fc7f01
5420350301131b1d0afeaa000001007dfe14049805cb002300544017115a0f10010710400f1248101018ba2001602070200220b8ffc0401a060a48202025af2501055b18662421005f1d040f0a5f1313
101b003f3fe1333fe1330110f6e15d11332f2b5d5d12392f2b5e5de1313001220e0215141e0233323e0237112311232226260235341236363332161707262603196bae7b433b76b0761d2d282515bb10
a4f09d4c57a9faa26cc44f4e3f9405275198da898ddb964e04090d09fd6201d86cc60115aaa60114c66e2c2a9c202e0000010071fe14036f045e001f003e40251d471c400e11481c1c040c215f217f21
0210210115480456201b18511f161c1b12510d0910003f33e13f3fe1330110f6e15d5d10ce11392f2be13130042e0235343e0233321617072e03232206151416333236371123112301edb0824a4c85b2
664e95323617383c3b199d9091943e6823b712143f89d5969ddb893e22199a0a130f09cad3d3c31b14fd5a01d80000010068fffa0479050a0013012540670d111d11020d011d0102020b120b02020712
07020710110c13120b000108030207040f040d110c0a120b090108060207050e050d10110c110a13120b120900010801060302070204050c0b02010e0f4f0101400b01050b010f0f010b050412601170
11f0110311b8ffc0404d070a4811080711152015301502030600090407010102130a100d040b1111120407010707080b0b010b0b0cb00cc00c020f121f120200081008020212080c0c08120204040f0e
40070d480e0504002f332f2b331217392f2f2f2f5d5d5d11332f7111332f7111332f12173911332f121739015d11332f322f2b5d3317392f2f2f2f5d5d11331133113311331087c0c01087c0c01087c0
c01087c0c0108708c008c008c008c0108708c008c008c008c03130005e5d5d5d5d0105072503271325370513253705131703050725024c011c47fee3b481b4fee546011fc6fee447011db67fb6011f4a
fee501b0a67ba4fec74a013ba47ba4015aa47da4013949fec4a47ba4000100c9048f03b005b800150042b50f20070a4803b8ffe0401e070a480c121217000006100620069006a00605070606160f0f0b
00030300002f322f10cd322f1101332f5e5d3311332f33002b2b313001060623222635343e023321363633321615140623018b062c3038280917251d01c1052d30382a2b3904ee2c3333381c2516092d
313236392900000100f404e503df05d90015004240118008900802080817001410142014030714b8ffc040120d1048141416151414090e40bf0501058009002f1add5d1acd11332fcd1101332f2b5e5d
11332f5d313001323e0233321615152335342623220e0223233501024e8577703a6d7c853a2e2a65799155100566242b24656c2311342e242b248100000101e104d702d306350011002040120b0e3f06
4f060206003f034f035f0303030f002fcc5d012fcd5d3239313001343633321615140e021514161715262601e1443b2f321f251f393c7a7805b6394626261b1a111113192e154c1f7300000101e104d7
02d30635001300204012073f004f000200030e3f114f115f11031103002fcc5d012f33cd5d3931300114060735363635342e0435343633321602d3787a3c390f151a150f322e3b4405b64d731f4c152e
190d0f0c0a0f17122626460000080029fec107c1059100130027003b004f00630077008b009f0000012e0323220e020723363633321e0217032e0323220e020723363633321e0217012e0323220e0207
23363633321e0217212e0323220e020723363633321e0217012e0323220e020723363633321e0217212e0323220e020723363633321e0217012e0323220e020723363633321e0217212e0323220e0207
23363633321e0217046f0318242e191e2f2114034b056467314d361e034f0318242e191e2f2114034b056467314d361e0301f40317242e191f2f2113034c056567314d361e03fb2f0317242e191f2f21
13034c056567314d361e0304310317242e191f2f2113034c056567314d361e03fb2f0317242e191f2f2113034c056567314d361e0304f00318242d191f2f2114034b056468314c361f03f9be0317242e
191f2f2113034c056567314d361e0304cf1c2313060512231e5a681c33482bf9f21c2313060512231e59691d33472b01161c2313060512231e5a691d33482b1c2313060512231e5a691d33482b03db1c
2313060512231e5a691d33482b1c2313060512231e5a691d33482bfe191c2313060512231e5a681d33472b1c2313060512231e5a681d33472b0000080029fe7f077d05d3000c0019002600330040004d
005a0067000005170e0307233e033703273e0337330e030701371e0317152e032705072e0327351e031701373e0337170e030701070e0307273e033703272e0327371e031701171e0317072e03270437
0b0a1c202312610a151210053b0b0a1c202312610a1512100502230e26555757272a5d5b5725fb680e27555756272a5d5b572503a602225054562845254f4e491efcea02234f54562845254f4e491e2b
11142927240f431733343217036a11142827250f431734343216230e27555756272a5d5b572504980e26555757272a5d5b5725fe160b0a1c202312610a151210053b0b0a1c202312610a1512100501aa
10142927250f441733343216fc9510142927250f44173334321602de02225054552846254f4e491efce902234f54552846254f4e491e000200c9fe7f05d30768001b003100b340481c1c302601d02601
af26cf2602202630268026032626170a191a1a181b1507005a176533d03301af3301203330330213080c5a096432269f1c011c400609481c1c2c8e21051afb13b8ffe8b314184813b8ffe040260a1348
061316132613030913175f0012061814184806200a1348090619062906030706160a03003f33335e5d2b2b3fe133335d2b2b3f3fe1332f2b5d330110f6e132325d5d5d10f4e132323333392f33111239
2f5d5d5d71332f313021113436373637230123113311140e020706073301331133032313030e0323222e0227331e0333323e023704640504040608fd31d7ae0103030104030702ccd5c38fdaa69d0631
5d8d64678c582a04aa041c324c352b493722060319388f414c4dfb4605b6fce01a3e43441f4a4c04b4faf0fdd9018107684d775029274f775039482910122c473500000200aefe8304f20617000f0025
0096403a1010041a341a841a03071a1a0b050d0e0e0f0c0910091848090200460b5527a427c427d427f42704802701020027102720275027602770270603b8fff04025091848030807460454261a7f10
0110400609481010208e15110efb0902050f0b6008040015003f3232e13f33333f3fe1332f2b5d330110f6e132322b5d5f5d5d10f4e132322b3333392f331112392f5e5d332f31302111130123113311
0701331133032313030e0323222e0227331e0333323e0237038f0bfdfeeaac0c0204e9b77bb67a4706315d8d64678c582a04aa041c324c352b493722060287010ffc6a044afd64fc0398fc50fde9017d
06174e7651292750775039492910122c48350002002f0000043305b6001600210052402c1300175a0c151506100c0c0e1b5b0667230e215f00160d5f1013108900010f10010700100010110317600c12
003fe13f39392f2f5e5d5d113310e13210e1012f10f6e111392f3312392f10e1323231300133321e0215140e022321112335333533152115211133323635342e02232301819698cf7e373c7ec286fe96
9898ba013bfec5a0aea42e5b8b5d81034e42719857619e703d0460a4b2b2a4fc40848846613c1b0000020012000004190614001400210061403b01010e1c470857236f23bf23df23ff23040300164712
110e5422155004030f5012001204400724480f12010f121f12020704120412130016500e15003fe13f39392f2f5e5d712b113310e13210e10110f6c433e132325d10f6e112392f313001211521112132
1615140e022321112335333533111121323e0235342e022301640117fee90115d3cd2f659f71fe399c9cb60104375e432621416040052189fdef9c9b4d7c582f049889f3fbdafea81129423031422811
000200c70000043305b6001300220088404f1b051a061a180819070706190619060c1e5b28003800480003006724cf24014024010f240106140c5a0d642319181baf1a011a1a142206b0070107070c05
081460300b400b020b0b0c22600e030c12003f3fe111392f5de1393912392f5d331112392f5d3939330110f6e1325e5d5d5d10f65de11239392f2f1133111239393211123939313001140e0207170727
06232311231121321e020133323637273717363635342623230433162f4b366e6a815d7c96ba016a86c27e3cfd4e8126441f5c6d703334a4aea0040a3a6f6456209b4eb61bfdc705b6396da0fe670505
854ca02473578e89000200aefe14043f045e002300390072404034353233332a201e1f1f3748212a1b573b303b012a100c470d543a3335322f0f341f346f34cf34040734342f24501016100e0f1f2020
000c1b211e2f50060016003f32e139393f12392f333f3f33e111392f5e5d123939330110f6e132325d10f61139e1392f393311392f393933313005222e02272316171616151123113317333e0333321e
0215140607170727060603220e020715141e023332363727371736353426029e3b604d3c160c03030204b6941a08163b4d603c5e996e3c5a526d6a751f41464c69411f021b416c5111210f6a6d6e547f
14192c3a20221f1a3710fe2b063694223e2d1b488fd48caff2449a4ea6090b03db2e5e8d5e29659d6b370305984c9e6aeed0ce000001002f000003be05b6000d008940151f052f05020505080f001f00
029000a000e0000300b8ffc0401e070a4800000f1f0f3f0f9f0fbf0f0403075a0c000a01060a8008c0080208b8ffc04026060a480806095f030f0c2f0c3f0c4f0c040f0c3f0caf0cdf0cff0c05080c0c
07025f0d030712003f3fe112392f5e5d7133e132012f2b5dc65e5d33e1325d11332f2b5d7112392f5d3130011521112115211123112335331103befdc30150feb0ba989805b6a6fe15a2fd7d0283a202
9100000100120000030a044a000d006e401d0b0b00200630060206060f700fa00fc00f03090d4704df02ef02020200b8ffc0b312154800b8ffc04021060b48000c014f094f045f04020440171c480b04
1b0402070404000850050f0015003f3fe112392f5e5d2b5d33e132012f2b2bc65d33e1325d11332f5d12392f31303311233533112115211121152111ae9c9c025cfe5a0117fee901e18901e09afeba89
fe1f000100c7fe00049805b600260066404109150107070705225b1228c028017f288f280210284028021c1c0a045a0564271d205f171c00600f0d2f0d3f0d030f0d3f0ddf0dff0d04080d0d05095f06
030512003f3fe112392f5e5d71e13fe1330110f6f132c02f5d5d5d10dee112392f3130005e5d0122060711231121152111363633321e0215140e0223222e0227351616332011342e0201fc253c1aba02
f7fdc31d472d9af0a5575292c976314e443d1f3f7f480177437bae02830505fd8705b6a6fe0b05035cabf297aaf8a24f060c130ca2171801ef79b97e4000000100aefe0a03c5044a0023005c40390922
19222922031414120a481f250f251f25025f257f25ff2503030317114712542404000d510b1a1b1a02071a1a121650130f12150751001b003fe13f3fe112392f5e5de911330110f6e132392f5d7110de
e112392f3130005d012226273516163332363534262322060711231121152111363633321e0215140e0202294b6a2e2b6a427675909d1a3e1fb6025cfe5a1d3b1f66b3854c406f96fe0a1f1da11825c3
d2d4ca0a08fe35044a9afec305053e8adb9d96d5884000010004fe7f06cb05b6001500e24057114014184811200913480a111a1102004014184800200913480a001a000207100d0d025a070a0aa803b8
03029903018d0301570367037703030303031206000e0f100f400c18480f110f155a04120174128412c4120312b8ffc04027070a48121217041701f01701e41701a01701022017301760177017901705
0f1701080908050806b8fff0401306061613fb0d0a0a000f080b03115f05030012003f3232e13f3333123911333f1101332f38333311335e5d5d5f5d5d5d7111332f2b5d71e133332f2b383332111239
2f5f5d5d5d5d331133e13211335e5d2b2b5d2b2b31302101112311012301013301113311013301013311231105aefdeeb3fdeed30221fdedcd020ab3020acdfded01a8c3b002e5fd1b02e5fd1b02f202
c4fd3c02c4fd3c02c4fd3cfdb4fdd901810000010004fe830604044a00150111406c2710371002290e390e022609360902270a370a02270437040227043704022701370102114014184811200913480a
111a1102004014184800200913480a001a0002100d0d0246070a0acd0301b90301a903019603018703010303031206000e0f100f400a18480f110f154612b8ffc0b312154812b8ffc04017070b481212
17101701001701e417f41702b417c4170217b8ffc040130d1148301701022017010f1701070908050806b8fff0401306061613fb0d0a0a000f080b0f116005030015003f3232e13f3333123911333f11
01332f38333311335e5d5d5f5d2b5d5d717111332f2b2be133332f2b3833321112392f5f5d5d5d5d5d331133e13211335d2b2b5d2b2b3130005d5d015d5d5d5d5d210111231101230101330111331101
3301013311231104f0fe43a4fe44cf01cffe58c5019fa401a0c5fe58014fc5ae022dfdd3022dfdd302350215fdeb0215fdeb0215fdebfe65fde9017dffff0048fe4203ec05cb022601b100000107037f
014800000011b10101b8ffffb44842130b25012b35003500ffff0044fe420352045e022601d100000107037f00f200000011b10101b8fff8b44842292125012b35003500000100c7fe7f04ec05b60010
00a640590540141848bd05cd05dd050305200910481a052a05020040141848bd00cd00dd000300200910481a002a0002061010080e0f107f0f9f0f02000f300f400f03060f0f05045a00000110010207
010112b012010c085a0964110cb8fff040160b104806100b10480c06090f0a03005f0505091202fb003f3f332fe13f331239392b2b0110f6e1325d11332f5e5d33e132332f5e5d5d3833123911335d2b
5d2b5d2b5d2b313025331123112301071123113311370133010435b7b16cfe3d8bbaba7901c4d1fdf8a6fdd9018102ba72fdb805b6fd25a80233fd83000100aefe83042b044a000e0099406a08401418
48bd08cd08dd080308200910481a082a08020340141848bd03cd03dd030303200910481a032a0302080746000176018601026d01015501012b013b014b01031401010b01010603010004100402040410
0e020a470b540f09020e0e000b0350081505fb0c000f003f323f3fe13312391133330110f6e1323211332f5d33335e5d5d5d5d5d5d1133e1325d2b5d2b5d2b5d2b313001330101331123112301112311
331102fac4fe61014bc1ae66fe4bb4b4044afdf1fe5ffde9017d022dfdd3044afdeb000100c7000004a205b600120097b9000fffe0405d121548500f600f02220f320f420f030240090d480b075a0834
12541202120f570301030c1d04010b0401040400086413101111010010000010000207000014b014012f14011014010b05090820043004400403040d040d100903080012003f323f3339392f2f5d1112
3939015d5d5d11332f5e5d383333113310e611392f5d5d33c15d32325d10e1323130002b5d5d2b2123011523110711231133113711331501330104a2d3feae8577baba77850141d1fdf801f4cd016462
fdd705b6fd06a40163b301a6fd65000100ae00000423044a0013009cb511400b11480ab8ffc0b30b11480eb8fff04057080d480e0a261201120789139913a913031f132f133f13030b130113130f0602
470354140b0c0c100f10200f300f020f0f1500151015020015401560158015a015c015e01507070e0d06060813081308020b040f100215003f333f331239392f2f1239113333015e5d7111332f5d3833
33113310f6e13212392f5d5d5d33c15d32322b3130002b2b012711231133113711331513330115012301152301cf6db4b46d7debc5fe5001d7cffef87d01b27bfdd3044afdeb7b0146b9010dfe1649fd
e90127cd0001002f000004a205b60014008540400214661401141304400e500e020e0e00100c045a09070564151213101313010010000010000207000016b016012f16011016010f065f0c0909000210
0b104810b8fff0400c0b1048100205120a03050012003f323f331239392b2b12392f33e132015d5d5d11332f5e5d3833332f383310f6c633e1323212392f5d1112395d11333130212301071123112335
33353315331523113701330104a2d3fe3d8bba9898bad5d57901c4d1fdf802ba72fdb80460a4b2b2a4fe7ba80233fd8300010012000003f0061400160084400f110e0e0f0408090313471400060106b8
ffc0b310154806b8ffc04035090e4806060f16011454170c0d100d0f10350f01000f100f200f03080f0f180f180109110214140f07154f040101020f150c0f0200003f3f3f12392f33e1321133111239
39015d11332f5e5d5d383333113310f632c612392f2b2b7110e117321139113331301333353315211521110333370133010123010711231123129cb4017bfe851004870125d3fe6f01acd1feb06db49c
055eb6b689fe75feedaa0169fe25fd9101f852fe5a04d500000100140000054405b6000e00574033020e0e0d0a045a2005300540058005f00505050500070c0d0d010010000010000207000010101001
020a050c075f0803050012003f323fe133123939015d11332f5e5d38333311332f12392f5de1321139113331302123010711231121352111370133010544d3fe3d8bbbfeac020f7901c4d1fdf802ba72
fdb80512a4fd25a80233fd8300010029000004c9044a000d00674040020d0d0b06470007010007100720076007c0070508070703090001d501f5010201040310000310030203030f400fe00f020f0f01
020d0d070950000a0f070315003f333f33e112391133015d5d11332f5d3833335d11332f12392f5e5d71e1323211333130013301012301112311213521071103ddc5fe6001c7cffe4cb5fe98021f0204
4afdf1fdc5022dfdd303b09a02fded00000100e5fe7f05a405b6000f0063403f045a040101740184019401030101110d055a0011e411f41102c01101021011201140118011040c085a096410075f0f0c
01080c0c050e0a0309005f051202fb003f3fe1333f3312392f5e5de10110f6e1325d5f5d5d10dce13211332f5d71e131302533112311231121112311331121113304f4b0b0bbfd67bbbb0299bba6fdd9
018102aafd5605b6fd980268000100aefe8304db044a000f00794052080047040b010b0b0f47c40cd40c020c0c110411241102d411f41102b011010230114011501180119011a011060703470454100d
fb0250eb0701a907b907020f071f072f07030607070009050f0b50040015003f32e13f3312392f5e5d5d5de13f0110f6e1325d5f5d5d7111332f5de1332f5de132313021112111231133112111331133
1123110385fddfb6b60221b6a0b601e9fe17044afe3901c7fc50fde9017d000100e50000064805b6000d005d403b09015a000005400b01e00b010b0f200f01000f100f300f400f600fa00fb00fe00f08
0608045a05640e0d5f0a0a06035f0f08010808080603050012003f323f392f5e5de111332fe10110f6e1325e5d7110c65d7111392fe1323130212311211123113311211121152104f4bbfd67bbbb0299
020ffeac02aafd5605b6fd980268a400000100ae000005a4044a000d006840210804470509014700000b05540e0b0b0f000f200f02900fb00fc00fd00ff00f050fb8ffc0401f070c480350eb0801a908
b908020f081f082f0803060808000d0a060f050015003f323f33c412392f5e5d5d5de1012b5d7111332f10e611392fe13210e13231302123112111231133112111211521043bb6fddfb6b60221021ffe
9701e9fe17044afe3901c79a000100c7fe0007db05b60027005f403a090b0107121200205a212125185b08296f2901245a2564281d600f032f033f03030f033f03df03ff030408030325235f26032125
1213165f0d1c003fe1333f333fe112392f5e5d71e10110f6e15d10dee111392fe132392f3130005e5d01363633321e0215140e0223222e0227351616332011342e02232207112311211123112104c11e
492e9af0a5565292c976314e433d1f3f7e490177437cae6b4b33bbfd7bba03fa031b05035cabf297aaf8a24f060c130ca2171801ef79b97e400cfd890512faee05b6000100aefe0a0675044a00240077
404e0b231b23020303191147021201c21201a612b6120277128712021212160a4820260f261f26024f265f26af26ff260410260115471654250d510b1b1b1b02071b1b161450170f121615040751001b
003fe1333f333fe112392f5e5de10110f6e15d5d7110dee111392f5d5d5d71e132392f3130005d01222627351616333236353426232206071123112111231121113633321e0215140e0204d94b6a2e2b
6a427675909d1c3e1fb6fe08b603643a3f66b3854c406f96fe0a1f1da11825c3d2d4ca0a08fe3503b0fc50044afe270c3e8adb9d96d588400002007dffac059605cd003e0052007a4050054e10320400
495b002010200235203520183f5b0067540f541f544f545f547f549f54af5407072b5b18665344604e05003a103a203a603a703a803a063a3a07265f211d041032305f13075f0e0e1313003f332fe110
e132393f33e112392f5d3939e10110f6e15e5d10f6e11139392f2f5de1121739313001140e0207163332363715060623222706062322262602353412363633321617072e0323220e0215141e02333237
260235343e0233321e0207342e0223220e0215141e02173e03056f2741552d365125461f1d4f26a68b327c3c96e89d514d9ef2a6427825350a2830331576a96c333c70a06430264f5b3760834c4b8562
39c5162a3d27293d29151829361f2a422e1802a665af906f24190e0baa0d0c6211116ac0010ea5b5011fc769160f9c050a0905519ade8d94da9046076901169b85c17b3b387bc6955489633637628750
4885766527216277880000020071ffc504a4045e000d004d0079404944051132043f00485421642102352135211908483f4a4a3f574f7b4f8b4fbb4f035f4f6f4f021b4f010f4f0107022a4819564e0b
44053a3a472550221e1011322f501447500e0e1416003f332fe110e132393f33e912392f3939c10110f6e15f5e5d5d5d5d10e6322f10e11139392f2f5de1121739313001141e02173636353426232206
01222627060623222e0235343e023332161707262623220e0215141e0233323637262635343e0233321e0215140e020716163332363715060602dd14202a153948383d3e41014c45883928624775b37a
3f3c78b47942521a29134a2a5470431c1e436e50253203394a31557241406f522e1e34452714371d1a391c194201f4355c4b391228966b677a7afd682a2511175797cc7583d6975310069605103a6fa0
6759946b3c080242b4775e88592b27568a634678634d1b0a0d0608930907ffff007dfe42049805cb0226002600000107037f022f0000000eb7010175322c182025012b350035ffff0071fe42036f045e
0226004600000107037f01710000000eb70101522e28050d25012b35003500010014fe7f041205b6000b006f40391f0d01e00d015f0ddf0d02200d300d400d03045a0f011f0102070101af0aef0a020a
0a0b5a064007e007020f07010807075706670677060306b8ffc0400f070a48060b075f0803005f051202fb003f3fe13fe132012f2b5d332f5e5d5d10e1322f5d392f5e5de15d5d5d7131302533112311
231121352115210271b0b0bbfe5e03fefe5fa6fdd901810512a4a40000010029fe83035e044a000b006f404df00d01af0d01400d500d022f0d010b47af08bf08cf08030840070a480808df05019d0501
8f0501050507470060027002d002031f0201020200001000b000c000040009fb060250030f07500015003fe13fe1323f012f5d332f5d5d10e1322f5d5d5d392f2b5de15d5d5d5d313021112135211521
11331123110168fec10335fec19fb603b09a9afceafde9017d00ffff00000000043705b60206003c000000010000fe1403d5044a0013007c40130a0a0047a60101870197010201010311121012b8ffc0
b312154812b8ffc04020070b48121215f01501df15ef150250159015a015034f15013015010f15010403b8fff040100311030f050a150a0207130a0215001b003f3f33335e5d3f33012f38335d5d5d5d
5d5d11332f2b2b383312392f5d5de1393d2f31300123110133131e0317333e03371333010246b7fe71bcc710201b14050704151b2010c7bcfe71fe1401e8044efdcf2e645e4f19194f5e642e0231fbb2
000100000000043705b60010009f4019ef12011240090c48000004085a090d09960901770987090206b8ffc0400d1518480b090606090b0302100fb8fff04038ab0f01900f01140f010f010210100201
ef0201d00201bf02018002014f025f020214020100020107020212070a5f04000d0d0108120f0103003f333f12392f3933e1321101332f5e5d5d5d5d5d5d5d7138332f5d5d5d38331217392f2f2f2b5d
5d113310e132393d2f2b5d31300101330115211521112311213521350133021b0154c8fe420127fed9bbfeda0126fe42cb02d302e3fc833da4fea80158a43303870000010000fe1403d5044a00190085
40150e0e17014706a60201870297020202020715161016b8ffc0b312154816b8ffc04020070b4816161bf01b01df1bef1b02501b901ba01b034f1b01301b010f1b010807b8fff040130715070f00034f
050e150e02070e170615011b003f3f33335e5de1323f33012f38335d5d5d5d5d5d11332f2b2b383312392f5d5d33e132393d2f3130051123112135210133131e0317333e033713330121150246b7fef6
0108fe73bcc710201b14050704151b2010c7bcfe71010a89fe9d016389044afdcf2e645e4f19194f5e642e0231fbb68900010000fe7f04b405b6000f01064088470f01480901030f000b06050a070e07
0c000b09050a080d080c0f000b000906050a05090c0f0306060e080a0501aa05ba05ca05ea05fa050505300b0e4805045a010d0e100b0e01ab0ebb0ecb0eeb0efb0e050e400b0e480e0a0001aa00ba00
ca00ea00fa000500300b0e480e00040114010207010111d411e411f41103c0110102201140110211b8ffc0400b1014480f11010b0a0a0708b8fff0401408280601270c010c06050d0a0307005f051202
fb003f3fe1333f331239395d5d012f38333311335d2b5d5f5d5d11332f5e5d33332b5d712f2b5d71383310e1322b5d711112393d2f173310870ec004c01087c00ec0108708c008c0108708c008c0015f
5d5d31302533112311230101230101330101330103feb6b077fe9efe91bc01c5fe5ac6014c014ebefe5ba6fdd90181027bfd8502fc02bafdd1022ffd4c0000010023fe83041f044a000f01394025590d
01560301b40c010c200e11483d0c01290c010b0c1b0c020c0b4608000306030d0d0104b8fff04071151848b504019904a904026a047a048a040345045504020604010405db02eb02fb0203c90201bb02
019602a60202650275028502034a025a02020201df05ef05ff050305400e11483b054b05022705010a051a050234014401022801010501150102050105010f3b070107200e11480708b8ffc04049070c
48080811601180119011b011d011e011f01107db0e01c90e01ba0e01650e750e850e034a0e5a0e02090e010e3b0f4b0f02280f01050f150f020f030d010e07500c1509fb04010f003f333f3fe1331239
39012f5d5d5dc55d5d5d5d5d5d5d11332f2b332b5d1239392f2f5d5d5d5d5d5d2b5d10c55d5d5d5d5d5d10c55d5d5d5d5d2b12393d2f173310e1325d5d5d2b5d3130005d5d0101331313330101331123
11230101230198fe9fcffafacffe9d010faaae65fef4fef2cf02330217fe66019afde9fe67fde9017d01b4fe4c0000010014fe7f066005b6000f0094400d015a0404085a100501d0050105b8ffc04044
071248050511df1101a011010f112f114f118f110407100e200e400e500e600e800e060e0e005a090f0b1f0b2f0b5f0b6f0baf0b06080b0b300940095009800990090509b8ffc04011070a48090f0b5f
020c0304005f091206fb003f3fe1323f33e132012f2b5d332f5e5d10e1322f5d5e5d5d5d11332f2b5d71e1332fe131302521113311331123112111213521152102710285bab0b0fc06fe5e03fefe5fa6
0510faf0fdd901810512a4a400010029fe83056d044a000f0080b60b470e0e02470fb8ffc0b30e18480fb8ffc04033070b480f0f110f111f112f114f11bf11df1106076008e008f0080308080a47031f
05ef05ff0503050500031003600370030403b8ffc04011141848030905500c060f0e0a50031500fb003f3fe1323f33e132012f2b5d332f5d10e1322f5d5e5d11332f2b2be1332fe13130012311211121
35211521112111331133056db7fcb2fec10335fec101f8b6a0fe83017d03b09a9afcea03b0fc5000000100a6fe7f053105b6001d006340120b0d1b0d021a045a901da01d021d1d035a00b8ffc0402c07
104800001f0f1f1f1f2f1f4f1f5f1f7f1faf1fbf1f0807125a0f641e155f1a0a0a041b10031d5f041201fb003f3fe13f3312392f33e10110f6e15e5d11332f2be1332f5de13231305d2511231123110e
0323222e0235113311141633323e02371133110531b0ba3a696361315d926535ba697b2d5a5e6638baa6fdd9018102561622160c315e8b590247fdd173740a141e1402c6faf00001009afe83049e044a
001c006140170b091b092b09030715004718181c470019101920190319b8ffc040250e154819191e0f1e5f1e7f1ebf1ecf1e050e470b541d1afb151050060600160c0f18500015003fe13f3312392fe1
333f0110f6e15d11332f2b5de1332fe13231305e5d21110e0323222e02351133111433323e02371133113311231103482c50555e3a4e7a522bb6b82d504c4d2ab6a0b701e91e3020112f54734401a6fe
66ae0f1c2b1d01d5fc50fde9017d000100a60000048105b6001d0070402e0b041b04021c0f1d040e140e02070e0e0613175a16651f701fa01f022f1f01101f01095a60067006a006b0060406b8ffc040
16070a48060e100e1d131b1d030c5f0101171407031712003f3f3312392fe117332f32322f012f2b5de15d5d5d10f6e13211392f5e5d33c13231305d0123222e02351133111416333311331136363711
3311231106060711230250215d926535ba697b0c85387644baba4277398501fc315e8b590247fdd173740158feb409221902c6fa4a02561a250bfec90001009a000003fe044a001c006b40420b041b04
02071b0e1c500d01040d140d240d03080d0d0612165a15551e101e601e801e03095a06541d200d300d400d030d0f0d1c121a1c030b5f01011613070f1615003f3f3312392fe117332f32322f5d0110f6
e15d10f6e13211392f5e5d5d33c13231305e5d31300123222e023511331114333311331136363711331123110606071123020e2f4e7a522bb6b8067d2f5b33b6b63259327d016a2f54734401a6fe66ae
012dfee50e302301d5fbb601e9233310ff00000100c7000004a205b60017005040360407140702070c5a0b65192f195f196f198f199f19af19cf19ef19ff190910190102165a17641815105f00051005
020505170b120003003f3f33392f5de1330110f6e1325d5d10f6e13130005e5d133311363633321e0215112311342623220e02071123c7ba73c3625d926535bb687b2d5a5f6538ba05b6fdaa2d2d315e
8a59fdb8022f73750a141e14fd39000100ae00000412044a0018003fb90014ffe04022070a48004718551aef1a01101a601a801a030d09470a541908035012120b0f0a0015003f323f392fe1330110f6
e1325d5d10f6e13130002b21113423220e020711231133113e0333321e021511035cb82d504c4d2ab6b62c50555e3a4e7a522b019aae0f1c2c1cfe2b044afe161f2f20112f537344fe5a0002003dffec
060605cd002b00360064403e325b140b67380f381f3802292022012222310c5b00701f801fc01f031f371f0c60314f2b010f2baf2bff2b030f2501082b252b2505115f141a132c5f0504003fe13f33e1
1139392f2f5e5d5d7133e1320110d65d32e132332f5dc15d10f632e13130013e0333321616121515211e0333323637150e0323222e022722263534363733060615143301220e020721342e0201660a5b
9cdd8d99d6883efc25073f74a97080cf5b2a5b697c4c98eba45c098f9a120b9c060f6702895b956e4107031225568b03399af4ab5b6bc5feebab457ac286472c20aa0f1a120a61b3fd9b757a2a42170f
3e236101f0417eb97875b880430000020033ffec04b6045e002c00350084402b3148271e5737ff37019037b037021f377f3702301f48050fdf080108081300051005200503400550050205b8ffc04028
1013480536051f50301b122b12020f12011b0b2b0b020f0b0106120b120b222d5018102251270016003f32e13fe11239392f2f5e5d5d5d5d33e1320110c62b5d7132322f5dc110e1325d5d5d10f632e1
313005222e022726263534363733060615141633333e0333321e02151521161633323e0237150e030322060721342e0203356bb3824b04858e120995060e31311b0e4c74985a639e6e3bfd4c05999733
57514c27284d51576072850b01ec1b3959144688c781026f72273f150e3a222f316ca9753e4781b56e71c1b60a131d12a2131c120803db9c954471502c000002003dfe7f060605cd002e003900724046
1b5a1c1c22355b140b673b0f3b1f3b022c2025012525340c5b0070228022c02203223a220c60344f2e010f2e3f2eaf2e030f2801082e282e28051a1411601d121bfb2f5f0504003fe13f3fe133331239
392f2f5e5d5d7133e1320110d65d32e132332f5dc15d10f632e112392fe13130013e0333321616121515211e0333323637150e03071123112e032722263534363733060615143301220e020721342e02
01660a5b9cdd8d99d6883efc25073f74a97080cf5b254e58643ab680c48a4d088f9a120b9c060f6702895b956e4107031225568b03399af4ab5b6bc5feebab457ac286472c20aa0e17110b02fe910173
106db0eb8e757a2a42170f3e236101f0417eb97875b8804300020033fe8304b6045e002d003600924030044705050b32482d245738ff38019038b038021f387f38023125480b15df0e010e0e19000b10
0b200b03400b500b020bb8ffc0402b1013480b370b2550311b182b18020f18011b112b11020f11010618111811282e501e1028512d03061604fb003f3f3333e13fe11239392f2f5e5d5d5d5d33e13201
10c62b5d7132322f5dc110e1325d5d5d10f632e112392fe13130250606071123112e032726263534363733060615141633333e0333321e02151521161633323e02370122060721342e02048542804cb6
5489633803858e120995060e31311b0e4c74985a639e6e3bfd4c0599973357514c27fe8372850b01ec1b395935202304fe950175115484b26f026f72273f150e3a222f316ca9753e4781b56e71c1b60a
131d1202f09c954471502c00ffff00520000026405b60206002c0000ffff0004000006810769022601b000000107023600f401520013400b0117052601001c12110a25012b35002b3500ffff00040000
05be0617022601d0000001070236009300000013400b0117112601001c120e0725012b35002b3500000100c7fe0004e105b600250067403f1b1b05215b11090a100a400c18480a0a0411271027402702
0b0808045a0564261f5f1c161c080300602f0c5f0cef0cff0c040c401015480c0c050906030512003f3f3312392f2b5de133333f33e10110f6e13211335d10ce11392f2b383310e111392f3130012206
07112311331101330133321e0215140e0223222e0227351616332011342e020246415c28baba0235cffdd1069af0a5565292c976314e433d1f3f7f480177437cae026f110efdb005b6fd3c02c4fd5855
a3ec97aaf8a24f060c130ca2171801ef79b3763a000100aefe0a03e9044a0023007240470b081b08022223101f230123231d050d0d1e144805250f25015f256f257f259f25bf25df25ff25070021211d
471e5424211c19510b001b00020700001e221f0f1e1511510e0a1b003f33e13f3f3312392f5e5de133330110f6e13211335d7110dee111392f1112392f7138333130005d011e0315140e022322262735
161633323635342e022322060711231133110133022b5ea378453f6f96574c692f2c69437675234a724e205529b4b40198c4026a054084d09496d588401f1da11825c3d26a97602d100dfe50044afe0a
01f600010000fe7f055605b600230073b10702b8fff84046141748025a891801181018e01d01b41dc41dd41d03001d501d02071d1d1f0f212222005a23201f259025c025f025030f251f257f2503080f
21fb025f1d0313600c131f5f0012003fe13fe13fe13f012f5e5d5d10d43232e1392f331112392f5e5d5d5d33385de12b3231302111210e03070e032322262735161633323e0237363612123721113303
231303d9fe850f2021211014354f6e4e234a1c173a20303e2a1a0c0c20272b1602d0c38fd9a5051271f5ebd45167a26f3b0e0b9a0d103f62753639c80106013aaafaf0fdd901810000010010fe830466
044a0018005840380246d412e4120260127012a012b0120434120112120a1617170047181514551a201ab01a020f1a01080a16fb0250120f0d4f071614500015003fe13fe13fe13f012f5e5d5d10f432
32e1392f3311392f5d5d5de131302111210202060623222627351633323636123721113303231302fafefe14405f82561c3110161c37594533120254b67ab77b03b0fefdfe94e6690606830675f1016f
fafc50fde9017d00000100e5fe0004f405b6001b004b402b050510140c5a17651dc01d01bf1d01201d01130f5a10641c0e5f0f1301081313101511031012095f06001c003f32e13f3f3312392f5e5de1
0110f6e1325d5d5d10f6e13211392f313001222e0227351616333236351121112311331121113311140e0202d1314e443d1f3f7f48bbc6fd67bbbb0299bb5292c9fe00060c130ca21718f0f7021ffd56
05b6fd980268faddaaf8a24f000100aefe0a043b044a001b0069401a030310140c4717551d001d201d02301d401d501dd01df01d051db8ffc040280e1148130f4710541c0e50eb1301a913b913020f13
1f132f13030613131015110f1015075104001b003f32e13f3f3312392f5e5d5d5de10110f6e1322b5d7110f6e13211392f31300122262735161633323e02351121112311331121113311140e0202a04c
692f2c69423b5b3d1ffddfb6b60221b63f6f96fe0a1f1da118252d61966901b2fe17044afe3901c7fbf396d58840000100e5fe7f05b605b6000f005240320203030d055a040100111011201140118011
c011e011f011070c085a096410075f0f0c01080c0c050e0a0309005f051202fb003f3fe1333f3312392f5e5de10110f6e1325d10d43232e132392f3331302533032313231121112311331121113304f4
c28fd9a6bbfd67bbbb0299bba6fdd9018102aafd5605b6fd98026800000100aefe8304f2044a000f0076404f0d0e0e0800470f0cab0bbb0b020b110411241102d411f41102b011010230114011501180
119011a011060703470454100dfb0250eb0701a907b907020f071f072f07030607070009050f0b50040015003f32e13f3312392f5e5d5d5de13f0110f6e1325d5f5d5d7110d45d3232e132392f333130
211121112311331121113311330323130385fddfb6b60221b6b77bb67a01e9fe17044afe3901c7fc50fde9017d00000100a6fe7f048105b6001d005940211d5a1a1a16005a19651f701fa01f022f1f01
101f010e5a600b700ba00bb00b040bb8ffc04014070a480b16115f06060c1a1bfb1d5f1a12170c03003f333fe13f1112392fe133012f2b5de15d5d5d10f6e132322fe1313025110e0323222e02351133
11141633323e02371133112311231103c73a696361315d926535ba697b2d5a5e6638babab0a601b01622160c315e8b590247fdd173740a141e1402c6fa4afe7f02270001009afe8303fe044a001c0040
40251b471818141c4717551e101e601e801e030d470a541d19fb140f5005050b1b501815150b0f003f333fe112392fe1333f0110f6e15d10f6e132322fe13130010e0323222e02351133111433323e02
37113311231123113303482c50555e3a4e7a522bb6b82d504c4d2ab6a0b6a001e91e3020112f54734401a6fe66ae0f1c2b1d01d5fbb6fe830217000100c7fe7f06f205b6001f009ab6361f013900011f
b8fff8402c0c0f481f00080c0f48000d0d0c10091213130e080c0f48390e010e1d155a141110212021c021020f2101080bb8fff840100c0f48260b010b02085a096420105f0cb8fff040160912480009
0c03151212fb1e010110091248010e0a03003f33332b11333f3f17332be10110f6e132325d2b5e5d5d10d43232e132325d2b392f331112393911332b332b31305d5d2101231617161615112311210133
012111330323132311343e0237363723010323fe450805040406ac0114019c06019e0114c390d9a6ba01020202030408fe4105004a493f8b39fc9605b6fb5804a8faf0fdd901810377193d41421e4749
fb02000100aefe8305c7044a00240087405a0807181810202223231e00462421af20bf20022026e02601cf2601302640265026a026040f260106120f4610542522fb013b0e4b0e5b0e030b0e1b0e2b0e
030e0e1e110f20503418441854180305181518251803081018030015003f17325d5de13f33332f5d5d333f0110f6e1325e5d5d5d5d10d45d3232e132392f33111239113333313021110e03070123012e
032711231133011e03173e033701331133032313046f0611141207fef887fef70511141207a2dd010b0d16130e05060e13160d010ed9b77bb67a038115393b3612fd5002b00d313b3e1afc7f044afd47
22433c321011313b412102bdfc50fde9017dffff00520000026405b60206002c0000ffff0000000004dd076902260024000001070236002101520013400b021a052602001f15040725012b35002b3500
ffff005effec03a1061702260044000001060236d4000013400b0238112602253d330c2225012b35002b3500ffff0000000004dd072b0226002400000107006a002101520017400d03021e0526030201
1529040725012b3535002b353500ffff005effec039c05d90226004400000106006ade000017400d03023c112603022f33470c2225012b3535002b353500fffffffe0000065605b6020600880000ffff
005effec0644045e020600a80000ffff00c7000003c8076902260028000001070236fffb01520013400b011105260106160c010025012b35002b3500ffff0071ffec03e1061702260048000001060236
ea000013400b022d1126020f3228050f25012b35002b350000020071ffec051205cd001e00290049402f241a5b0e672b0f2b8f2baf2bbf2b042b400b0f48255b0319662a255f2f195f197f1903191909
1f5f131303005f0904003fe1333fe111392f5de10110f632e12b5d10f6e132313001220607353e033332161612151402060623222626023535212e0303323e023721141e02027980cf5b2a5a697d4ca0
f4a554519de89699d7873e03db074073a9435b956d4207fced26558c05292c20aa0f1a130a6cc6fee9abaefeebc2686bc40115ab467ac18647fb66417eb97875b87f440000020066ffec03d7045e001e
002700434029221148055729b029013f290123481910562823509f10af10cf10df10041010001f500a161914510010003fe1333fe111392f5de10110f632e15d5d10f6e132313001321e0215140e0223
222e02353521262623220e0207353e031332363721141e0201e76eb683494278a766639e6e3b02b5059a973357504c27284c51576073850bfe141b3958045e4a8ed28788d6954e4781b56e71c0b60a13
1c12a1131c1209fc259c954471502c00ffff0071ffec0512072b022602e100000107006a004601520019b603023305260302b8ffd3b42a3e180e25012b3535002b353500ffff0066ffec03d705d90226
02e200000106006ac4000019b603023111260302b8fff4b4283c0f0525012b3535002b353500ffff000400000681072b022601b000000107006a00f401520017400d02011b05260201001226110a2501
2b3535002b353500ffff0004000005be05d9022601d000000107006a009100000019b602011b11260201b8ffffb412260e0725012b3535002b353500ffff0048ffec03ec072b022601b100000107006a
ffbf01520019b602014305260201b8fff4b43a4e130b25012b3535002b353500ffff0044ffec035205d9022601d100000107006aff5500000019b602014311260201b8ffd9b43a4e292125012b353500
2b35350000010048ffec03ec05b600200072402d005b1b1c5b2020401018481d1b20201b1d030d165b0567229f2201700d800d023f0d4f0d5f0d030d201d5f1e1bb8fff04018070d481b19aa00017800
010f00010800001e0313600e0a13003f33e13f392f5e5d5d5d33332b10e132012f5d5d5d10f6e11217392f2f2f2b10e110e13130011e0315140e0223222627351e033332363534262323350121352115
01f473ba84474388cd896ec0552b6063632eb2b0cfba7b01a6fda6033d033b053564976660a07440222daa1724180d948787819701d1a6910001001bfe140375044a00240064403e004a1f204a242440
131848211f24241f21030d18470557266f269f26023026010f2601070d400c11480d242150221f1d0020090c480000220f13500e0a1b003f33e13f392f2b333310e132012f2b5e5d5d5d10f6e1121739
2f2f2f2b10e110e13130011e0315140e0223222627351e0333323e0235342e02232335012135211501a85fa87d494784ba7373b53a1f4e5b66364777563037638b546d0192fdc6030601d5074175a66c
6eb784492422a6101f180f2e577d4e567d52277d01ed9a85ffff00c90000051006c1022601b200000107014d00b001520015b4011a052601b8fffeb41819000c25012b35002b3500ffff00ae0000043b
056f022601d200000106014d39000015b4010e112601b8ffffb40c0d0a0425012b35002b3500ffff00c900000510072b022601b200000107006a009c01520019b602012105260201b8fffeb4182c000c
25012b3535002b353500ffff00ae0000043b05d9022601d200000106006a25000019b602011511260201b8ffffb40c200a0425012b3535002b353500ffff007dffec0571072b0226003200000107006a
00aa01520017400d0302310526030201283c0a0025012b3535002b353500ffff0071ffec042d05d90226005200000106006af9000019b603022911260302b8fff9b420340a0025012b3535002b353500
ffff007dffec057105cd0206027e0000ffff0071ffec042d045e0206027f0000ffff007dffec0571072b0226027e00000107006a00aa01520017400d04033305260403012a3e0a0025012b3535002b35
3500ffff0071ffec042d05d90226027f00000106006a00000017400d04032b112604030022360a0025012b3535002b353500ffff003bffec0444072b022601c700000107006aff9701520019b602012e
05260201b8ffa6b42539040c25012b3535002b353500ffff0037ffec034205d9022601e700000107006aff3800000019b602013011260201b8ffcab4273b032225012b3535002b353500ffff0019ffec
04ae06c1022601bd00000107014d002701520015b40124052601b8fffeb42223130025012b35002b3500ffff000afe1403df056f0226005c00000106014dbb000013400b0125112601012324000f2501
2b35002b3500ffff0019ffec04ae072b022601bd00000107006a001201520019b602012b05260201b8fffdb42236130025012b3535002b353500ffff000afe1403df05d90226005c00000106006aa700
0017400d02012c11260201012337000f25012b3535002b353500ffff0019ffec04ae0773022601bd000001070153006601520017400d02012e05260201512236130025012b3535002b353500ffff000a
fe1403df06210226005c00000106015300000017400d02012f112602015a2337000f25012b3535002b353500ffff00a600000481072b022601c100000107006a005401520017400d02011f052602010f
162a0a0025012b3535002b353500ffff009a000003fe05d9022601e100000106006a06000017400d0201221126020108192d170a25012b3535002b353500000100c7fe7f03be05b600090064400b1400
01e40001d000010200b8ffc0403508134800000b2f0b4f0b6f0b8f0b040b40111848075abf04cf04028404010f041f0402070404035a08640a025f0903035f071206fb003f3fe13fe10110f6e1322f5e
5d5d5de12b5d11332f2b5f5d5d7131300115211133112311231103befdc3b0b0ba05b6a6fb96fdd9018105b6000100aefe83030a044a0009004e4032200230020202020bc00b01a10b01700b010947bf
06cf06028406010f061f0602070606054700540a0550091508fb0450010f003fe13f3fe10110f6e1322f5e5d5d5de15d5d5d11332f5d313033112115211133112311ae025cfe5aa0b6044a9afceafde9
017dffff00c7000005cf072b022601c500000107006a00fc01520017400d04032705260403001e32061c25012b3535002b353500ffff00ae0000055005d9022601e500000107006a00b000000017400d
04032711260403001e320a0d25012b3535002b353500ffff002ffe6d03be05b60226029b00000107038000a60000000ab3011f1f0701103c1135ffff0012fe6d030a044a0226029c0000010703810089
0000000ab3011f1f0d01103c1135ffff0000fe6d049a05b60026003b000001070380032300000017b1011db8ffc0b2131548b8ff9eb41d1d000025012b2b3500ffff0023fe6d0411044a0026005b0000
01070381029a00000012b301501d01b8ff9ab41d1d070725012b5d35000100000000046005b6001100b24027460f560f02490959090236000139060106090f00040c0303013011013f07010711071105
010b0ab8fff0b30a0a0405b8fff04014050d0e100e0e02011000010170018001c0010301b8ffc04027070a480101130f131f132f137f13040800065f0f09280301270c01030c0a0909010d0a03050112
003f333f3312392f1139395d5d1133e132015e5d11332f2b5d713833332f38332f3833332f3833111239392f2f5d5d11393d2f3317335d5d5d5d313001012301012301213521013301013301211502c5
019bd3fe9efe91bc019afedf0114fe92c6014c014ebefe8e011c02b4fd4c027bfd8502b4a4025efdd1022ffda2a400010023000003db044a0011011740bf8008900802080008000b0f060501f70501e5
05013605010506070a0a040d0110100d090301f80301ea030139030103026b067b06025706013a064a06026402740202580201350245020206020d020d06030f060c01f70c01e50c01360c010c0b4016
19480b400e11486b0b7b0b02570b013a0b4a0b020b0b1310133013029013b013020f1301090e01f80e01ea0e01390e010e6b0f7b0f02580f01350f450f020f0d0a104f0407ef01010b011b012b015b01
6b0105060101020f0b1505020f003f333f3312392f5e5d5d3333e13232012f5d5d5dc15d5d5d715d5d7110ce2f5d5d5d2b2bc15d5d5d711217393d2f182f2f5d5d5d5d5d5d10c15d5d5d711133113311
3333113310c15d5d5d71111239392f2f5d3130132101331313330121152101230101230121680100fecfcffafacffecd0104fef7014acffef4fef2cf014afefb027b01cffe66019afe3189fe0e01b4fe
4c01f20000020089000003f605b6000e00190040402906195a09651b9f1b01101b01155b00661a0f5f3f064f066f069f06bf06df0606060607195f09120703003f3fe111392f5de10110f6e15d5d10f6
e132313013343e02333311331121222e020123220e02151416333389377fcf9895bbfe9586c27e3c02b2815d8a5b2ea3af9f01ac579871420268fa4a3d709e015f1b3c6146888400ffff0071ffec0402
061402060047000000020089ffec063b05b6002300320063403f1809325a1a1a11225a0165340f34016f349f34ef340340345034020f3401062a5b116633245f1723233f174f176f17bf17df17051717
18031d2d6009060e13003f3333e1323f392f5d332f10e10110f6e15e5d5d5d7110f6e111392fe1393231300111140e02232226270e0323222635343e023333113311141633323e0235110523220e0215
141633323e0235063b2d5b8c5e72992b183f52673fd7e4418ad7967abb67612c48331cfdba815d8b5b2d7e86435b38170350fe18518b663a614d263f2e19d2d667a3723c0268fbb270691e39503201e8
a61e436b4d8082253e4d290000020071ffec065a061400320043005c4039102c3847502e012e2e1b03470655456f457f459f45cf45ef45054045010f452f45020641481b56440404333e502520102c00
003350100b1616003f3333e1323f3f33e112392f0110f6e15e5d5d5d10f6e111392f5de13239313025323635113311140e0223222e0227230e0323222e0235343e0233321e0217332627262635113311
1e0321323e023735342e0223220615141604d36f62b62f5c8b5b4167513d17061b445b79515d9a6e3c3c6e9a5d3b604d3b170c03030204b6010f2c51fda64c69411f021b416c51877f7f8382820135fe
c16998622e1e364a2c294a3720488fd48c8dd590491a2c3a20221f1a361101b4fb923e6a4e2d2e5e8c5f29659d6b37daccd1cd000001004effec064405cb003f008540560d5b20005b2b252007202b2b
20070316335a3665417f418f419f41ef41ff41050041204130410370168016023f164f1602161640250660070034013434df0701aa07017807010f07010807071b305f3b131512601b04003fe1333fe1
11392f5e5d5d5d5d332f5d10e1391101332f5d5d5d5d10f6e11117392f2f2f123910e110e1313001342e0223233533323e0235342e0223220607273e0333321e0215140e0207151e0315141e02333236
35113311140e0223222e0203293565925dbfb05c8e613225445f3a6ea94b5c26627483476da67139335b7d4b578a5e3218324f376f62b73866915957946c3c01874d6d4621972748643e36533a1e4336
7d1f3629183661854f4a7758390c060b395977484a68411e778101c9fe2d6998622e2f649c0000010050ffec05a2045e003b00814023154700204631363119003119193100032708470b553d0f3d01d0
3d01bf3d01303d0127b8ffc040291215485f270127273c3619501a0909df1a01991aa91a020b1a1b1a02071a1a052623502c1005501016003fe13fe13312392f5e5d5d5d332f10e1391101332f5d2b5d
5d5d7110f6e11117392f2f2f11123910e110e1313001141e0233323635113311140e0223222e0235342623233533323e0235342623220607273e0333321e0215140e0207151e0303521531503b6960b6
2f5f8f605c906435858a81613d644827736c47814b3f284d525a355690693a1c33462b32533c22014c2749382182820135fec16998622e2b547b4f5e5b991227402d4d4d262293131d1309264a6d4632
4d3b290e0b09293e52000001004efe7f049c05cb0031008340540f5b27092209220918015a702e802e022e2e005a2f2f33003301c033d033029f33010033203330330370188018023f184f1802181832
30fb270860df0901aa09017809010f0901080909011714601d042e5f0112003fe13fe13312392f5e5d5d5d5de1393f1101332f5d5d5d5d5d7111332fe1322f5de11239392f2f1239e13130212311342e
0223233533323e0235342e0223220607273e0333321e0215140e0207151e03151533112303ecbb3867955dbfb05c8e613225445f3a6ea94b5c26627483476da67139335b7d4b578a5e32b0b001a04364
4120972748643e36533a1e43367d1f3629183661854f4a7758390c060b39597748fafdd900010050fe8303f2045e002d008d401a0c461d221d061d061d13004729292d47202a302a402a602a042ab8ff
c0400e0f13482a2a2fc02fd02fe02f0313b8ffc0402d1215485f130113132e2bfb220550df06019906a906024f065f06020b061b060207060600120f50181029500015003fe13fe13312392f5e5d5d5d
5de9393f1101332f5d2b5d11332f2b5de1322fe11239392f2f123910e131302111342623233533323e0235342623220607273e0333321e0215140e0207151e03151533112311029c818a81613d644827
736c47814b3f284d525a355690693a1c33462b2c523f26a0b7012b5e65991227402d4d4d262293131d1309264a6d46324d3b290e0b0e2c425b3d91fde9017d0000010000ffe906ec05b6003100924065
d706e7060206070101d701f70102015a1c17101018488b17011710070e48171c315a1eb01e01e01c01b41cc41cd41c03001c501c901ca01c04071c1e1c1e0e245a2765334f336f337f339f33af33df33
062033010f33010e252512015f1c032112602c0b13003f33e1323fe112392f012f5d5d5d10f6e11139392f2f5e5d5d5d5d10e111332b5d2b10e15d71325d313001210e03070e03232226273516163332
3e023736361212372111141633323635113311140e0223222e023503d9fe850f2021211014354f6e4e234a1c173a20303e2a1a0c0c20272b1602d0636e6f62b7386691595792683a051271f5ebd45167
a26f3b0e0b9a0d103f62753639c80106013aaafbd18177778101c9fe2d6998622e2c60996c0000010010ffec0600044a0028005c4039014611284713a01301db1101c411019011a01102241101101101
11131113091b471e552a7f2acf2aef2a03091c1c0c0150110f180c4f230616003f33e1323fe112392f012f5d10f6e11139392f2f5d5d5d5d5d5d10e110e1313001210202060623222627351633323636
12372111141e0233323635113311140e0223222e023502fafefe14405f82561c3110161c375945331202541531503b6960b62f5f8f605c91663603b0fefdfe94e6690606830675f1016ffafd3d416241
2082820135fec16998622e2e62966900000100c7ffec072d05b6001d005c40371b135aa00001000017065a09651fc01f01af1fbf1f02201f010f1f011a165a17641e155f1a07070f1a01081a1a171c18
031712035f0e13003fe13f3f3312392f5e5d332f10e10110f6e1325d5d5d5d10f6e111392f5de132313001141633323635113311140e0223222e02351121112311331121113304d5626f6e63b6386690
595891683afd66baba029aba01878177778101c9fe2d6998622e2c60996c012dfd5605b6fd980268000100aeffec068b044a001f006c4047021a4705051e0d4710552100212021024021502180219021
b021f021060f2101011d471e54201c50010e0eeb0101a901b901020f011f012f01030601011e031f0f1e150a501516003fe13f3f3312392f5e5d5d5d332f10e10110f6e1325d5d7110f6e111392fe132
3130011121113311141e0233323635113311140e0223222e0235352111231101640221b61531503b6a5fb62f5f8f605b926636fddfb6044afe3901c7fd3d4162412082820135fec16998622e2e629669
6efe17044a000001007dffec055205cb002a004740292a2a0c295b011440090e48141401672cbf2c01702c011f5b0c662b2a5f0000241a5f151104245f0713003fe13f33e112392fe10110f6e15d5d10
e6322f2b10e112392f3130012115140206062322262602353412362433321617072e0323220e0215141e0233323e0235210317023b428ede9c9df2a6565fb6010bab6fcc58482453585d2e7abc7f4236
70ab756c955d29fe8802f656a8fefeaf5b69c30117aeac0116c3692c2aa2111e170e5198da8982d89c564277a66400010071ffec047b045e00280081405a28280c27470114400c1148141401572a142a
242a02e42af42a02d02a0102402a502a802aa02ac02a051d480c562928500040151948cb00db00eb0003a900b900028f009f00020b001b002b000306000022185015111022500716003fe13f33e11239
2f5e5d5d5d5d2be10110f6e15d5f5d5d7110e6322f2b10e112392f3130012115140e0223222e0235343e023332161707262623220e0215141e0233323e023521027b02003978ba8187cb88444c95db8f
6fad4d3e3c945b619565342956875e51744a23feb8024a4482c889474f93d38482d3945025278d1d293a6d9d625d9b703e2d5070440000010014ffec04c905b600190065402f065a09651b4f1b7f1b9f
1baf1b04001b01af18ef18028418011818005a134015010f15010815155713671377130313b8ffc04010070a481319155f1607071603035f0e13003fe13f392f10e132012f2b5d332f5e5d5d10e1322f
5d5d5d5d10f6e1313001141633323635113311140e0223222e02351121352115210271626f6e62b7386690595891693afe5e03fefe5f01878177778101c9fe2d6998622e2c60996c0395a4a400010029
ffec046f044a001b006d404a0d4710551d0f1d01cf1def1dff1d03301d401d601d030f1d1f1d020f0301cc03010f03bf030206030305471a60007000d000031f00010000001a101a021a0e0e010a5015
16040050010f003fe1323fe111392f012f5d332f5d5d10e1322f5e5d5d715d5d5d7110f6e13130133521152111141e0233323635113311140e0223222e023511290335fec11531503b695fb72f5f8f60
5c92663603b09a9afdd74162412082820135fec16998622e2e62966902350001006fffec040c05cb0039005db4135b341a00b8ffc0402e171a48001a001a2f0a0a273b9f3b01205b2f663a341b60aa18
017818010f1801081818052360262a130e600b0504003f33e13f33e111392f5e5d5d5de1390110f6e15d10ce322f1239392f2f2b1239e1313013343e0233321e021707262623220e0215141e02333315
23220e021514163332363715060623222e0235343e0237352e039c3b73a96d487b695a265c4ba267395f44262c578257c3bf5d926535aaa46ccc5353c08080c38342315f89574a78542d04604f856136
1829361f7d36431e3a53363e6448279721436543898e312baa26253c6f9c60487b5e3d0b060c395877000001005cffec036a045e0037006db432461e0123b8ffc0403d171a4823012301182b0f39d039
e039f039038f390140390105471856381d0250ef3701a937b937020f371f372f37030637370a2f502c28100a500f1316003f33e13f33e112392f5e5d5d5de1390110f6e15d5d5d10ce321139392f2f2b
1239e13130011523220615141e0233323e023715060623222e0235343e0237352e0335343e023332161707262623220615141e0233029e818a85294660373361574d1e3caf736ea26b35284359302b49
351e3a6990565aa2503f4b8347666d26445f390287995b5e33452a120e161e10a21d28305678473d5b40290d0b0e293b4d32466d4a2625279322264d4d2d40271200ffff0000fe6d052f05b6022601b5
00000107038003b80000000db4010131310001103c1135003500ffff0010fe6d044c044a022601d500000107038102d50000000db4010126260001103c1135003500ffff0000fe9804dd05bc02260024
00000107026704d300000013400b021814270200151b040725012b35002b3500ffff005efe98039c045e02260044000001070267046f00000013400b02361727020d33390c2225012b35002b3500ffff
0000000004dd07e10226002400000107026604d101520013400b021b052602212a15040725012b35002b3500ffff005effec039c068f02260044000001070266047500000013400b0239112602364833
0c2225012b35002b3500ffff0000000004dd07d10226002400000107037704cb01520019b603021505260302b8ffffb41d15040725012b3535002b353500ffff005effec0447067f0226004400000107
0377048900000017400d03023311260302333b330c2225012b3535002b353500ffff0000000004dd07d10226002400000107037804cb01520019b603021d05260302b8ffffb4251d040725012b353500
2b353500ffff0012ffec039c067f02260044000001070378048900000017400d03023b1126030233433b0c2225012b3535002b353500ffff0000000004dd084a0226002400000107037904cb01520019
b603021505260302b8ffffb41d15040725012b3535002b353500ffff005effec040606f802260044000001070379048900000017400d03023311260302333b330c2225012b3535002b353500ffff0000
000004dd08620226002400000107037a04cb01520019b603021d05260302b8ffffb4251d040725012b3535002b353500ffff005effec039c07100226004400000107037a048900000017400d03023b11
2603020b3b330c2225012b3535002b353500ffff0000fe9804dd07730226002400000027014b001f01520107026704d3000000254010032d14270215052603002a3004072502b8ffffb41d1504072501
2b352b35002b352b3500ffff005efe98039c06210226004400000026014bd40001070267046f000000224016034b172702331126030d484e0c222502253b330c2225012b352b35002b352b35ffff0000
000004dd08130226002400000107037b04cb01520017400d03022605260302002921040725012b3535002b353500ffff005effec039c06c10226004400000107037b047b00000017400d030244112603
021e473f0c2225012b3535002b353500ffff0000000004dd08130226002400000107037c04cb01520017400d03022605260302002921040725012b3535002b353500ffff005effec039c06c102260044
00000107037c047b00000017400d030244112603021e473f0c2225012b3535002b353500ffff0000000004dd08580226002400000107037d04cb01520017400d03021a05260302001d15040725012b35
35002b353500ffff005effec039c07060226004400000107037d047b00000017400d030238112603021e3b330c2225012b3535002b353500ffff0000000004dd08620226002400000107037e04cb0152
0017400d03021a05260302001d15040725012b3535002b353500ffff005effec039c07100226004400000107037e047b00000017400d030238112603021e505e0c2225012b3535002b353500ffff0000
fe9804dd075b0226002400000027014e0021016d0107026704d3000000224016032e1427021a052603002b3104072502001f15040725012b352b35002b352b35ffff005efe98039c05ee022600440000
0026014ed40001070267046f000000224016034c172702381126030d494f0c2225021e3d330c2225012b352b35002b352b35ffff00c7fe9803be05b60226002800000107026704aa00000013400b010f
142701030c12010025012b35002b3500ffff0071fe9803e1045e0226004800000107026704be00000013400b022b17270230282e050f25012b35002b3500ffff00c7000003be07e10226002800000107
0266049101520013400b01120526010d210c010025012b35002b3500ffff0071ffec03e1068f02260048000001070266049800000013400b022e1126022d3d28050f25012b35002b3500ffff00c70000
03be073502260028000001070152ffd401520015b40114052601b8fffbb41523010025012b35002b3500ffff0071ffec03e105e302260048000001060152de000013400b02301126021e313f050f2501
2b35002b3500ffff00c70000045c07d102260028000001070377049e01520019b602010c05260201b8fffdb4140c010025012b3535002b353500ffff0071ffec0447067f022600480000010703770489
00000017400d03022811260302033028050f25012b3535002b353500ffff0027000003be07d102260028000001070378049e01520019b602011405260201b8fffdb41c14010025012b3535002b353500
ffff0012ffec03e1067f02260048000001070378048900000017400d03023011260302033830050f25012b3535002b353500ffff00c70000041b084a02260028000001070379049e01520019b602010c
05260201b8fffdb4140c010025012b3535002b353500ffff0071ffec040606f802260048000001070379048900000017400d03022811260302033028050f25012b3535002b353500ffff00c7000003be
08620226002800000107037a049e01520019b602011405260201b8fffdb41c14010025012b3535002b353500ffff0071ffec03e107100226004800000107037a048900000017400d0302301126030203
3830050f25012b3535002b353500ffff00c7fe9803be07730226002800000027014bfff101520107026704aa00000025401002241427010c05260203212701002501b8fffdb4140c010025012b352b35
002b352b3500ffff0071fe9803e106210226004800000026014bde000107026704aa0000002240160340172702281126031c3d43050f2502033028050f25012b352b35002b352b35ffff005200000264
07e10226002c00000107026603b001520013400b011205260113210c010025012b35002b3500ffff0062000001d7068f022600f3000001070266035e00000013400b010a112601131904010025012b35
002b3500ffff0052fe98026405b60226002c00000107026703be00000015b4010f142701b8ffffb40c12010025012b35002b3500ffff009efe98017505e50226004c000001070267036d00000015b402
15172702b8fffeb41218040c25012b35002b3500ffff007dfe98057105cd02260032000001070267055c00000013400b022b14270200282e0a0025012b35002b3500ffff0071fe98042d045e02260052
00000107026704b200000015b40223172702b8ffffb420260a0025012b35002b3500ffff007dffec057107e102260032000001070266054e01520013400b022e052602153d280a0025012b35002b3500
ffff0071ffec042d068f0226005200000107026604a400000013400b02261126021335200a0025012b35002b3500ffff007dffec057107d102260032000001070377055801520017400d030228052603
020530280a0025012b3535002b353500ffff0071ffec0468067f0226005200000107037704aa00000019b603022011260302b8fffab428200a0025012b3535002b353500ffff007dffec057107d10226
0032000001070378055801520017400d030230052603020538300a0025012b3535002b353500ffff0033ffec042d067f0226005200000107037804aa00000019b603022811260302b8fffab430280a00
25012b3535002b353500ffff007dffec0571084a02260032000001070379055801520017400d030228052603020530280a0025012b3535002b353500ffff0071ffec042d06f802260052000001070379
04aa00000019b603022011260302b8fffab428200a0025012b3535002b353500ffff007dffec057108620226003200000107037a055801520017400d030230052603020538300a0025012b3535002b35
3500ffff0071ffec042d07100226005200000107037a04aa00000019b603022811260302b8fffab430280a0025012b3535002b353500ffff007dfe98057107730226003200000027014b00ae01520107
0267055c000000224016034014270228052603003d430a0025020530280a0025012b352b35002b352b35ffff0071fe98042d06210226005200000026014bfb000107026704b200000028400903381727
0220112603b8ffffb5353b0a002502b8fffab428200a0025012b352b35002b352b35ffff007dffec062507730226025f000001070076010601520013400b024105260202353b130125012b35002b3500
ffff0071ffec04f406210226026000000106007650000015b4023a112602b8ffebb42d330a1925012b35002b3500ffff007dffec062507730226025f000001070043005401520015b40235052602b8ff
51b43b35130125012b35002b3500ffff0071ffec04f4062102260260000001060043d8000015b4022e112602b8ffd7b4342d0a0025012b35002b3500ffff007dffec062507e10226025f000001070266
054e01520015b4023c052602b8ffbcb44a35130125012b35002b3500ffff0071ffec04f4068f0226026000000107026604a400000015b40234112602b8ffb0b4422d0a1925012b35002b3500ffff007d
ffec062507350226025f000001070152007d01520015b4023d052602b8ff96b43e4c130125012b35002b3500ffff0071ffec04f405e302260260000001060152e2000015b4022d112602b8fffdb43644
0a0025012b35002b3500ffff007dfe98062506140226025f000001070267055c00000015b40238142702b8ffa7b4353b130125012b35002b3500ffff0071fe9804f404f20226026000000107026704b2
00000015b40230172702b8ff9bb42d330a1925012b35002b3500ffff00b8fe9804dd05b802260038000001070267052f00000013400b011b14270100181e0b0025012b35002b3500ffff00a4fe980408
044a0226005800000107026704b800000015b4011e172701b8fffeb41b210c1925012b35002b3500ffff00b8ffec04dd07e102260038000001070266051d01520013400b011e052601112d180b002501
2b35002b3500ffff00a4ffec0408068f0226005800000107026604a600000013400b01211126010e301b0c1925012b35002b3500ffff00b8ffec064e07730226026100000107007600c501520015b401
31052601b8ff90b4252b180825012b35002b3500ffff00a4ffec057906210226026200000106007660000011b10101b8ff9fb4282e0c2125012b35003500ffff00b8ffec064e07730226026100000107
0043003d01520015b40125052601b8ff08b42b25180825012b35002b3500ffff00a4ffec0579062102260262000001060043a3000011b10101b8fee2b42e280c2125012b35003500ffff00b8ffec064e
07e102260261000001070266051d01520011b10101b8ff59b43a25180825012b35003500ffff00a4ffec0579068f0226026200000107026604a600000011b10101b8ff56b43d280c2125012b35003500
ffff00b8ffec064e073502260261000001070152006001520015b4012d052601b8ff47b42e3c180825012b35002b3500ffff00a4ffec057905e302260262000001060152f3000011b10101b8ff4eb431
3f0c2125012b35003500ffff00b8fe98064e061402260261000001070267052f00000015b40128142701b8ff48b4252b180825012b35002b3500ffff00a4fe98057904f20226026200000107026704b8
00000015b4012b172701b8ff45b4282e0c2125012b35002b3500ffff0000fe98043705b60226003c000001070267047f00000015b4010c142701b8ffffb4090f070225012b35002b3500ffff000afe14
03df044a0226005c000001070267058b00000011b10101b80132b42329000f25012b35003500ffff00000000043707e10226003c000001070266047101520013400b010f052601141e09070225012b35
002b3500ffff000afe1403df068f0226005c000001070266044a00000013400b0129112601143823000f25012b35002b3500ffff00000000043707350226003c000001070152ffb301520013400b0111
052601011220070225012b35002b3500ffff000afe1403df05e30226005c0000010601528c000013400b012b112601012c3a000f25012b35002b3500ffff0071febc049e0614022600d3000001070042
00b40000000eb90002ffd2b43a390b2325012b350002fbdb04d9feba0621000d001b002d4019140e0e07405000013f004f000200150792800f0f015f010201002f5d331aed32012f5d5d1acd392fcd31
3001232e032735331e031705232e032735331e0317feba6423514d3f10c6102a2f3015fe966423524d3f10c7102a2f301504d91c5358511b152350514c1d1b1c5358511b152350514c1d0002fc6a04d9
ffbe067f0010001c005240310d0c0410170117402f1c3f1c4f1c031c04041c2f005f008f009f000400c008161b1b047f0c8f0c020c80000f085f080208002f5d331acc5d32392fcd012f1acc5d39393d
2f182f5d1acd5d11333331300123262627060607233536363733161617273e0337331506060723fed964336c34366a3365337530c03074334e14211d1d10b42d6a376504d92254303054221b3b844545
843bc2192d2e3421153c672b0002fb8904d9fedd067f0010001c004a402a04030c1f160116401c0c0c1c2f085f088f089f080408c0001612120c7f038f03020380100f085f080208002f5d331acd5d32
392fcd012f1acc5d39393d2f182f1acd5d11333331300136363733161617152326262706060723372326262735331e0317fc6f337430c030743364336a36356b33644d64386a2db4101d1e201404f43b
844545843b1b225430305422c32b673c1521342e2d190002fc6a04d9ff7d06f800100028005e403816191920060e48191c11802f233f234f2303230404232f005f008f009f000400c0081619191f2618
18047f0c8f0c020c80000f085f080208002f5d331acc5d32392fcc32391133012f1acc5d39393d2f182f5d1acd32392b11333130012326262706060723353636373316161713140e0207072327363635
342623220607353636333216fed964336c34366a3365337530c0307433a414222e1906560a3741392b16270b0b261f636404d92254303054221b3b844545843b01761d2b1e15065183091f2525190303
520303450002fc6404d9fee907100010002c0061403b04030c0c0028282f085f088f089f08040800c01a1a0027168ecf1fdf1fef1f031f40090d481f1f248e1911110c7f038f03020380100f085f0802
08002f5d331acd5d32322f33e1332f2b5de133012f332f1a10cc5d322f11393d2f33333130013636373316161715232626270606072301222e0223220607233e0333321e0233323637330e03fc6f3374
30c030743364336a36356b336401be244745401c282a0e5d051d30412a254a453e1a282a0e5c051c304104f43b844545843b1b225430305422015a1c221c2a3230513b211c221c2b3130513b21000002
fc7304d9fecd06c1000b001f0032401906800b0b1f2f0c010cc01514050b0b1f14801a0f115f110211002f5d331acd32322fcc012f331acc5d32392f1acd3130013e0337331506060723250e03232226
27331e0333323e0237fd5e14201e1d10b42d6a3765016f052b4d6e469291066c041b30442c244133210505f8192c2e3521153c672c063c6345268c7e2b321907091b31280002fc7304d9fecd06c1000b
001f0038401e05800b401316480b0b1f2f0c010cc015140500001f14801a0f115f110211002f5d331acd32322fcd012f331acc5d32392f2b1acd3130012326262735331e0317170e0323222627331e03
33323e0237fdd164386a2db4101d1e2014fc052b4d6e469291066c041b30442c244133210505dd2c673c1521352e2c19153c6345268c7e2b321907091b3128000002fc7304d9fecd07060013002a0042
4021191c1c1f14802525132f0001000908191c1c2228c01b1b1308800e0f055f050205002f5d331acd32392f1acc32391133012f33cc5d32392f1acc323911333130010e0323222627331e0333323e02
3727140e02070723273636353426232206073536333216fecd052b4d6e469291066c041b30442c24413321052b14222e1906560a3841392c15270b153a646305e33c6345268c7e2b321907091b312896
1d2b1f1406295a09202525180303520645000002fc6404d9fee907100013002f004840271c1d1d082a2b2b132f000100c009082a19cf22df22ef220322271c14141308800e0f055f050205002f5d331a
cd32322f3333cd5d3232012f331acc5d32322f3311332f333130010e0323222627331e0333323e023727222e0223220607233e0333321e0233323637330e03fecd052b4d6e469291066c041b30442c24
413321052f244745401c282a0e5d051d30412a254a453e1a282a0e5c051c304105e33c6345268c7e2b321907091b3128501c221c2a3230513b211c221c2b3130513b210000010031fe42017100000014
001840090f8000080802120b02002f2f33012f332f331acc3130173427331e0315140623222627351616333236db8b811a392f1e65641d401a0e2d192234ee69851334404b2a5a6808087104052b0001
0010fe6d017700a600110012b60901100d06fa00002f3f33012f33c4313025110e032322262735161633323635110177011a39593f263f160e3b222e32a6fed942664624110996070e3131013c000001
0010fe6d0177009a00110012b60901100d06fa00002f3f33012f33c4313025110e032322262735161633323635110177011a39593f263f160e3b222e329afee542664624110996070e31310130000000
0000001b014a0001000000000000003400000001000000000001000a003400010000000000020007003e0001000000000003001500450001000000000004000a00340001000000000005000c005a0001
000000000006000900660001000000000007004e006f0001000000000008001400bd000100000000000a006700d1000100000000000d002e0138000100000000000e002a01660001000000000012000a
00340003000104090000006801900003000104090001001401f80003000104090002000e020c0003000104090003002a021a0003000104090004001401f80003000104090005002c0244000300010409
0006001202700003000104090007009c028200030001040900080028031e000300010409000a00ce0346000300010409000b00380414000300010409000c005c044c000300010409000d005c04a80003
00010409000e005405044469676974697a6564206461746120636f7079726967687420a920323030372c20476f6f676c6520436f72706f726174696f6e2e44726f69642053616e73526567756c617241
7363656e646572202d2044726f69642053616e7356657273696f6e20312e303044726f696453616e7344726f696420697320612074726164656d61726b206f6620476f6f676c6520616e64206d617920
6265207265676973746572656420696e206365727461696e206a7572697364696374696f6e732e417363656e64657220436f72706f726174696f6e44726f69642053616e7320697320612068756d616e
6973742073616e732073657269662074797065666163652064657369676e656420666f72207573657220696e746572666163657320616e6420656c656374726f6e696320636f6d6d756e69636174696f
6e2e4c6963656e73656420756e6465722074686520417061636865204c6963656e73652c2056657273696f6e20322e30687474703a2f2f7777772e6170616368652e6f72672f6c6963656e7365732f4c
4943454e53452d322e30004400690067006900740069007a006500640020006400610074006100200063006f0070007900720069006700680074002000a900200032003000300037002c00200047006f
006f0067006c006500200043006f00720070006f0072006100740069006f006e002e00440072006f00690064002000530061006e00730052006500670075006c006100720041007300630065006e0064
006500720020002d002000440072006f00690064002000530061006e007300560065007200730069006f006e00200031002e003000300020006200750069006c0064002000310031003300440072006f
0069006400530061006e007300440072006f0069006400200069007300200061002000740072006100640065006d00610072006b0020006f006600200047006f006f0067006c006500200061006e0064
0020006d006100790020006200650020007200650067006900730074006500720065006400200069006e0020006300650072007400610069006e0020006a007500720069007300640069006300740069
006f006e0073002e0041007300630065006e00640065007200200043006f00720070006f0072006100740069006f006e00440072006f00690064002000530061006e0073002000690073002000610020
00680075006d0061006e006900730074002000730061006e0073002000730065007200690066002000740079007000650066006100630065002000640065007300690067006e0065006400200066006f
00720020007500730065007200200069006e0074006500720066006100630065007300200061006e006400200065006c0065006300740072006f006e0069006300200063006f006d006d0075006e0069
0063006100740069006f006e002e0068007400740070003a002f002f007700770077002e0061007300630065006e0064006500720063006f00720070002e0063006f006d002f0068007400740070003a
002f002f007700770077002e0061007300630065006e0064006500720063006f00720070002e0063006f006d002f007400790070006500640065007300690067006e006500720073002e00680074006d
006c004c006900630065006e00730065006400200075006e006400650072002000740068006500200041007000610063006800650020004c006900630065006e00730065002c00200056006500720073
0069006f006e00200032002e00300068007400740070003a002f002f007700770077002e006100700061006300680065002e006f00720067002f006c006900630065006e007300650073002f004c0049
00430045004e00530045002d0032002e003000000002000000000000ff660066000000000000000000000000000000000000000003820000010200020003000400050006000700080009000a000b000c
000d000e000f0010001100120013001400150016001700180019001a001b001c001d001e001f0020002100220023002400250026002700280029002a002b002c002d002e002f00300031003200330034
00350036003700380039003a003b003c003d003e003f0040004100420043004400450046004700480049004a004b004c004d004e004f0050005100520053005400550056005700580059005a005b005c
005d005e005f0060006100ac00a30084008500bd009600e80086008e008b009d00a900a40103008a01040083009300f200f3008d00970088010500de00f1009e00aa00f500f400f600a200ad00c900c7
00ae006200630090006400cb006500c800ca00cf00cc00cd00ce00e9006600d300d000d100af006700f0009100d600d400d5006800eb00ed0089006a0069006b006d006c006e00a0006f007100700072
0073007500740076007700ea0078007a0079007b007d007c00b800a1007f007e0080008100ec00ee00ba0106010701080109010a010b00fd00fe010c010d010e010f00ff010001100111011201130114
01150116011701180119011a011b011c011d011e011f00f800f90120012101220123012401250126012701280129012a012b012c012d012e012f013000d7013101320133013401350136013701380139
013a013b013c013d013e013f00e200e30140014101420143014401450146014701480149014a014b014c014d014e00b000b1014f01500151015201530154015501560157015800fb00fc00e400e50159
015a015b015c015d015e015f0160016101620163016401650166016701680169016a016b016c016d016e00bb016f01700171017200e600e7017300a6017401750176017701780179017a017b00d800e1
017c00db00dc00dd00e000d900df017d017e017f0180018101820183018401850186018701880189018a018b018c018d018e018f0190019101920193019401950196019701980199019a019b019c019d
019e019f01a001a101a201a301a401a501a601a701a801a901aa01ab01ac01ad01ae01af01b001b101b201b301b401b5009b01b601b701b801b901ba01bb01bc01bd01be01bf01c001c101c201c301c4
01c501c601c701c801c901ca01cb01cc01cd01ce01cf01d001d101d201d301d401d501d601d701d801d901da01db01dc01dd01de01df01e001e101e201e301e401e501e601e701e801e901ea01eb01ec
01ed01ee01ef01f001f101f201f301f401f501f601f701f801f901fa01fb01fc01fd01fe01ff0200020102020203020402050206020702080209020a020b020c020d020e020f02100211021202130214
02150216021702180219021a021b021c021d021e021f022002210222022302240225022602270228022900b200b3022a022b00b600b700c4022c00b400b500c5008200c2008700ab00c6022d022e00be
00bf022f00bc023000f7023102320233023402350236008c009f023702380239023a023b0098023c009a009900ef00a50092009c00a7008f0094009500b9023d023e023f024002410242024302440245
0246024702480249024a024b024c024d024e024f0250025102520253025402550256025702580259025a025b025c025d025e025f0260026102620263026402650266026702680269026a026b026c026d
026e026f0270027102720273027402750276027702780279027a027b027c027d027e027f0280028102820283028402850286028702880289028a028b028c028d028e028f029002910292029302940295
0296029702980299029a029b029c029d029e029f02a002a102a202a302a402a502a602a702a802a902aa02ab02ac02ad02ae02af02b002b102b202b302b402b502b602b702b802b902ba02bb02bc02bd
02be02bf02c002c102c202c302c402c502c602c702c802c902ca02cb02cc02cd02ce02cf02d002d102d202d302d402d502d602d702d802d902da02db02dc02dd02de02df02e002e102e202e302e402e5
02e602e702e802e902ea02eb02ec02ed02ee02ef02f002f102f202f302f402f502f602f702f802f902fa02fb02fc02fd02fe02ff0300030103020303030403050306030703080309030a030b030c030d
030e030f0310031103120313031403150316031703180319031a031b031c031d031e031f0320032103220323032403250326032703280329032a032b032c032d032e032f033003310332033303340335
0336033703380339033a033b033c033d033e033f0340034103420343034403450346034703480349034a034b034c034d034e034f0350035103520353035403550356035703580359035a035b035c035d
035e035f0360036103620363036403650366036703680369036a036b036c036d036e036f0370037103720373037403750376037703780379037a037b037c037d037e037f038003810382038303840385
0386038703880389038a052e6e756c6c07756e6930304144096f76657273636f72650e706572696f6463656e746572656407416d6163726f6e07616d6163726f6e064162726576650661627265766507
416f676f6e656b07616f676f6e656b0b4363697263756d666c65780b6363697263756d666c65780443646f740463646f7406446361726f6e06646361726f6e064463726f6174066463726f617407456d
6163726f6e07656d6163726f6e06456272657665066562726576650a45646f74616363656e740a65646f74616363656e7407456f676f6e656b07656f676f6e656b06456361726f6e06656361726f6e0b
4763697263756d666c65780b6763697263756d666c65780447646f740467646f740c47636f6d6d61616363656e740c67636f6d6d61616363656e740b4863697263756d666c65780b6863697263756d66
6c657804486261720468626172064974696c6465066974696c646507496d6163726f6e07696d6163726f6e064962726576650669627265766507496f676f6e656b07696f676f6e656b0a49646f746163
63656e7402494a02696a0b4a63697263756d666c65780b6a63697263756d666c65780c4b636f6d6d61616363656e740c6b636f6d6d61616363656e740c6b677265656e6c616e646963064c6163757465
066c61637574650c4c636f6d6d61616363656e740c6c636f6d6d61616363656e74064c6361726f6e066c6361726f6e044c646f74046c646f74064e6163757465066e61637574650c4e636f6d6d616163
63656e740c6e636f6d6d61616363656e74064e6361726f6e066e6361726f6e0b6e61706f7374726f70686503456e6703656e67074f6d6163726f6e076f6d6163726f6e064f6272657665066f62726576
650d4f68756e676172756d6c6175740d6f68756e676172756d6c61757406526163757465067261637574650c52636f6d6d61616363656e740c72636f6d6d61616363656e7406526361726f6e06726361
726f6e06536163757465067361637574650b5363697263756d666c65780b7363697263756d666c65780c54636f6d6d61616363656e740c74636f6d6d61616363656e7406546361726f6e06746361726f
6e04546261720474626172065574696c6465067574696c646507556d6163726f6e07756d6163726f6e0655627265766506756272657665055572696e67057572696e670d5568756e676172756d6c6175
740d7568756e676172756d6c61757407556f676f6e656b07756f676f6e656b0b5763697263756d666c65780b7763697263756d666c65780b5963697263756d666c65780b7963697263756d666c657806
5a6163757465067a61637574650a5a646f74616363656e740a7a646f74616363656e74056c6f6e67730a4172696e6761637574650a6172696e676163757465074145616375746507616561637574650b
4f736c61736861637574650b6f736c61736861637574650c53636f6d6d61616363656e740c73636f6d6d61616363656e74066d6163726f6e05746f6e6f730d6469657265736973746f6e6f730a416c70
6861746f6e6f7309616e6f74656c6569610c457073696c6f6e746f6e6f7308457461746f6e6f7309496f7461746f6e6f730c4f6d6963726f6e746f6e6f730c557073696c6f6e746f6e6f730a4f6d6567
61746f6e6f7311696f74616469657265736973746f6e6f7305416c70686104426574610547616d6d6107756e693033393407457073696c6f6e045a6574610345746105546865746104496f7461054b61
707061064c616d626461024d75024e75025869074f6d6963726f6e0250690352686f055369676d610354617507557073696c6f6e03506869034368690350736907756e69303341390c496f7461646965
72657369730f557073696c6f6e64696572657369730a616c706861746f6e6f730c657073696c6f6e746f6e6f7308657461746f6e6f7309696f7461746f6e6f7314757073696c6f6e6469657265736973
746f6e6f7305616c70686104626574610567616d6d610564656c746107657073696c6f6e047a6574610365746105746865746104696f7461056b61707061066c616d62646107756e6930334243026e75
027869076f6d6963726f6e0372686f067369676d6131057369676d610374617507757073696c6f6e037068690363686903707369056f6d6567610c696f746164696572657369730f757073696c6f6e64
696572657369730c6f6d6963726f6e746f6e6f730c757073696c6f6e746f6e6f730a6f6d656761746f6e6f73096166696931303032330961666969313030353109616669693130303532096166696931
3030353309616669693130303534096166696931303035350961666969313030353609616669693130303537096166696931303035380961666969313030353909616669693130303630096166696931
3030363109616669693130303632096166696931303134350961666969313030313709616669693130303138096166696931303031390961666969313030323009616669693130303231096166696931
3030323209616669693130303234096166696931303032350961666969313030323609616669693130303237096166696931303032380961666969313030323909616669693130303330096166696931
3030333109616669693130303332096166696931303033330961666969313030333409616669693130303335096166696931303033360961666969313030333709616669693130303338096166696931
3030333909616669693130303430096166696931303034310961666969313030343209616669693130303433096166696931303034340961666969313030343509616669693130303436096166696931
3030343709616669693130303438096166696931303034390961666969313030363509616669693130303636096166696931303036370961666969313030363809616669693130303639096166696931
3030373009616669693130303732096166696931303037330961666969313030373409616669693130303735096166696931303037360961666969313030373709616669693130303738096166696931
3030373909616669693130303830096166696931303038310961666969313030383209616669693130303833096166696931303038340961666969313030383509616669693130303836096166696931
3030383709616669693130303838096166696931303038390961666969313030393009616669693130303931096166696931303039320961666969313030393309616669693130303934096166696931
3030393509616669693130303936096166696931303039370961666969313030373109616669693130303939096166696931303130300961666969313031303109616669693130313032096166696931
3031303309616669693130313034096166696931303130350961666969313031303609616669693130313037096166696931303130380961666969313031303909616669693130313130096166696931
3031393309616669693130303530096166696931303039380657677261766506776772617665065761637574650677616375746509576469657265736973097764696572657369730659677261766506
796772617665096166696930303230380d756e64657273636f726564626c0d71756f74657265766572736564066d696e757465067365636f6e64096578636c616d64626c096e7375706572696f720961
666969303839343106706573657461044575726f09616669693631323438096166696936313238390961666969363133353209657374696d61746564096f6e656569676874680c746872656565696768
7468730b66697665656967687468730c736576656e656967687468730544656c746107756e694642303107756e69464230320d637972696c6c6963627265766508646f746c6573736a106361726f6e63
6f6d6d61616363656e740b636f6d6d61616363656e7411636f6d6d61616363656e74726f746174650c7a65726f7375706572696f720c666f75727375706572696f720c666976657375706572696f720b
7369787375706572696f720d736576656e7375706572696f720d65696768747375706572696f720c6e696e657375706572696f7207756e693230303007756e693230303107756e693230303207756e69
3230303307756e693230303407756e693230303507756e693230303607756e693230303707756e693230303807756e693230303907756e693230304107756e693230304207756e694645464607756e69
4646464307756e694646464407756e693031463007756e693032424307756e693033443107756e693033443207756e693033443607756e693145334507756e693145334607756e693145303007756e69
3145303107756e693146344407756e69303246330964617369616f78696107756e694642303307756e6946423034054f686f726e056f686f726e0555686f726e0575686f726e07756e69303330300775
6e693033303107756e693033303304686f6f6b08646f7462656c6f7707756e693034303007756e693034304407756e693034353007756e693034354407756e693034363007756e693034363107756e69
3034363207756e693034363307756e693034363407756e693034363507756e693034363607756e693034363707756e693034363807756e693034363907756e693034364107756e693034364207756e69
3034364307756e693034364407756e693034364507756e693034364607756e693034373007756e693034373107756e693034373207756e693034373307756e693034373407756e693034373507756e69
3034373607756e693034373707756e693034373807756e693034373907756e693034374107756e693034374207756e693034374307756e693034374407756e693034374507756e693034374607756e69
3034383007756e693034383107756e693034383207756e693034383307756e693034383407756e693034383507756e693034383607756e693034383807756e693034383907756e693034384107756e69
3034384207756e693034384307756e693034384407756e693034384507756e693034384607756e693034393207756e693034393307756e693034393407756e693034393507756e693034393607756e69
3034393707756e693034393807756e693034393907756e693034394107756e693034394207756e693034394307756e693034394407756e693034394507756e693034394607756e693034413007756e69
3034413107756e693034413207756e693034413307756e693034413407756e693034413507756e693034413607756e693034413707756e693034413807756e693034413907756e693034414107756e69
3034414207756e693034414307756e693034414407756e693034414507756e693034414607756e693034423007756e693034423107756e693034423207756e693034423307756e693034423407756e69
3034423507756e693034423607756e693034423707756e693034423807756e693034423907756e693034424107756e693034424207756e693034424307756e693034424407756e693034424507756e69
3034424607756e693034433007756e693034433107756e693034433207756e693034433307756e693034433407756e693034433507756e693034433607756e693034433707756e693034433807756e69
3034433907756e693034434107756e693034434207756e693034434307756e693034434407756e693034434507756e693034434607756e693034443007756e693034443107756e693034443207756e69
3034443307756e693034443407756e693034443507756e693034443607756e693034443707756e693034443807756e693034443907756e693034444107756e693034444207756e693034444307756e69
3034444407756e693034444507756e693034444607756e693034453007756e693034453107756e693034453207756e693034453307756e693034453407756e693034453507756e693034453607756e69
3034453707756e693034453807756e693034453907756e693034454107756e693034454207756e693034454307756e693034454407756e693034454507756e693034454607756e693034463007756e69
3034463107756e693034463207756e693034463307756e693034463407756e693034463507756e693034463607756e693034463707756e693034463807756e693034463907756e693034464107756e69
3034464207756e693034464307756e693034464407756e693034464507756e693034464607756e693035303007756e693035303107756e693035303207756e693035303307756e693035303407756e69
3035303507756e693035303607756e693035303707756e693035303807756e693035303907756e693035304107756e693035304207756e693035304307756e693035304407756e693035304507756e69
3035304607756e693035313007756e693035313107756e693035313207756e693035313307756e693145413007756e693145413107756e693145413207756e693145413307756e693145413407756e69
3145413507756e693145413607756e693145413707756e693145413807756e693145413907756e693145414107756e693145414207756e693145414307756e693145414407756e693145414507756e69
3145414607756e693145423007756e693145423107756e693145423207756e693145423307756e693145423407756e693145423507756e693145423607756e693145423707756e693145423807756e69
3145423907756e693145424107756e693145424207756e693145424307756e693145424407756e693145424507756e693145424607756e693145433007756e693145433107756e693145433207756e69
3145433307756e693145433407756e693145433507756e693145433607756e693145433707756e693145433807756e693145433907756e693145434107756e693145434207756e693145434307756e69
3145434407756e693145434507756e693145434607756e693145443007756e693145443107756e693145443207756e693145443307756e693145443407756e693145443507756e693145443607756e69
3145443707756e693145443807756e693145443907756e693145444107756e693145444207756e693145444307756e693145444407756e693145444507756e693145444607756e693145453007756e69
3145453107756e693145453207756e693145453307756e693145453407756e693145453507756e693145453607756e693145453707756e693145453807756e693145453907756e693145454107756e69
3145454207756e693145454307756e693145454407756e693145454507756e693145454607756e693145463007756e693145463107756e693145463407756e693145463507756e693145463607756e69
3145463707756e693145463807756e693145463907756e693230414207756e69303330461363697263756d666c65786163757465636f6d621363697263756d666c65786772617665636f6d6212636972
63756d666c6578686f6f6b636f6d621363697263756d666c657874696c6465636f6d620e62726576656163757465636f6d620e62726576656772617665636f6d620d6272657665686f6f6b636f6d620e
627265766574696c6465636f6d6210637972696c6c6963686f6f6b6c65667411637972696c6c6963626967686f6f6b554311637972696c6c6963626967686f6f6b4c43000000000200050002ffff0003
00010000000c000000160000000200010000038100010004000000010000000000010000000a001e002c00016c61746e0008000400000000ffff0001000000016b65726e000800000001000000010004
00020008000100080001a53a0004000001f003ea03ea074c075a08d4075a09320ac00b220c100d3e0e240f0a0ff0117e12ac138214b014de196019ee1d90213623682782074c28682868286e28682880
291a293029362868286829482956286e29d429ea29ea2a002ada2af0074c2bba2bc82bd62e242e242bd6306632ac0b2234fe35e434fe34fe0e240e240e240e2436c237443744374437443744117e389e
389e389e389e39a82868286828682868286828682948286e3e623e623e623e623e6c3e7a3e7a3e7a3e7a3e7a29483e943e943e943e943e9e40240932286809322868404228680b22286e286e286e0b22
286e41d42868286828684252286828680e2442580e240f0a29360ff00ff00ff041d40ff00ff0286828682868117e29482948117e29482948286e286e286e4342286e444c444c41d41960196019601960
1d9029ea23682ada48c2278227824d38504e09322868286e511c514a5158518652185226528c53ca542855ee58b858da59f45a0a5ab45df25e005f1a5ff4609a644064f66520657e65b065be66706a2a
6a3028686a4a6a2a6a9c6aa26ad06ada6b0828686b7a6bbc6be26c186c366c446c5a6c446c6c6c8a6cc46cd629486d2c6d3e294829486d8c6e6e6f6472b67334734a73b474927564762a76a0785679f4
7c1a7cc47d4a7f888092815881f28324847e8594869e8be88d768e208eda8f9c9096911091929220929692fc938294149432947494aa952c95a2962896b29700975e982098b29924999a99f09a4a286e
9ab09b229b68286e9baa9bb8286e286e9c2a9c349cba9d389d3e9db09e2694141d9029ea1d9029ea1d9029ea23682adaa190a190a19ea19e09320932a4aca4ee947483249820911096288594970079f4
79f4809294aa809294aa83249628847e832496288be898208be8982098209820832496288324962883249820a5308eda999a91109628847e9628869e975e762a8eda999a96288eda999a809294aa8092
94aa809294aa8eda999a869e975e762a8324962885949700869e975e869e9820869e98208eda999a8eda999a8eda999a762a83248324734a809294aa762a8eda999a8eda999a8eda999a734a79f479f4
947494748324962883249628809294aa832496288324832496288324962883249628832496288be898208be898208be89820869e975e869e975e8eda999a809294aa832496288be896288eda999a8324
9628832496288324962883249628859497008eda999a093209320932093209320932093209320932093209320932286828682868286828682868286828680e240e24117e2948117e2948117e2948117e
2948117e2948117e2948117e2948a4aca4aca4aca4aca4ac19601960a4eea4eea4eea4eea4ee23682ada23682ada23682ada00d80024ffae002c00290037005200390052003a0066003b0029003c0052
003d00290046ffc30047ffc30048ffc3004affd70052ffc30054ffc30057002900590029005a0014005c00290082ffae0083ffae0084ffae0085ffae0086ffae0087ffae0088ff5c008e0029008f0029
0090002900910029009f005200a8ffc300a9ffc300aaffc300abffc300acffc300adffc300b4ffc300b5ffc300b6ffc300b7ffc300b8ffc300baffc300bf002900c1002900c2ffae00c4ffae00c6ffae
00c9ffc300cbffc300cdffc300cfffc300d5ffc300d7ffc300d9ffc300dbffc300ddffc300ec002900f0002900f20029010fffc30111ffc30113ffc30115ffc301240052012600520136006601370014
0138005201390029013a0052013b0029013d0029013f00290143ffae015fffae0169ffae017100520179ffc3017effc3018000290182ffc3018a0029018cffc3018effc30190ffc3019100290193ffc3
019400290196ffc30199ffc3019bffc3019d005201a4ff9a01a6005201a8003d01aaffae01aeff8501b0003d01b1001401b5ff8501bc005201bd003d01bf002901c4005201cfffc301d8ffc301dbffc3
01dc002901dd002901deffc301eaffc301edffc301fa006601fb001401fc006601fd001401fe006601ff001402000052020100290228ffae0258ffae0260ffc3026affc3026d00290272ffae0276003d
027fffc302810029028300290285ffc30287ffc30289ffc3028b0029028dffc3029f003d02a9005202aa002902b2ffc302b4ffc302b5005202b6002902b7005202b8002902b9005202ba002902bb003d
02bd005202ca003d02ceff8502d9ffae02dbffae02ddffae02e0ffc302e5003d02f0ffc302f2ffc302f4ffc302f7003d02f8002902f9003d02fa002902fb003d02fc00290305003d0307003d030affc3
030cffc3030e0029031000290311ff850316ffc30317005203180029031affc3031bff85031dffae031fffae0321ffae0323ffae0325ffae0327ffae0329ffae032bffae032dffae032fffae0331ffae
0333ffae0336ffc30338ffc3033affc3033cffc3033effc30340ffc30342ffc30344ffc30345002903470029034affc3034cffc3034effc30350ffc30352ffc30354ffc30356ffc30358ffc3035affc3
035cffc3035effc30360ffc3036f005203700029037100520372002903730052037400290003002d007b00f6007b01a3007b005e0026ffc3002affc30032ffc30034ffc30037ff9a0038ffd70039ff9a
003affae003cff9a0089ffc30094ffc30095ffc30096ffc30097ffc30098ffc3009affc3009bffd7009cffd7009dffd7009effd7009fff9a00c8ffc300ceffc300deffc300e0ffc300e2ffc300e4ffc3
010effc30112ffc30124ff9a0126ff9a012cffd70130ffd70132ffd70134ffd70136ffae0138ff9a013aff9a0166ffc3016dffc30171ff9a01b8ffc301bbffc301bcff9a01faffae01fcffae01feffae
0200ff9a025fffc30261ffd7026cffc3027effc30284ffc30286ffc30288ffc3028affc3028cffc302a9ff9a02b1ffc302b3ffc302b5ff9a02b7ff9a02b9ff9a02bdff9a02e1ffc302e3ffc302efffc3
02f1ffc302f3ffc30315ffc30317ff9a0319ffc30349ffc3034bffc3034dffc3034fffc30351ffc30353ffc30355ffc30357ffc30359ffc3035bffc3035dffc3035fffc30361ffd70363ffd70365ffd7
0367ffd70369ffd7036bffd7036dffd7036fff9a0371ff9a0373ff9a00170037ff9a0171ff9a0172ffc301b0ffd701b5ffd701bcff9a01c4ffae0276ffd7029fffd702a9ff9a02b5ff9a02b7ff9a02b9
ff9a02bbffd702bdff9a02caffd702ceffd702e5ffd70305ffd70307ffd70311ffd70317ff9a031bffd700630005ffae000affae0026ffec002affec0032ffec0034ffec0037ff850038ffec0039ffc3
003affd7003cff9a0089ffec0094ffec0095ffec0096ffec0097ffec0098ffec009affec009bffec009cffec009dffec009effec009fff9a00c8ffec00ceffec00deffec00e0ffec00e2ffec00e4ffec
010effec0112ffec0114ffd70124ff850126ff85012cffec0130ffec0132ffec0134ffec0136ffd70138ff9a013aff9a0166ffec016dffec0171ff8501b8ffec01bbffec01bcff8501faffd701fcffd7
01feffd70200ff9a0207ffae020bffae025fffec0261ffec026cffec027effec0284ffec0286ffec0288ffec028affec028cffec02a9ff8502b1ffec02b3ffec02b5ff8502b7ff8502b9ff8502bdff85
02e1ffec02e3ffec02efffec02f1ffec02f3ffec0315ffec0317ff850319ffec0349ffec034bffec034dffec034fffec0351ffec0353ffec0355ffec0357ffec0359ffec035bffec035dffec035fffec
0361ffec0363ffec0365ffec0367ffec0369ffec036bffec036dffec036fff9a0371ff9a0373ff9a0018002cffec0037ffec0039ffec003bffec003cffec009fffec0124ffec0126ffec0138ffec013a
ffec0171ffec01bcffec0200ffec02a9ffec02b5ffec02b7ffec02b9ffec02bdffec0317ffec0345ffec0347ffec036fffec0371ffec0373ffec003b00050029000a0029000c00290026ffd7002affd7
0032ffd70034ffd700400029006000290089ffd70094ffd70095ffd70096ffd70097ffd70098ffd7009affd700c8ffd700ceffd700deffd700e0ffd700e2ffd700e4ffd7010effd70112ffd70166ffd7
016dffd701b8ffd701bbffd702070029020b0029025fffd7026cffd7027effd70284ffd70286ffd70288ffd7028affd7028cffd702b1ffd702b3ffd702e1ffd702e3ffd702efffd702f1ffd702f3ffd7
0315ffd70319ffd70349ffd7034bffd7034dffd7034fffd70351ffd70353ffd70355ffd70357ffd70359ffd7035bffd7035dffd7035fffd7004b000fffc30011ffc30024ffec002cffec0037ffc30039
ffec003affec003bffec003cffd7003dffec0082ffec0083ffec0084ffec0085ffec0086ffec0087ffec0088ffc3008effec008fffec0090ffec0091ffec009fffd700c2ffec00c4ffec00c6ffec00ec
ffec00f0ffec00f2ffec0124ffc30126ffc30136ffec0138ffd7013affd7013bffec013dffec013fffec0143ffec015fffec0169ffec0171ffc301aaffec01bcffc301faffec01fcffec01feffec0200
ffd70228ffec0258ffec0272ffec02a9ffc302b5ffc302b7ffc302b9ffc302bdffc302d9ffec02dbffec02ddffec0317ffc3031dffec031fffec0321ffec0323ffec0325ffec0327ffec0329ffec032b
ffec032dffec032fffec0331ffec0333ffec0345ffec0347ffec036fffd70371ffd70373ffd700390005003d000a003d000c0029000fff9a0011ff9a002200290024ffd700390014003a0014003c0014
00400029006000290082ffd70083ffd70084ffd70085ffd70086ffd70087ffd70088ffc3009f001400c2ffd700c4ffd700c6ffd70136001401380014013a00140143ffd7015fffd70169ffd701aaffd7
01fa001401fc001401fe0014020000140207003d020b003d0228ffd70258ffd70272ffd702d9ffd702dbffd702ddffd7031dffd7031fffd70321ffd70323ffd70325ffd70327ffd70329ffd7032bffd7
032dffd7032fffd70331ffd70333ffd7036f00140371001403730014003900050029000a00290026ffec002affec0032ffec0034ffec0089ffec0094ffec0095ffec0096ffec0097ffec0098ffec009a
ffec00c8ffec00ceffec00deffec00e0ffec00e2ffec00e4ffec010effec0112ffec0114ffec0166ffec016dffec01b8ffec01bbffec02070029020b0029025fffec026cffec027effec0284ffec0286
ffec0288ffec028affec028cffec02b1ffec02b3ffec02e1ffec02e3ffec02efffec02f1ffec02f3ffec0315ffec0319ffec0349ffec034bffec034dffec034fffec0351ffec0353ffec0355ffec0357
ffec0359ffec035bffec035dffec035fffec003900050029000a00290026ffd7002affd70032ffd70034ffd70089ffd70094ffd70095ffd70096ffd70097ffd70098ffd7009affd700c8ffd700ceffd7
00deffd700e0ffd700e2ffd700e4ffd7010effd70112ffd70114ffd70166ffd7016dffd701b8ffd701bbffd702070029020b0029025fffd7026cffd7027effd70284ffd70286ffd70288ffd7028affd7
028cffd702b1ffd702b3ffd702e1ffd702e3ffd702efffd702f1ffd702f3ffd70315ffd70319ffd70349ffd7034bffd7034dffd7034fffd70351ffd70353ffd70355ffd70357ffd70359ffd7035bffd7
035dffd7035fffd700630005ff9a000aff9a0026ffec002affec0032ffec0034ffec0037ff850038ffec0039ffae003affc3003cff9a0089ffec0094ffec0095ffec0096ffec0097ffec0098ffec009a
ffec009bffec009cffec009dffec009effec009fff9a00c8ffec00ceffec00deffec00e0ffec00e2ffec00e4ffec010effec0112ffec0114ffec0124ff850126ff85012cffec0130ffec0132ffec0134
ffec0136ffc30138ff9a013aff9a0166ffec016dffec0171ff8501b8ffec01bbffec01bcff8501faffc301fcffc301feffc30200ff9a0207ff9a020bff9a025fffec0261ffec026cffec027effec0284
ffec0286ffec0288ffec028affec028cffec02a9ff8502b1ffec02b3ffec02b5ff8502b7ff8502b9ff8502bdff8502e1ffec02e3ffec02efffec02f1ffec02f3ffec0315ffec0317ff850319ffec0349
ffec034bffec034dffec034fffec0351ffec0353ffec0355ffec0357ffec0359ffec035bffec035dffec035fffec0361ffec0363ffec0365ffec0367ffec0369ffec036bffec036dffec036fff9a0371
ff9a0373ff9a004b000fffc30011ffc30024ffec002cffec0037ffc30039ffd7003affec003bffd7003cffd7003dffec0082ffec0083ffec0084ffec0085ffec0086ffec0087ffec0088ffd7008effec
008fffec0090ffec0091ffec009fffd700c2ffec00c4ffec00c6ffec00ecffec00f0ffec00f2ffec0124ffc30126ffc30136ffec0138ffd7013affd7013bffec013dffec013fffec0143ffec015fffec
0169ffec0171ffc301aaffec01bcffc301faffec01fcffec01feffec0200ffd70228ffec0258ffec0272ffec02a9ffc302b5ffc302b7ffc302b9ffc302bdffc302d9ffec02dbffec02ddffec0317ffc3
031dffec031fffec0321ffec0323ffec0325ffec0327ffec0329ffec032bffec032dffec032fffec0331ffec0333ffec0345ffec0347ffec036fffd70371ffd70373ffd70035000fff330011ff330024
ffae0026ffec003bffec003cffec003dffd70082ffae0083ffae0084ffae0085ffae0086ffae0087ffae0088ff710089ffec009fffec00c2ffae00c4ffae00c6ffae00c8ffec00ceffec0138ffec013a
ffec013bffd7013dffd7013fffd70143ffae015fffae0169ffae01aaffae01bbffec0200ffec0228ffae0258ffae0272ffae02d9ffae02dbffae02ddffae031dffae031fffae0321ffae0323ffae0325
ffae0327ffae0329ffae032bffae032dffae032fffae0331ffae0333ffae036fffec0371ffec0373ffec004b000fffc30011ffc30024ffec002cffec0037ffc30039ffd7003affec003bffd7003cffd7
003dffec0082ffec0083ffec0084ffec0085ffec0086ffec0087ffec0088ffc3008effec008fffec0090ffec0091ffec009fffd700c2ffec00c4ffec00c6ffec00ecffec00f0ffec00f2ffec0124ffc3
0126ffc30136ffec0138ffd7013affd7013bffec013dffec013fffec0143ffec015fffec0169ffec0171ffc301aaffec01bcffc301faffec01fcffec01feffec0200ffd70228ffec0258ffec0272ffec
02a9ffc302b5ffc302b7ffc302b9ffc302bdffc302d9ffec02dbffec02ddffec0317ffc3031dffec031fffec0321ffec0323ffec0325ffec0327ffec0329ffec032bffec032dffec032fffec0331ffec
0333ffec0345ffec0347ffec036fffd70371ffd70373ffd7000b0037ffec0124ffec0126ffec0171ffec01bcffec02a9ffec02b5ffec02b7ffec02b9ffec02bdffec0317ffec012000050052000a0052
000fff9a0010ff9a0011ff9a002200290024ff850026ffc3002affc30032ffc30034ffc30036ffec003700140044ff850046ff850047ff850048ff85004aff9a0050ffae0051ffae0052ff850053ffae
0054ff850055ffae0056ff850058ffae0059ffc3005affc3005bffc3005cffc3005dffc30082ff850083ff850084ff850085ff850086ff850087ff850088ff710089ffc30094ffc30095ffc30096ffc3
0097ffc30098ffc3009affc300a2ff8500a3ff8500a4ff8500a5ff8500a6ff8500a7ff8500a8ff8500a9ff8500aaff8500abff8500acff8500adff8500b3ffae00b4ff8500b5ff8500b6ff8500b7ff85
00b8ff8500baff8500bbffae00bcffae00bdffae00beffae00bfffc300c1ffc300c2ff8500c3ff8500c4ff8500c5ff8500c6ff8500c7ff8500c8ffc300c9ff8500cbff8500cdff8500ceffc300cfff85
00d5ff8500d7ff8500d9ff8500dbff8500ddff8500deffc300e0ffc300e2ffc300e4ffc30106ffae0108ffae010affae010effc3010fff850111ff850112ffc30113ff850114ffc30115ff85011cffec
011dff85011fff850120ffec0121ff850122ffec0123ff8501240014012600140137ffc30139ffc30140ffc30143ff850144ff85014aff85015fff850166ffc30169ff85016dffc3017100140179ff85
017bffae017eff850180ffc30182ff850184ffae018affc3018cff85018eff850190ff850191ffc30193ff850194ffc30196ff850199ff85019bff8501a0ffec01aaff8501b8ffc301bbffc301bc0014
01caff8501cfff8501d8ff8501dbff8501dcffc301ddffc301deff8501eaff8501edff8501eeff8501fbffc301fdffc301ffffc30201ffc30202ffae0203ff9a02070052020b00520228ff850258ff85
0259ff85025fffc30260ff85026aff85026cffc3026dffc30272ff85027effc3027fff850281ffc30283ffc30284ffc30285ff850286ffc30287ff850288ffc30289ff85028affc3028bffc3028cffc3
028dff8502a9001402aaffc302b1ffc302b2ff8502b3ffc302b4ff8502b5001402b6ffc302b7001402b8ffc302b9001402baffc302bd001402d9ff8502daff8502dbff8502dcff8502ddff8502e0ff85
02e1ffc302e3ffc302efffc302f0ff8502f1ffc302f2ff8502f3ffc302f4ff8502f8ffc302faffc302fcffc3030aff85030cff85030effc30310ffc30315ffc30316ff85031700140318ffc30319ffc3
031aff85031dff85031eff85031fff850321ff850322ff850323ff850324ff850325ff850326ff850327ff850328ff850329ff85032aff85032bff85032cff85032dff85032eff85032fff850330ff85
0331ff850332ff850333ff850334ff850336ff850338ff85033aff85033cff85033eff850340ff850342ff850344ff850349ffc3034aff85034bffc3034cff85034dffc3034eff85034fffc30350ff85
0351ffc30352ff850353ffc30354ff850355ffc30356ff850357ffc30358ff850359ffc3035aff85035bffc3035cff85035dffc3035eff85035fffc30360ff850362ffae0364ffae0366ffae0368ffae
036affae036cffae036effae0370ffc30372ffc30374ffc30023000fffd70011ffd70024ffec0082ffec0083ffec0084ffec0085ffec0086ffec0087ffec0088ffd700c2ffec00c4ffec00c6ffec0143
ffec015fffec0169ffec01aaffec0228ffec0258ffec0272ffec02d9ffec02dbffec02ddffec031dffec031fffec0321ffec0323ffec0325ffec0327ffec0329ffec032bffec032dffec032fffec0331
ffec0333ffec00e800050052000a0052000fff9a0011ff9a002200290024ffc30026ffd7002affd70032ffd70034ffd70044ffc30046ffc30047ffc30048ffc3004affc30050ffd70051ffd70052ffc3
0053ffd70054ffc30055ffd70056ffd70058ffd70082ffc30083ffc30084ffc30085ffc30086ffc30087ffc30088ff850089ffd70094ffd70095ffd70096ffd70097ffd70098ffd7009affd700a2ffc3
00a3ffc300a4ffc300a5ffc300a6ffc300a7ffc300a8ffc300a9ffc300aaffc300abffc300acffc300adffc300b3ffd700b4ffc300b5ffc300b6ffc300b7ffc300b8ffc300baffc300bbffd700bcffd7
00bdffd700beffd700c2ffc300c3ffc300c4ffc300c5ffc300c6ffc300c7ffc300c8ffd700c9ffc300cbffc300cdffc300ceffd700cfffc300d5ffc300d7ffc300d9ffc300dbffc300ddffc300deffd7
00e0ffd700e2ffd700e4ffd70106ffd70108ffd7010affd7010effd7010fffc30111ffc30112ffd70113ffc30114ffd70115ffc3011dffd7011fffd70121ffd70123ffd70143ffc30144ffc3014affd7
015fffc30166ffd70169ffc3016dffd70179ffc3017bffd7017effc30182ffc30184ffd7018cffc3018effc30190ffc30193ffc30196ffc30199ffc3019bffc301aaffc301b8ffd701bbffd701caffc3
01cfffc301d8ffc301dbffc301deffc301eaffc301edffc301eeffd702070052020b00520228ffc30258ffc30259ffc3025fffd70260ffc3026affc3026cffd70272ffc3027effd7027fffc30284ffd7
0285ffc30286ffd70287ffc30288ffd70289ffc3028affd7028cffd7028dffc302b1ffd702b2ffc302b3ffd702b4ffc302d9ffc302daffc302dbffc302dcffc302ddffc302e0ffc302e1ffd702e3ffd7
02efffd702f0ffc302f1ffd702f2ffc302f3ffd702f4ffc3030affc3030cffc30315ffd70316ffc30319ffd7031affc3031dffc3031effc3031fffc30321ffc30322ffc30323ffc30324ffc30325ffc3
0326ffc30327ffc30328ffc30329ffc3032affc3032bffc3032cffc3032dffc3032effc3032fffc30330ffc30331ffc30332ffc30333ffc30334ffc30336ffc30338ffc3033affc3033cffc3033effc3
0340ffc30342ffc30344ffc30349ffd7034affc3034bffd7034cffc3034dffd7034effc3034fffd70350ffc30351ffd70352ffc30353ffd70354ffc30355ffd70356ffc30357ffd70358ffc30359ffd7
035affc3035bffd7035cffc3035dffd7035effc3035fffd70360ffc30362ffd70364ffd70366ffd70368ffd7036affd7036cffd7036effd700e900050066000a0066000fffae0011ffae0024ffd70026
ffec002affec0032ffec0034ffec0044ffd70046ffd70047ffd70048ffd7004affec0050ffec0051ffec0052ffd70053ffec0054ffd70055ffec0056ffd70058ffec005dffec0082ffd70083ffd70084
ffd70085ffd70086ffd70087ffd70088ffae0089ffec0094ffec0095ffec0096ffec0097ffec0098ffec009affec00a2ffd700a3ffd700a4ffd700a5ffd700a6ffd700a7ffd700a8ffd700a9ffd700aa
ffd700abffd700acffd700adffd700b3ffec00b4ffd700b5ffd700b6ffd700b7ffd700b8ffd700baffd700bbffec00bcffec00bdffec00beffec00c2ffd700c3ffd700c4ffd700c5ffd700c6ffd700c7
ffd700c8ffec00c9ffd700cbffd700cdffd700ceffec00cfffd700d5ffd700d7ffd700d9ffd700dbffd700ddffd700deffec00e0ffec00e2ffec00e4ffec0106ffec0108ffec010affec010effec010f
ffd70111ffd70112ffec0113ffd70114ffec0115ffd7011dffd7011fffd70121ffd70123ffd70140ffec0143ffd70144ffd7014affd7015fffd70166ffec0169ffd7016dffec0179ffd7017bffec017e
ffd70182ffd70184ffec018cffd7018effd70190ffd70193ffd70196ffd70199ffd7019bffd701aaffd701b8ffec01bbffec01caffd701cfffd701d8ffd701dbffd701deffd701eaffd701edffd701ee
ffd702070066020b00660228ffd70258ffd70259ffd7025fffec0260ffd7026affd7026cffec0272ffd7027effec027fffd70284ffec0285ffd70286ffec0287ffd70288ffec0289ffd7028affec028c
ffec028dffd702b1ffec02b2ffd702b3ffec02b4ffd702d9ffd702daffd702dbffd702dcffd702ddffd702e0ffd702e1ffec02e3ffec02efffec02f0ffd702f1ffec02f2ffd702f3ffec02f4ffd7030a
ffd7030cffd70315ffec0316ffd70319ffec031affd7031dffd7031effd7031fffd70321ffd70322ffd70323ffd70324ffd70325ffd70326ffd70327ffd70328ffd70329ffd7032affd7032bffd7032c
ffd7032dffd7032effd7032fffd70330ffd70331ffd70332ffd70333ffd70334ffd70336ffd70338ffd7033affd7033cffd7033effd70340ffd70342ffd70344ffd70349ffec034affd7034bffec034c
ffd7034dffec034effd7034fffec0350ffd70351ffec0352ffd70353ffec0354ffd70355ffec0356ffd70357ffec0358ffd70359ffec035affd7035bffec035cffd7035dffec035effd7035fffec0360
ffd70362ffec0364ffec0366ffec0368ffec036affec036cffec036effec008c00050029000a00290026ffd7002affd70032ffd70034ffd70046ffec0047ffec0048ffec0052ffec0054ffec0089ffd7
0094ffd70095ffd70096ffd70097ffd70098ffd7009affd700a8ffec00a9ffec00aaffec00abffec00acffec00adffec00b4ffec00b5ffec00b6ffec00b7ffec00b8ffec00baffec00c8ffd700c9ffec
00cbffec00cdffec00ceffd700cfffec00d5ffec00d7ffec00d9ffec00dbffec00ddffec00deffd700e0ffd700e2ffd700e4ffd7010effd7010fffec0111ffec0112ffd70113ffec0114ffd70115ffec
0166ffd7016dffd70179ffec017effec0182ffec018cffec018effec0190ffec0193ffec0196ffec0199ffec019bffec01b8ffd701bbffd701cfffec01d8ffec01dbffec01deffec01eaffec01edffec
02070029020b0029025fffd70260ffec026affec026cffd7027effd7027fffec0284ffd70285ffec0286ffd70287ffec0288ffd70289ffec028affd7028cffd7028dffec02b1ffd702b2ffec02b3ffd7
02b4ffec02e0ffec02e1ffd702e3ffd702efffd702f0ffec02f1ffd702f2ffec02f3ffd702f4ffec030affec030cffec0315ffd70316ffec0319ffd7031affec0336ffec0338ffec033affec033cffec
033effec0340ffec0342ffec0344ffec0349ffd7034affec034bffd7034cffec034dffd7034effec034fffd70350ffec0351ffd70352ffec0353ffd70354ffec0355ffd70356ffec0357ffd70358ffec
0359ffd7035affec035bffd7035cffec035dffd7035effec035fffd70360ffec010600050052000a0052000fff9a0011ff9a002200290024ff9a0026ffd7002affd70032ffd70034ffd70036ffec0044
ff9a0046ff9a0047ff9a0048ff9a004aff9a0050ffc30051ffc30052ff9a0053ffc30054ff9a0055ffc30056ffae0058ffc3005bffd7005cffec005dffc30082ff9a0083ff9a0084ff9a0085ff9a0086
ff9a0087ff9a0088ff710089ffd70094ffd70095ffd70096ffd70097ffd70098ffd7009affd700a2ff9a00a3ff9a00a4ff9a00a5ff9a00a6ff9a00a7ff9a00a8ff9a00a9ff9a00aaff9a00abff9a00ac
ff9a00adff9a00b3ffc300b4ff9a00b5ff9a00b6ff9a00b7ff9a00b8ff9a00baff9a00bbffc300bcffc300bdffc300beffc300bfffec00c1ffec00c2ff9a00c3ff9a00c4ff9a00c5ff9a00c6ff9a00c7
ff9a00c8ffd700c9ff9a00cbff9a00cdff9a00ceffd700cfff9a00d5ff9a00d7ff9a00d9ff9a00dbff9a00ddff9a00deffd700e0ffd700e2ffd700e4ffd70106ffc30108ffc3010affc3010effd7010f
ff9a0111ff9a0112ffd70113ff9a0114ffd70115ff9a011cffec011dffae011fffae0120ffec0121ffae0122ffec0123ffae0139ffec0140ffc30143ff9a0144ff9a014affae015fff9a0166ffd70169
ff9a016dffd70179ff9a017bffc3017eff9a0180ffec0182ff9a0184ffc3018affec018cff9a018eff9a0190ff9a0193ff9a0196ff9a0199ff9a019bff9a01a0ffec01aaff9a01b8ffd701bbffd701ca
ff9a01cfff9a01d8ff9a01dbff9a01ddffec01deff9a01eaff9a01edff9a01eeffae0201ffec02070052020b00520228ff9a0258ff9a0259ff9a025fffd70260ff9a026aff9a026cffd7026dffec0272
ff9a027effd7027fff9a0281ffec0283ffec0284ffd70285ff9a0286ffd70287ff9a0288ffd70289ff9a028affd7028bffec028cffd7028dff9a02b1ffd702b2ff9a02b3ffd702b4ff9a02b8ffec02ba
ffec02d9ff9a02daff9a02dbff9a02dcff9a02ddff9a02e0ff9a02e1ffd702e3ffd702efffd702f0ff9a02f1ffd702f2ff9a02f3ffd702f4ff9a02f8ffec02faffec02fcffec030aff9a030cff9a030e
ffec0310ffec0315ffd70316ff9a0319ffd7031aff9a031dff9a031eff9a031fff9a0321ff9a0322ff9a0323ff9a0324ff9a0325ff9a0326ff9a0327ff9a0328ff9a0329ff9a032aff9a032bff9a032c
ff9a032dff9a032eff9a032fff9a0330ff9a0331ff9a0332ff9a0333ff9a0334ff9a0336ff9a0338ff9a033aff9a033cff9a033eff9a0340ff9a0342ff9a0344ff9a0349ffd7034aff9a034bffd7034c
ff9a034dffd7034eff9a034fffd70350ff9a0351ffd70352ff9a0353ffd70354ff9a0355ffd70356ff9a0357ffd70358ff9a0359ffd7035aff9a035bffd7035cff9a035dffd7035eff9a035fffd70360
ff9a0362ffc30364ffc30366ffc30368ffc3036affc3036cffc3036effc30370ffec0372ffec0374ffec003900050029000a00290026ffec002affec0032ffec0034ffec0089ffec0094ffec0095ffec
0096ffec0097ffec0098ffec009affec00c8ffec00ceffec00deffec00e0ffec00e2ffec00e4ffec010effec0112ffec0114ffd70166ffec016dffec01b8ffec01bbffec02070029020b0029025fffec
026cffec027effec0284ffec0286ffec0288ffec028affec028cffec02b1ffec02b3ffec02e1ffec02e3ffec02efffec02f1ffec02f3ffec0315ffec0319ffec0349ffec034bffec034dffec034fffec
0351ffec0353ffec0355ffec0357ffec0359ffec035bffec035dffec035fffec0001000affd700040005003d000a003d0207003d020b003d002600050066000a006600590014005a0014005c001400bf
001400c10014013700140139001401800014018a0014019100140194001401dc001401dd001401fb001401fd001401ff00140201001402070066020b0066026d00140281001402830014028b001402aa
001402b6001402b8001402ba001402f8001402fa001402fc0014030e00140310001403180014037000140372001403740014000500050029000a0029004a001402070029020b00290001000affc30004
00050029000a002902070029020b00290003005bffd7005dffec0140ffec001f00050066000a00660044ffec004affec00a2ffec00a3ffec00a4ffec00a5ffec00a6ffec00a7ffec00c3ffec00c5ffec
00c7ffec0144ffec01caffec02070066020b00660259ffec02daffec02dcffec031effec0322ffec0324ffec0326ffec0328ffec032affec032cffec032effec0330ffec0332ffec0334ffec00050005
0052000a00520057001402070052020b0052000500050052000a00520049001402070052020b0052003600050029000a00290052ffd700a8ffd700b4ffd700b5ffd700b6ffd700b7ffd700b8ffd700ba
ffd7010fffd70111ffd70113ffd70115ffd7018cffd7018effd70190ffd70193ffd70196ffd70199ffd7019bffd701d8ffd701deffd702070029020b00290260ffd7026affd7027fffd70285ffd70287
ffd70289ffd7028dffd702b2ffd702b4ffd702e0ffd702f0ffd702f2ffd702f4ffd7030affd7030cffd70316ffd7031affd7034affd7034cffd7034effd70350ffd70352ffd70354ffd70356ffd70358
ffd7035affd7035cffd7035effd70360ffd700050005003d000a003d004900140207003d020b003d00320052ffec00a8ffec00b4ffec00b5ffec00b6ffec00b7ffec00b8ffec00baffec010fffec0111
ffec0113ffec0115ffec018cffec018effec0190ffec0193ffec0196ffec0199ffec019bffec01d8ffec01deffec0260ffec026affec027fffec0285ffec0287ffec0289ffec028dffec02b2ffec02b4
ffec02e0ffec02f0ffec02f2ffec02f4ffec030affec030cffec0316ffec031affec034affec034cffec034effec0350ffec0352ffec0354ffec0356ffec0358ffec035affec035cffec035effec0360
ffec00030171ffd70178ffd70191002900030171ff9a0172ffc30178ffc300930005ffae000affae000dff85000f0044001e00440022ffc30026ffec002affec002d005e0032ffec0034ffec0037ff85
0038ffec0039ffc3003affd7003cff9a003d003b0049ffec0057ffec0059ffd7005affec005cffd70082ffd70089ffec0094ffec0095ffec0096ffec0097ffec0098ffec009affec009bffec009cffec
009dffec009effec009fff9a00bfffd700c1ffd700c8ffec00ceffec00deffec00e0ffec00e2ffec00e4ffec00f6005e010effec0112ffec0114ffd70124ff850126ff85012cffec0130ffec0132ffec
0134ffec0136ffd70137ffec0138ff9a0139ffd7013aff9a013b003b013d003b013f003b0166ffec016dffec0171ff850180ffd7018affd70191ffd70194ffd701a3005e01b8ffec01bbffec01bcff85
01dcffd701ddffd701faffd701fbffec01fcffd701fdffec01feffd701ffffec0200ff9a0201ffd70207ffae020bffae025fffec0261ffec026cffec026dffd7027effec0281ffd70283ffd70284ffec
0286ffec0288ffec028affec028bffd7028cffec02a9ff8502aaffd702b1ffec02b3ffec02b5ff8502b6ffd702b7ff8502b8ffd702b9ff8502baffd702bdff8502e1ffec02e3ffec02efffec02f1ffec
02f3ffec02f8ffd702faffd702fcffd7030effd70310ffd70315ffec0317ff850318ffd70319ffec0349ffec034bffec034dffec034fffec0351ffec0353ffec0355ffec0357ffec0359ffec035bffec
035dffec035fffec0361ffec0363ffec0365ffec0367ffec0369ffec036bffec036dffec036fff9a0370ffd70371ff9a0372ffd70373ff9a0374ffd700900005ffae000affae000dff85000f0044001e
00440022ffc30026ffec002affec0032ffec0034ffec0037ff850038ffec0039ffc3003affd7003cff9a003d003b0049ffec0057ffec0059ffd7005affec005cffd70089ffec0094ffec0095ffec0096
ffec0097ffec0098ffec009affec009bffec009cffec009dffec009effec009fff9a00bfffd700c1ffd700c8ffec00ceffec00deffec00e0ffec00e2ffec00e4ffec010effec0112ffec0114ffd70124
ff850126ff85012cffec0130ffec0132ffec0134ffec0136ffd70137ffec0138ff9a0139ffd7013aff9a013b003b013d003b013f003b0166ffec016dffec0171ff850180ffd7018affd70191ffd70194
ffd701a3005e01b8ffec01bbffec01bcff8501dcffd701ddffd701faffd701fbffec01fcffd701fdffec01feffd701ffffec0200ff9a0201ffd70207ffae020bffae025fffec0261ffec026cffec026d
ffd7027effec0281ffd70283ffd70284ffec0286ffec0288ffec028affec028bffd7028cffec02a9ff8502aaffd702b1ffec02b3ffec02b5ff8502b6ffd702b7ff8502b8ffd702b9ff8502baffd702bd
ff8502e1ffec02e3ffec02efffec02f1ffec02f3ffec02f8ffd702faffd702fcffd7030effd70310ffd70315ffec0317ff850318ffd70319ffec0349ffec034bffec034dffec034fffec0351ffec0353
ffec0355ffec0357ffec0359ffec035bffec035dffec035fffec0361ffec0363ffec0365ffec0367ffec0369ffec036bffec036dffec036fff9a0370ffd70371ff9a0372ffd70373ff9a0374ffd70091
0005ffae000affae000dff7f000f0044001e00440022ffd70026ffec002affec002d005e0032ffec0034ffec0037ff850038ffec0039ffc3003affd7003cff9a003d003b0057ffe50059ffd5005affe5
005cffdb0089ffec0094ffec0095ffec0096ffec0097ffec0098ffec009affec009bffec009cffec009dffec009effec009fff9a00bfffdb00c1ffdb00c8ffec00ceffec00deffec00e0ffec00e2ffec
00e4ffec00f6005e010effec0112ffec0114ffd70124ff850126ff85012cffec0130ffec0132ffec0134ffec0136ffd70137ffe50138ff9a0139ffdb013aff9a013b003b013d003b013f003b0166ffec
016dffec0171ff850180ffdb018affd50191ffd50194ffd501a3005e01b8ffec01bbffec01bcff8501dcffd501ddffdb01faffd701fbffe501fcffd701fdffe501feffd701ffffe50200ff9a0201ffdb
0207ffae020bffae025fffec0261ffec026cffec026dffdb027effec0281ffdb0283ffdb0284ffec0286ffec0288ffec028affec028bffdb028cffec02a9ff8502aaffd502b1ffec02b3ffec02b5ff85
02b6ffd502b7ff8502b8ffdb02b9ff8502baffdb02bdff8502e1ffec02e3ffec02efffec02f1ffec02f3ffec02f8ffdb02faffdb02fcffdb030effdb0310ffdb0315ffec0317ff850318ffd50319ffec
0349ffec034bffec034dffec034fffec0351ffec0353ffec0355ffec0357ffec0359ffec035bffec035dffec035fffec0361ffec0363ffec0365ffec0367ffec0369ffec036bffec036dffec036fff9a
0370ffdb0371ff9a0372ffdb0373ff9a0374ffdb00940005ff66000aff66000dff7f000f0044001e00440022ffd70026ffec002affec002d005e0032ffec0034ffec0037ff850038ffec0039ffc3003a
ffd7003cff9a003d003b0057ffe50059ffd5005affe5005cffdb0089ffec0094ffec0095ffec0096ffec0097ffec0098ffec009affec009bffec009cffec009dffec009effec009fff9a00bfffdb00c1
ffdb00c8ffec00ceffec00deffec00e0ffec00e2ffec00e4ffec00f6005e010effec0112ffec0114ffd70124ff850126ff85012cffec0130ffec0132ffec0134ffec0136ffd70137ffe50138ff9a0139
ffdb013aff9a013b003b013d003b013f003b0166ffec016dffec0171ff850180ffdb018affd50191ffd50194ffd501a3005e01b8ffec01bbffec01bcff8501dcffd501ddffdb01faffd701fbffe501fc
ffd701fdffe501feffd701ffffe50200ff9a0201ffdb0206ff660207ffae020aff66020bffae0220ff9a025fffec0261ffec026cffec026dffdb027effec0281ffdb0283ffdb0284ffec0286ffec0288
ffec028affec028bffdb028cffec02a9ff8502aaffd502b1ffec02b3ffec02b5ff8502b6ffd502b7ff8502b8ffdb02b9ff8502baffdb02bdff8502e1ffec02e3ffec02efffec02f1ffec02f3ffec02f8
ffdb02faffdb02fcffdb030effdb0310ffdb0315ffec0317ff850318ffd50319ffec0349ffec034bffec034dffec034fffec0351ffec0353ffec0355ffec0357ffec0359ffec035bffec035dffec035f
ffec0361ffec0363ffec0365ffec0367ffec0369ffec036bffec036dffec036fff9a0370ffdb0371ff9a0372ffdb0373ff9a0374ffdb003900050029000a00290010ffd70026ffec0032ffec0034ffec
0084ffec0089ffec008affec008fffec0094ffec0095ffec0096ffec0097ffec0098ffec009affec00c8ffec00ceffec00f6003d010effec0112ffec0166ffec016dffec01a3003d01b8ffec01bbffec
02070029020b0029025fffec026cffec027effec0284ffec0286ffec0288ffec028affec028cffec02b1ffec02b3ffec02e1ffec02e3ffec02efffec02f1ffec02f3ffec0315ffec0319ffec0349ffec
034bffec034dffec034fffec0351ffec0353ffec0355ffec0357ffec0359ffec035bffec035dffec035fffec003700050029000a00290010ffd70026ffec0032ffec0034ffec0089ffec008bffec0094
ffec0095ffec0096ffec0097ffec0098ffec009affec00c8ffec00ceffec00f6003d010effec0112ffec0166ffec016dffec01a3003d01b8ffec01bbffec02070029020b0029025fffec026cffec027e
ffec0284ffec0286ffec0288ffec028affec028cffec02b1ffec02b3ffec02e1ffec02e3ffec02efffec02f1ffec02f3ffec0315ffec0319ffec0349ffec034bffec034dffec034fffec0351ffec0353
ffec0355ffec0357ffec0359ffec035bffec035dffec035fffec00200024ffec0082ffec0083ffec0084ffec0085ffec0086ffec0087ffec00c2ffec00c4ffec00c6ffec0143ffec015fffec0169ffec
01aaffec0228ffec0258ffec0272ffec02d9ffec02dbffec02ddffec031dffec031fffec0321ffec0323ffec0325ffec0327ffec0329ffec032bffec032dffec032fffec0331ffec0333ffec0056000c
ffd7000fffc30011ffc30024ffec002cffec002dfff60036ffec0037ffc30039ffd7003affec003bffd7003cffd7003dffec0040ffd70060ffd70082ffec0083ffec0084ffec0085ffec0086ffec0087
ffec0088ffd7008effec008fffec0090ffec0091ffec009fffd700c2ffec00c4ffec00c6ffec00ecffec00f0ffec00f2ffec00f6fff6011cffec0120ffec0122ffec0124ffc30126ffc30136ffec0138
ffd7013affd7013bffec013dffec013fffec0143ffec015fffec0169ffec0171ffc301a0ffec01a3fff601aaffec01bcffc301faffec01fcffec01feffec0200ffd70228ffec0258ffec0272ffec02a9
ffc302b5ffc302b7ffc302b9ffc302bdffc302d9ffec02dbffec02ddffec0317ffc3031dffec031fffec0321ffec0323ffec0325ffec0327ffec0329ffec032bffec032dffec032fffec0331ffec0333
ffec0345ffec0347ffec036fffd70371ffd70373ffd70042000fffd70011ffd70024ffec0030ffec003dffec0044ffec0082ffec0083ffec0084ffec0085ffec0086ffec0087ffec0088ffd700a2ffec
00a3ffec00a4ffec00a5ffec00a6ffec00a7ffec00c2ffec00c3ffec00c4ffec00c5ffec00c6ffec00c7ffec013bffec013dffec013fffec0143ffec0144ffec015fffec0169ffec01aaffec01caffec
0228ffec0258ffec0259ffec0272ffec02d9ffec02daffec02dbffec02dcffec02ddffec031dffec031effec031fffec0321ffec0322ffec0323ffec0324ffec0325ffec0326ffec0327ffec0328ffec
0329ffec032affec032bffec032cffec032dffec032effec032fffec0330ffec0331ffec0332ffec0333ffec0334ffec012e000500520009ffc3000a0052000c003d000d0029000fff9a0010ff9a0011
ff9a002200290024ff9a0026ffd7002affd7002dffbe0030ffc30032ffd70034ffd70036ffec0037002700390029003a00140040003d0044ff9a0046ff9a0047ff9a0048ff9a0049ffe5004aff9a0050
ffc30051ffc30052ff9a0053ffc30054ff9a0055ffc30056ffae0058ffc30059ffd7005affec005bffd7005cffec005dffc30060003d0082ff9a0083ff9a0084ff9a0085ff9a0086ff9a0087ff9a0088
ff710089ffd70094ffd70095ffd70096ffd70097ffd70098ffd7009affd700a2ff9a00a3ff9a00a4ff9a00a5ff9a00a6ff9a00a7ff9a00a8ff9a00a9ff9a00aaff9a00abff9a00acff9a00adff9a00b3
ffc300b4ff9a00b5ff9a00b6ff9a00b7ff9a00b8ff9a00baff9a00bbffc300bcffc300bdffc300beffc300bfffec00c1ffec00c2ff9a00c3ff9a00c4ff9a00c5ff9a00c6ff9a00c7ff9a00c8ffd700c9
ff9a00cbff9a00cdff9a00ceffd700cfff9a00d5ff9a00d7ff9a00d9ff9a00dbff9a00ddff9a00deffd700e0ffd700e2ffd700e4ffd700f6ffbe0106ffc30108ffc3010affc3010effd7010fff9a0111
ff9a0112ffd70113ff9a0114ffd70115ff9a011cffec011dffae011fffae0120ffec0121ffae0122ffec0123ffae0124002701260027013600140137ffec0139ffec0140ffc30143ff9a0144ff9a014a
ffae015fff9a0166ffd70169ff9a016dffd7017100270179ff9a017bffc3017eff9a0180ffec0182ff9a0184ffc3018affd7018cff9a018eff9a0190ff9a0191ffd70193ff9a0194ffd70196ff9a0199
ff9a019bff9a01a0ffec01a3ffbe01aaff9a01b8ffd701bbffd701bc002701caff9a01cfff9a01d8ff9a01dbff9a01dcffd701ddffec01deff9a01eaff9a01edff9a01eeffae01fa001401fbffec01fc
001401fdffec01fe001401ffffec0201ffec02070052020b00520228ff9a0258ff9a0259ff9a025fffd70260ff9a026aff9a026cffd7026dffec0272ff9a027effd7027fff9a0281ffec0283ffec0284
ffd70285ff9a0286ffd70287ff9a0288ffd70289ff9a028affd7028bffec028cffd7028dff9a02a9002702aaffd702b1ffd702b2ff9a02b3ffd702b4ff9a02b5002702b6ffd702b7002702b8ffec02b9
002702baffec02bd002702d9ff9a02daff9a02dbff9a02dcff9a02ddff9a02e0ff9a02e1ffd702e3ffd702efffd702f0ff9a02f1ffd702f2ff9a02f3ffd702f4ff9a02f8ffec02faffec02fcffec030a
ff9a030cff9a030effec0310ffec0315ffd70316ff9a031700270318ffd70319ffd7031aff9a031dff9a031eff9a031fff9a0321ff9a0322ff9a0323ff9a0324ff9a0325ff9a0326ff9a0327ff9a0328
ff9a0329ff9a032aff9a032bff9a032cff9a032dff9a032eff9a032fff9a0330ff9a0331ff9a0332ff9a0333ff9a0334ff9a0336ff9a0338ff9a033aff9a033cff9a033eff9a0340ff9a0342ff9a0344
ff9a0349ffd7034aff9a034bffd7034cff9a034dffd7034eff9a034fffd70350ff9a0351ffd70352ff9a0353ffd70354ff9a0355ffd70356ff9a0357ffd70358ff9a0359ffd7035aff9a035bffd7035c
ff9a035dffd7035eff9a035fffd70360ff9a0362ffc30364ffc30366ffc30368ffc3036affc3036cffc3036effc30370ffec0372ffec0374ffec00020005ff98000affd700030005ff98000affd7020b
ffd700060005ff6f000aff6f0049ffdb005bffd7005dffec0140ffec00020005ffbe000affbe00610005003d000a003d000fffbe0011ffbe0022ffb40046fff60047fff60048fff600490014004afff6
0052fff60054fff60057000600a8fff600a9fff600aafff600abfff600acfff600adfff600b4fff600b5fff600b6fff600b7fff600b8fff600bafff600c9fff600cbfff600cdfff600cffff600d5fff6
00d7fff600d9fff600dbfff600ddfff6010ffff60111fff60113fff60115fff60179fff6017efff60182fff6018cfff6018efff60190fff60193fff60196fff60199fff6019bfff601cffff601d8fff6
01dbfff601defff601eafff601edfff60207003d0208ff8d020b003d020cff8d0210ff810215000c0260fff6026afff6027ffff60285fff60287fff60289fff6028dfff602b2fff602b4fff602e0fff6
02f0fff602f2fff602f4fff6030afff6030cfff60316fff6031afff60336fff60338fff6033afff6033cfff6033efff60340fff60342fff60344fff6034afff6034cfff6034efff60350fff60352fff6
0354fff60356fff60358fff6035afff6035cfff6035efff60360fff600070005003d000a003d000fffbe0011ffbe004900140207003d020b003d00640005ffae000affae0026ffec002affec002d00e1
0032ffec0034ffec0037ff850038ffec0039ffc3003affd7003cff9a0089ffec0094ffec0095ffec0096ffec0097ffec0098ffec009affec009bffec009cffec009dffec009effec009fff9a00c8ffec
00ceffec00deffec00e0ffec00e2ffec00e4ffec010effec0112ffec0114ffd70124ff850126ff85012cffec0130ffec0132ffec0134ffec0136ffd70138ff9a013aff9a0166ffec016dffec0171ff85
01b8ffec01bbffec01bcff8501faffd701fcffd701feffd70200ff9a0207ffae020bffae025fffec0261ffec026cffec027effec0284ffec0286ffec0288ffec028affec028cffec02a9ff8502b1ffec
02b3ffec02b5ff8502b7ff8502b9ff8502bdff8502e1ffec02e3ffec02efffec02f1ffec02f3ffec0315ffec0317ff850319ffec0349ffec034bffec034dffec034fffec0351ffec0353ffec0355ffec
0357ffec0359ffec035bffec035dffec035fffec0361ffec0363ffec0365ffec0367ffec0369ffec036bffec036dffec036fff9a0371ff9a0373ff9a001f00050066000a0066000c008f002200a40040
00a400450052004b0052004c003d004d003d004e0052004f0052006000b800ae00f600b000cd00b100cd00e7005200e900a400eb010a00ed00cd00ef00f600f1002900f5003d00f700e100f9005200fc
005200fe00520100005201020052010400520207008f020b008f0001002d0066003a00050029000a00290026ffec002affec002d00520032ffec0034ffec0089ffec0094ffec0095ffec0096ffec0097
ffec0098ffec009affec00c8ffec00ceffec00deffec00e0ffec00e2ffec00e4ffec010effec0112ffec0114ffec0166ffec016dffec01b8ffec01bbffec02070029020b0029025fffec026cffec027e
ffec0284ffec0286ffec0288ffec028affec028cffec02b1ffec02b3ffec02e1ffec02e3ffec02efffec02f1ffec02f3ffec0315ffec0319ffec0349ffec034bffec034dffec034fffec0351ffec0353
ffec0355ffec0357ffec0359ffec035bffec035dffec035fffec004200050029000a0029003000140036ffec0037ffd70039ffec003affec003bffd7003cffd70059ffec005cffec009fffd700bfffec
00c1ffec011cffec0120ffec0122ffec0123ffec0124ffd70126ffd70136ffec0138ffd70139ffec013affd70171ffd70180ffec018affec0191ffec0194ffec01a0ffec01bcffd701dcffec01ddffec
01faffec01fcffec01feffec0200ffd70201ffec02070029020b0029026dffec0281ffec0283ffec028bffec02a9ffd702aaffec02b5ffd702b6ffec02b7ffd702b8ffec02b9ffd702baffec02bdffd7
02f8ffec02faffec02fcffec030effec0310ffec0317ffd70318ffec036fffd70370ffec0371ffd70372ffec0373ffd70374ffec011d00050052000a0052000fff9a0011ff9a002200290024ff850026
ffc3002affc30032ffc30034ffc30036ffec003700140044ff850046ff850047ff850048ff85004aff9a0050ffae0051ffae0052ff850053ffae0054ff850055ffae0056ff850058ffae0059ffc3005a
ffc3005bffc3005cffc3005dffc30082ff850083ff850084ff850085ff850086ff850087ff850088ff710089ffc30094ffc30095ffc30096ffc30097ffc30098ffc3009affc300a2ff8500a3ff8500a4
ff8500a5ff8500a6ff8500a7ff8500a8ff8500a9ff8500aaff8500abff8500acff8500adff8500b3ffae00b4ff8500b5ff8500b6ff8500b7ff8500b8ff8500baff8500bbffae00bcffae00bdffae00be
ffae00bfffc300c1ffc300c2ff8500c3ff8500c4ff8500c5ff8500c6ff8500c7ff8500c8ffc300c9ff8500cbff8500cdff8500ceffc300cfff8500d5ff8500d7ff8500d9ff8500dbff8500ddff8500de
ffc300e0ffc300e2ffc300e4ffc30106ffae0108ffae010affae010effc3010fff850111ff850112ffc30113ff850114ffc30115ff85011cffec011dff85011fff850120ffec0121ff850122ffec0123
ff8501240014012600140137ffc30139ffc30140ffc30143ff850144ff85014aff85015fff850166ffc30169ff85016dffc3017100140179ff85017bffae017eff850180ffc30182ff850184ffae018a
ffc3018cff85018eff850190ff850191ffc30193ff850194ffc30196ff850199ff85019bff8501a0ffec01aaff8501b8ffc301bbffc301bc001401caff8501cfff8501d8ff8501dbff8501dcffc301dd
ffc301deff8501eaff8501edff8501eeff8501fbffc301fdffc301ffffc30201ffc302070052020b00520228ff850258ff850259ff85025fffc30260ff85026aff85026cffc3026dffc30272ff85027e
ffc3027fff850281ffc30283ffc30284ffc30285ff850286ffc30287ff850288ffc30289ff85028affc3028bffc3028cffc3028dff8502a9001402aaffc302b1ffc302b2ff8502b3ffc302b4ff8502b5
001402b6ffc302b7001402b8ffc302b9001402baffc302bd001402d9ff8502daff8502dbff8502dcff8502ddff8502e0ff8502e1ffc302e3ffc302efffc302f0ff8502f1ffc302f2ff8502f3ffc302f4
ff8502f8ffc302faffc302fcffc3030aff85030cff85030effc30310ffc30315ffc30316ff85031700140318ffc30319ffc3031aff85031dff85031eff85031fff850321ff850322ff850323ff850324
ff850325ff850326ff850327ff850328ff850329ff85032aff85032bff85032cff85032dff85032eff85032fff850330ff850331ff850332ff850333ff850334ff850336ff850338ff85033aff85033c
ff85033eff850340ff850342ff850344ff850349ffc3034aff85034bffc3034cff85034dffc3034eff85034fffc30350ff850351ffc30352ff850353ffc30354ff850355ffc30356ff850357ffc30358
ff850359ffc3035aff85035bffc3035cff85035dffc3035eff85035fffc30360ff850362ffae0364ffae0366ffae0368ffae036affae036cffae036effae0370ffc30372ffc30374ffc3011d00050052
0009ffc3000a0052000c003d000d0029000fff9a0010ff5c0011ff9a002200290024ff9a0026ffd7002affd7002dffbe0030ffec0032ffd70034ffd70036ffec003700270040003d0044ff9a0046ff9a
0047ff9a0048ff9a0049ffe5004aff9a0050ffc30051ffc30052ff9a0053ffc30054ff9a0055ffc30056ffae0058ffc3005bffd7005cffec005dffc30060003d0082ff9a0083ff9a0084ff9a0085ff9a
0086ff9a0087ff9a0088ff710089ffd70094ffd70095ffd70096ffd70097ffd70098ffd7009affd700a2ff9a00a3ff9a00a4ff9a00a5ff9a00a6ff9a00a7ff9a00a8ff9a00a9ff9a00aaff9a00abff9a
00acff9a00adff9a00b3ffc300b4ff9a00b5ff9a00b6ff9a00b7ff9a00b8ff9a00baff9a00bbffc300bcffc300bdffc300beffc300bfffec00c1ffec00c2ff9a00c3ff9a00c4ff9a00c5ff9a00c6ff9a
00c7ff9a00c8ffd700c9ff9a00cbff9a00cdff9a00ceffd700cfff9a00d5ff9a00d7ff9a00d9ff9a00dbff9a00ddff9a00deffd700e0ffd700e2ffd700e4ffd700f6ffbe0106ffc30108ffc3010affc3
010effd7010fff9a0111ff9a0112ffd70113ff9a0114ffd70115ff9a011cffec011dffae011fffae0120ffec0121ffae0122ffec0123ffae01240027012600270139ffec0140ffc30143ff9a0144ff9a
014affae015fff9a0166ffd70169ff9a016dffd7017100270179ff9a017bffc3017eff9a0180ffec0182ff9a0184ffc3018affec018cff9a018eff9a0190ff9a0193ff9a0196ff9a0199ff9a019bff9a
01a0ffec01a3ffbe01aaff9a01b8ffd701bbffd701bc002701caff9a01cfff9a01d8ff9a01dbff9a01ddffec01deff9a01eaff9a01edff9a01eeffae0201ffec02070052020b0052020fffd70228ff9a
0258ff9a0259ff9a025fffd70260ff9a026aff9a026cffd7026dffec0272ff9a027effd7027fff9a0281ffec0283ffec0284ffd70285ff9a0286ffd70287ff9a0288ffd70289ff9a028affd7028bffec
028cffd7028dff9a02a9002702b1ffd702b2ff9a02b3ffd702b4ff9a02b5002702b7002702b8ffec02b9002702baffec02bd002702d9ff9a02daff9a02dbff9a02dcff9a02ddff9a02e0ff9a02e1ffd7
02e3ffd702efffd702f0ff9a02f1ffd702f2ff9a02f3ffd702f4ff9a02f8ffec02faffec02fcffec030aff9a030cff9a030effec0310ffec0315ffd70316ff9a031700270319ffd7031aff9a031dff9a
031eff9a031fff9a0321ff9a0322ff9a0323ff9a0324ff9a0325ff9a0326ff9a0327ff9a0328ff9a0329ff9a032aff9a032bff9a032cff9a032dff9a032eff9a032fff9a0330ff9a0331ff9a0332ff9a
0333ff9a0334ff9a0336ff9a0338ff9a033aff9a033cff9a033eff9a0340ff9a0342ff9a0344ff9a0349ffd7034aff9a034bffd7034cff9a034dffd7034eff9a034fffd70350ff9a0351ffd70352ff9a
0353ffd70354ff9a0355ffd70356ff9a0357ffd70358ff9a0359ffd7035aff9a035bffd7035cff9a035dffd7035eff9a035fffd70360ff9a0362ffc30364ffc30366ffc30368ffc3036affc3036cffc3
036effc30370ffec0372ffec0374ffec00c500050029000a00290010ffc30026ffec002affec0032ffec0034ffec0036ffd70038ffec0046ffec0047ffec0048ffec0052ffec0054ffec0059ffd7005a
ffd7005cffd70089ffec0094ffec0095ffec0096ffec0097ffec0098ffec009affec009bffec009cffec009dffec009effec00a8ffec00a9ffec00aaffec00abffec00acffec00adffec00b4ffec00b5
ffec00b6ffec00b7ffec00b8ffec00baffec00bfffd700c1ffd700c8ffec00c9ffec00cbffec00cdffec00ceffec00cfffec00d5ffec00d7ffec00d9ffec00dbffec00ddffec00deffec00e0ffec00e2
ffec00e4ffec010effec010fffec0111ffec0112ffec0113ffec0114ffd70115ffec011cffd70120ffd70122ffd7012cffec0130ffec0132ffec0134ffec0137ffd70139ffd70166ffec016dffec0179
ffec017effec0180ffd70182ffec018affd7018cffec018effec0190ffec0191ffd70193ffec0194ffd70196ffec0199ffec019bffec01a0ffd701b8ffec01bbffec01cfffec01d8ffec01dbffec01dc
ffd701ddffd701deffec01eaffec01edffec01fbffd701fdffd701ffffd70201ffd702070029020b0029025fffec0260ffec0261ffec026affec026cffec026dffd7027effec027fffec0281ffd70283
ffd70284ffec0285ffec0286ffec0287ffec0288ffec0289ffec028affec028bffd7028cffec028dffec02aaffd702b1ffec02b2ffec02b3ffec02b4ffec02b6ffd702b8ffd702baffd702e0ffec02e1
ffec02e3ffec02efffec02f0ffec02f1ffec02f2ffec02f3ffec02f4ffec02f8ffd702faffd702fcffd7030affec030cffec030effd70310ffd70315ffec0316ffec0318ffd70319ffec031affec0336
ffec0338ffec033affec033cffec033effec0340ffec0342ffec0344ffec0349ffec034affec034bffec034cffec034dffec034effec034fffec0350ffec0351ffec0352ffec0353ffec0354ffec0355
ffec0356ffec0357ffec0358ffec0359ffec035affec035bffec035cffec035dffec035effec035fffec0360ffec0361ffec0363ffec0365ffec0367ffec0369ffec036bffec036dffec0370ffd70372
ffd70374ffd700330052ffec00a8ffec00b4ffec00b5ffec00b6ffec00b7ffec00b8ffec00baffec010fffec0111ffec0113ffec0115ffec018cffec018effec0190ffec0193ffec0196ffec0199ffec
019bffec01d8ffec01deffec020b00140260ffec026affec027fffec0285ffec0287ffec0289ffec028dffec02b2ffec02b4ffec02e0ffec02f0ffec02f2ffec02f4ffec030affec030cffec0316ffec
031affec034affec034cffec034effec0350ffec0352ffec0354ffec0356ffec0358ffec035affec035cffec035effec0360ffec000b016200290166ffec01690029016dffec0171ff850172ff9a0173
ffd70175ffd70178ff9a018800290191ffc300030166ffec016dffec0173ffec000b015fffec0164ffec0167ffec016cffec0170ffec0171ffc30172ffd70174ffd70177ffec0178ffd70188ffec0024
0010ffc3006dffc3015fff9a0162ffc30166ffd70169ffc30173ffd70176ffd70179ff9a017aff9a017bffc3017dffc3017eff9a0181ffae0182ff9a0183ffd70184ffc30185ffd70186ffc30187ffc3
0189ffc3018cff9a018eff9a018fff9a0190ff9a0192ffc30193ffae0194ffd70195ffc30196ffae0198ffd70199ff9a019affc3019bffae0202ffc30203ffc300030171ffc30172ffc30178ffc30019
0179ffd7017dffd7017effd70180ffd70181ffd70183ffec0185ffd70186ffd701880029018affd7018bffec018cffd7018dffec018fffec0190ffd70191ffec0192ffd70193ffd70194ffec0195ffd7
0196ffec0198ffd70199ffd7019affd7019bffd7004f0005ffae000affae0026ffec002affec0032ffec0034ffec0037ff850038ffec0039ffc3003affd7003cff9a0089ffec0094ffec0095ffec0096
ffec0097ffec0098ffec009affec009bffec009cffec009dffec009effec009fff9a00c8ffec00ceffec00deffec00e0ffec00e2ffec00e4ffec010effec0112ffec0114ffd70124ff850126ff85012c
ffec0130ffec0132ffec0134ffec0136ffd70138ff9a013aff9a0166ffec016dffec0171ff850172ff9a0173ffd70175ffc30178ff9a0191ffc301faffd701fcffd701feffd70200ff9a0207ffae020b
ffae025fffec0261ffec0349ffec034bffec034dffec034fffec0351ffec0353ffec0355ffec0357ffec0359ffec035bffec035dffec035fffec0361ffec0363ffec0365ffec0367ffec0369ffec036b
ffec036dffec036fff9a0371ff9a0373ff9a00170037ffec0039ffec003bffec003cffec009fffec0124ffec0126ffec0138ffec013affec0167ffec016cffd70170ffec0171ffec0172ffec0174ffec
0177ffec0178ffec0191ffec0194ffec0200ffec036fffec0371ffec0373ffec00710005ff9a000aff9a000fff9a0010ff9a0011ff9a0026ffec002affec0032ffec0034ffec0037ff850038ffec0039
ffae003affc3003cff9a006dff9a007dffae0089ffec0094ffec0095ffec0096ffec0097ffec0098ffec009affec009bffec009cffec009dffec009effec009fff9a00c8ffec00ceffec00deffec00e0
ffec00e2ffec00e4ffec010effec0112ffec0114ffec0124ff850126ff85012cffec0130ffec0132ffec0134ffec0136ffc30138ff9a013aff9a015fff850162ff9a0166ffec0169ff9a016dffec0173
ffae0176ffd70179ff85017aff85017bffae017eff850180ffc30181ff9a0182ff850184ffc30186ffc30187ffc30189ffc3018affc3018cff85018dffc3018eff9a018fff850190ff850191ffc30192
ffc30193ff850194ffc30195ffc30196ff9a019700290198ffc30199ff85019affc3019bff9a01faffc301fcffc301feffc30200ff9a0202ff9a0203ff9a0207ff9a020bff9a025fffec0261ffec0349
ffec034bffec034dffec034fffec0351ffec0353ffec0355ffec0357ffec0359ffec035bffec035dffec035fffec0361ffec0363ffec0365ffec0367ffec0369ffec036bffec036dffec036fff9a0371
ff9a0373ff9a00b200050052000a0052000fff9a0011ff9a002200290024ffc30026ffd7002affd70032ffd70034ffd70044ffc30046ffc30047ffc30048ffc3004affc30050ffd70051ffd70052ffc3
0053ffd70054ffc30055ffd70056ffd70058ffd70082ffc30083ffc30084ffc30085ffc30086ffc30087ffc30088ff850089ffd70094ffd70095ffd70096ffd70097ffd70098ffd7009affd700a2ffc3
00a3ffc300a4ffc300a5ffc300a6ffc300a7ffc300a8ffc300a9ffc300aaffc300abffc300acffc300adffc300b3ffd700b4ffc300b5ffc300b6ffc300b7ffc300b8ffc300baffc300bbffd700bcffd7
00bdffd700beffd700c2ffc300c3ffc300c4ffc300c5ffc300c6ffc300c7ffc300c8ffd700c9ffc300cbffc300cdffc300ceffd700cfffc300d5ffc300d7ffc300d9ffc300dbffc300ddffc300deffd7
00e0ffd700e2ffd700e4ffd70106ffd70108ffd7010affd7010effd7010fffc30111ffc30112ffd70113ffc30114ffd70115ffc3011dffd7011fffd70121ffd70123ffd70143ffc30144ffc3014affd7
016200140166ffd7016dffd70171ff9a0172ffc30173ffd70175ffd70178ffc30179ffc301880029018dffd702070052020b00520228ffc30258ffc30259ffc3025fffd70260ffc3031dffc3031effc3
031fffc30321ffc30322ffc30323ffc30324ffc30325ffc30326ffc30327ffc30328ffc30329ffc3032affc3032bffc3032cffc3032dffc3032effc3032fffc30330ffc30331ffc30332ffc30333ffc3
0334ffc30336ffc30338ffc3033affc3033cffc3033effc30340ffc30342ffc30344ffc30349ffd7034affc3034bffd7034cffc3034dffd7034effc3034fffd70350ffc30351ffd70352ffc30353ffd7
0354ffc30355ffd70356ffc30357ffd70358ffc30359ffd7035affc3035bffd7035cffc3035dffd7035effc3035fffd70360ffc30362ffd70364ffd70366ffd70368ffd7036affd7036cffd7036effd7
00080166ffec016dffec0173ffec0192ffd70195ffd7019700290198ffd7019affd70046000fffc30011ffc30024ffec002cffec0037ffc30039ffd7003affec003bffd7003cffd7003dffec0082ffec
0083ffec0084ffec0085ffec0086ffec0087ffec0088ffd7008effec008fffec0090ffec0091ffec009fffd700c2ffec00c4ffec00c6ffec00ecffec00f0ffec00f2ffec0124ffc30126ffc30136ffec
0138ffd7013affd7013bffec013dffec013fffec0143ffec015fffec0162ffd70167ffec0169ffec016cffec0170ffec0171ffc30172ffd70174ffd70178ffd701faffec01fcffec01feffec0200ffd7
0228ffec0258ffec031dffec031fffec0321ffec0323ffec0325ffec0327ffec0329ffec032bffec032dffec032fffec0331ffec0333ffec0345ffec0347ffec036fffd70371ffd70373ffd700050166
ffec016dffec0173ffec0181ffec01880029002a00050029000a00290026ffd7002affd70032ffd70034ffd70089ffd70094ffd70095ffd70096ffd70097ffd70098ffd7009affd700c8ffd700ceffd7
00deffd700e0ffd700e2ffd700e4ffd7010effd70112ffd70114ffd70166ffd7016dffd70173ffae0191ffd70197002902070029020b0029025fffd70349ffd7034bffd7034dffd7034fffd70351ffd7
0353ffd70355ffd70357ffd70359ffd7035bffd7035dffd7035fffd700cf0005ffae000affae002200290024ffc30026ffec002affec0032ffec0034ffec0037ff850038ffec0039ffc3003affd7003c
ff9a0044ffc30046ffc30047ffc30048ffc3004affc30050ffd70051ffd70052ffc30053ffd70054ffc30055ffd70056ffd70058ffd70082ffc30083ffc30084ffc30085ffc30086ffc30087ffc30088
ff850089ffec0094ffec0095ffec0096ffec0097ffec0098ffec009affec009bffec009cffec009dffec009effec009fff9a00a2ffc300a3ffc300a4ffc300a5ffc300a6ffc300a7ffc300a8ffc300a9
ffc300aaffc300abffc300acffc300adffc300b3ffd700b4ffc300b5ffc300b6ffc300b7ffc300b8ffc300baffc300bbffd700bcffd700bdffd700beffd700c2ffc300c3ffc300c4ffc300c5ffc300c6
ffc300c7ffc300c8ffec00c9ffc300cbffc300cdffc300ceffec00cfffc300d5ffc300d7ffc300d9ffc300dbffc300ddffc300deffec00e0ffec00e2ffec00e4ffec0106ffd70108ffd7010affd7010e
ffec010fffc30111ffc30112ffec0113ffc30114ffd70115ffc3011dffd7011fffd70121ffd70123ffd70124ff850126ff85012cffec0130ffec0132ffec0134ffec0136ffd70138ff9a013aff9a0143
ffc30144ffc3014affd70166ffec016dffec0171ff850172ff9a0173ffd70175ffc30178ff9a0191ffd701faffd701fcffd701feffd70200ff9a0207ffae020bffae0228ffc30258ffc30259ffc3025f
ffec0260ffc30261ffec031dffc3031effc3031fffc30321ffc30322ffc30323ffc30324ffc30325ffc30326ffc30327ffc30328ffc30329ffc3032affc3032bffc3032cffc3032dffc3032effc3032f
ffc30330ffc30331ffc30332ffc30333ffc30334ffc30336ffc30338ffc3033affc3033cffc3033effc30340ffc30342ffc30344ffc30349ffec034affc3034bffec034cffc3034dffec034effc3034f
ffec0350ffc30351ffec0352ffc30353ffec0354ffc30355ffec0356ffc30357ffec0358ffc30359ffec035affc3035bffec035cffc3035dffec035effc3035fffec0360ffc30361ffec0362ffd70363
ffec0364ffd70365ffec0366ffd70367ffec0368ffd70369ffec036affd7036bffec036cffd7036dffec036effd7036fff9a0371ff9a0373ff9a00030166ffec016dffec018800290046000fffc30011
ffc30024ffec002cffec0037ffc30039ffd7003affec003bffd7003cffd7003dffec0082ffec0083ffec0084ffec0085ffec0086ffec0087ffec0088ffd7008effec008fffec0090ffec0091ffec009f
ffd700c2ffec00c4ffec00c6ffec00ecffec00f0ffec00f2ffec0124ffc30126ffc30136ffec0138ffd7013affd7013bffec013dffec013fffec0143ffec015fffec0167ffec0169ffec016cffec0170
ffec0171ffc30172ffd70174ffd70177ffec0178ffd701faffec01fcffec01feffec0200ffd70228ffec0258ffec031dffec031fffec0321ffec0323ffec0325ffec0327ffec0329ffec032bffec032d
ffec032fffec0331ffec0333ffec0345ffec0347ffec036fffd70371ffd70373ffd70036000fff330011ff330024ffae0026ffec003bffec003cffec003dffd70082ffae0083ffae0084ffae0085ffae
0086ffae0087ffae0088ff710089ffec009fffec00c2ffae00c4ffae00c6ffae00c8ffec00ceffec0138ffec013affec013bffd7013dffd7013fffd70143ffae015fffae0162ffae0164ffec0169ffae
0174ffec0178ffec0181ffd70188ffd7018effd70200ffec0228ffae0258ffae031dffae031fffae0321ffae0323ffae0325ffae0327ffae0329ffae032bffae032dffae032fffae0331ffae0333ffae
036fffec0371ffec0373ffec002900050029000a00290026ffd7002affd70032ffd70034ffd70089ffd70094ffd70095ffd70096ffd70097ffd70098ffd7009affd700c8ffd700ceffd700deffd700e0
ffd700e2ffd700e4ffd7010effd70112ffd70114ffd70166ffd7016dffd70173ffd70188002902070029020b0029025fffd70349ffd7034bffd7034dffd7034fffd70351ffd70353ffd70355ffd70357
ffd70359ffd7035bffd7035dffd7035fffd700e900050052000a0052000fff9a0010ff9a0011ff9a002200290024ff850026ffc3002affc30032ffc30034ffc30036ffec003700140044ff850046ff85
0047ff850048ff85004aff9a0050ffae0051ffae0052ff850053ffae0054ff850055ffae0056ff850058ffae0059ffc3005affc3005bffc3005cffc3005dffc3006dff9a007dffd70082ff850083ff85
0084ff850085ff850086ff850087ff850088ff710089ffc30094ffc30095ffc30096ffc30097ffc30098ffc3009affc300a2ff8500a3ff8500a4ff8500a5ff8500a6ff8500a7ff8500a8ff8500a9ff85
00aaff8500abff8500acff8500adff8500b3ffae00b4ff8500b5ff8500b6ff8500b7ff8500b8ff8500baff8500bbffae00bcffae00bdffae00beffae00bfffc300c1ffc300c2ff8500c3ff8500c4ff85
00c5ff8500c6ff8500c7ff8500c8ffc300c9ff8500cbff8500cdff8500ceffc300cfff8500d5ff8500d7ff8500d9ff8500dbff8500ddff8500deffc300e0ffc300e2ffc300e4ffc30106ffae0108ffae
010affae010effc3010fff850111ff850112ffc30113ff850114ffc30115ff85011cffec011dff85011fff850120ffec0121ff850122ffec0123ff8501240014012600140137ffc30139ffc30140ffc3
0143ff850144ff85014aff85015fff850162ff9a0166ffc30169ff85016dffc3017100140173ffc30176ffc30179ff85017aff85017bffae017dffae017eff850180ffc30181ffae0182ff850184ffae
0186ffae0187ffae018800140189ffae018affc3018cff85018eff85018fff850190ff850191ffc30192ffae0193ff850194ffc30195ffae0196ff850197003d0198ffae0199ff85019affae019bff85
01fbffc301fdffc301ffffc30201ffc30202ff9a0203ff9a02070052020b00520228ff850258ff850259ff85025fffc30260ff85031dff85031eff85031fff850321ff850322ff850323ff850324ff85
0325ff850326ff850327ff850328ff850329ff85032aff85032bff85032cff85032dff85032eff85032fff850330ff850331ff850332ff850333ff850334ff850336ff850338ff85033aff85033cff85
033eff850340ff850342ff850344ff850349ffc3034aff85034bffc3034cff85034dffc3034eff85034fffc30350ff850351ffc30352ff850353ffc30354ff850355ffc30356ff850357ffc30358ff85
0359ffc3035aff85035bffc3035cff85035dffc3035eff85035fffc30360ff850362ffae0364ffae0366ffae0368ffae036affae036cffae036effae0370ffc30372ffc30374ffc3002d000fff9a0010
ffc30011ff9a006dffc3015fff9a0162ffc30166ffd70169ff9a016dffd70173ffc30176ffd70179ff9a017affae017bffc3017cffec017dffd7017eff9a017fffd70181ff9a0182ffae0183ffd70184
ffc30185ffd70186ffc30187ffc3018800140189ffc3018bffec018cff9a018eff9a018fff9a0190ff9a0191ffec0192ffc30193ff9a0194ffd70195ffc30196ffae019700290198ffc30199ff9a019a
ffc3019bffae0202ffc30203ffc3000a015fffd70162ffd70167ffec0169ffd70171ffc30172ffc30174ffc30177ffd70178ffc30188ffd700170166ffd7016dffd70173ffc30179ffec017cffec017d
ffec017effec0181ffec0185ffec0186ffec01880014018cffec018fffec0190ffec0191ffd70192ffd70193ffec0195ffd70196ffec0198ffd70199ffec019affd7019bffec000c000fffae0011ffae
015fffc30162ffd70169ffc30181ffd7018effd70190ffec0193ffec0196ffec0199ffec019bffec00030171ffc30172ffd70178ffd7002c00050029000a00290026ffec002affec0032ffec0034ffec
0089ffec0094ffec0095ffec0096ffec0097ffec0098ffec009affec00c8ffec00ceffec00deffec00e0ffec00e2ffec00e4ffec010effec0112ffec0114ffec0166ffec016dffec0173ffd70180ffec
0181ffec018800140191ffec02070029020b0029025fffec0349ffec034bffec034dffec034fffec0351ffec0353ffec0355ffec0357ffec0359ffec035bffec035dffec035fffec00ee000500520009
ffc3000a0052000c003d000d0029000fff9a0010ff5c0011ff9a002200290024ff9a0026ffd7002affd7002dffbe0030ffec0032ffd70034ffd70036ffec003700270040003d0044ff9a0046ff9a0047
ff9a0048ff9a0049ffe5004aff9a0050ffc30051ffc30052ff9a0053ffc30054ff9a0055ffc30056ffae0058ffc3005bffd7005cffec005dffc30060003d006dffc3007dffd70082ff9a0083ff9a0084
ff9a0085ff9a0086ff9a0087ff9a0088ff710089ffd70094ffd70095ffd70096ffd70097ffd70098ffd7009affd700a2ff9a00a3ff9a00a4ff9a00a5ff9a00a6ff9a00a7ff9a00a8ff9a00a9ff9a00aa
ff9a00abff9a00acff9a00adff9a00b3ffc300b4ff9a00b5ff9a00b6ff9a00b7ff9a00b8ff9a00baff9a00bbffc300bcffc300bdffc300beffc300bfffec00c1ffec00c2ff9a00c3ff9a00c4ff9a00c5
ff9a00c6ff9a00c7ff9a00c8ffd700c9ff9a00cbff9a00cdff9a00ceffd700cfff9a00d5ff9a00d7ff9a00d9ff9a00dbff9a00ddff9a00deffd700e0ffd700e2ffd700e4ffd700f6ffbe0106ffc30108
ffc3010affc3010effd7010fff9a0111ff9a0112ffd70113ff9a0114ffd70115ff9a011cffec011dffae011fffae0120ffec0121ffae0122ffec0123ffae01240027012600270139ffec0140ffc30143
ff9a0144ff9a014affae015fff9a0162ffc30166ffd70169ff9a016dffd70173ffc30176ffd70179ff9a017affae017bffc3017cffec017dffc3017eff9a017fffd70180ffec0181ff9a0182ff9a0183
ffd70184ffc30186ffc30187ffc30189ffc3018affec018bffec018cff9a018dffd7018eff9a018fff9a0190ff9a0191ffd70192ffc30193ff9a0194ffd70195ffc30196ff9a019700290198ffc30199
ff9a019affc3019bff9a0201ffec02070052020b0052020fffd70228ff9a0258ff9a0259ff9a025fffd70260ff9a031dff9a031eff9a031fff9a0321ff9a0322ff9a0323ff9a0324ff9a0325ff9a0326
ff9a0327ff9a0328ff9a0329ff9a032aff9a032bff9a032cff9a032dff9a032eff9a032fff9a0330ff9a0331ff9a0332ff9a0333ff9a0334ff9a0336ff9a0338ff9a033aff9a033cff9a033eff9a0340
ff9a0342ff9a0344ff9a0349ffd7034aff9a034bffd7034cff9a034dffd7034eff9a034fffd70350ff9a0351ffd70352ff9a0353ffd70354ff9a0355ffd70356ff9a0357ffd70358ff9a0359ffd7035a
ff9a035bffd7035cff9a035dffd7035eff9a035fffd70360ff9a0362ffc30364ffc30366ffc30368ffc3036affc3036cffc3036effc30370ffec0372ffec0374ffec00010188002900060179ffd7017e
ffec0181ffec018cffec0190ffec0193ffec00140179ffec017cffec017dffec0180ffec0181ffec0185ffec0186ffec01880029018affd7018cffec018dffec018fffec0190ffec0191ffd70192ffec
0195ffec0197ffec0198ffec0199ffec019affec00010194ffec000b0005003d000a003d004900140181ffec018d0029018effec0191001401940014019600140207003d020b003d00020191ffec0194
ffd7000b0005003d000a003d0179ffd70181ffec018cffd7018fffec0190ffd70193ffd70199ffd70207003d020b003d001c0179ffae017affd7017dffd7017effc30180ffd70181ffc30182ffd70183
ffd70184ffd70185ffd70186ffd70187ffec01880029018affd7018bffd7018cffc3018dffc3018fffc30190ffc30191ffc30192ffc30193ffc30195ffc30196ffc30198ffc30199ffc3019affc3019b
ffc3001000050052000a00520179ffd7017dffec017effec0180ffd70185ffd70186ffec01880029018affd7018dffd70191ffd70193ffd70197ffec02070052020b0052000900050029000a00290179
ffd7017effd70181ffd70182ffec018cffec02070029020b0029000d0005003d000a003d00490014017dffec0180ffec0185ffd70188003d018affd7018dffd70191ffc30194ffd70207003d020b003d
00070005003d000a003d00490014018d0014018effec0207003d020b003d0003001e003d017effec018800290005005bffd7005dffec0140ffec0191ffec0194ffd70004017effec01800014018a0029
019100140007005bffd7005dffec0140ffec01800014018a0014018d001401910029000e00050052000a005200490014007d0029018000140181ffec018a0029018cffd7018d0014018fffd70190ffec
0191001402070052020b00520004005bffd7005dffec0140ffec0194ffec001500050052000a0052004900140179ffd7017affd7017effd7018000140181ffd70182ffd70185ffec018cffd7018fffd7
0190ffd70192ffec0193ffec019400290195ffd70196ffd70199ffec02070052020b00520004005bffd7005dffec0140ffec0180001400130179ffec017dffec017effd70180ffd70181ffec0185ffd7
01880029018cffec018dffd7018fffec0190ffec0191ffd70192ffec0194ffec0195ffd70198ffec0199ffec019affec019bffec003800050029000a00290010ffd70026ffec002d003d0032ffec0034
ffec0084ffec0089ffec008affec008fffec0094ffec0095ffec0096ffec0097ffec0098ffec009affec00c8ffec00ceffec00f6003d010effec0112ffec01b8ffec01bbffec01beffec02070029020b
0029025fffec026cffec027effec0284ffec0286ffec0288ffec028affec028cffec02b1ffec02b3ffec02e1ffec02e3ffec02efffec02f1ffec02f3ffec0315ffec0319ffec0349ffec034bffec034d
ffec034fffec0351ffec0353ffec0355ffec0357ffec0359ffec035bffec035dffec035fffec003d0005ffc3000affc3019dff8501a6ff8501b0ffd701bcff8501bdffd701bfffec01c1ffc301c4ff85
01dcffc301ddffec01dfffec01e1ffc301e4ffae0207ffc3020bffc3026dffec0276ffd7027cffc3027dffc30280ffc30281ffec0282ffc30283ffec028bffec029fffd702a9ff8502aaffc302b5ff85
02b6ffc302b7ff8502b8ffec02b9ff8502baffec02bbffd702bdff8502bfffc302c0ffc302c1ffc302c2ffc302caffd702d4ffc302d5ffc302e5ffd702f7ffd702f8ffec02f9ffd702faffec02fbffd7
02fcffec02fdffc302feffc30305ffd70307ffd7030dffc3030effec030fffc30310ffec0317ff850318ffc300d400050052000a00520010ff5c0026ffec002affec0032ffec0034ffec0037ff850038
ffec0039ffae003affc3003cff9a0089ffec0094ffec0095ffec0096ffec0097ffec0098ffec009affec009bffec009cffec009dffec009effec009fff9a00c8ffec00ceffec00deffec00e0ffec00e2
ffec00e4ffec010effec0112ffec0114ffec0124ff850126ff85012cffec0130ffec0132ffec0134ffec0136ffc30138ff9a013aff9a019fffec01a4ff9a01aaff8501aeff8501b5ff8501b8ffd701bb
ffec01beffc301caff8501cbffd701ccffc301cdffc301ceff5c01cfff8501d0ffc301d1ffc301d2ffc301d3ffc301d4ffc301d5ff5c01d6ffc301d7ffc301d8ff8501d9ffc301daffc301dbff8501dc
ffc301ddffc301deff8501dfffc301e0ffc301e1ffc301e2ffc301e3ffc301e4ffc301e5ffc301e6ffc301e7ffc301e8ffc301e9ff9a01eaff8501ecffc301edff8501eeffae01f0003d01f2ff5c01f3
ffc301f5ffc301f7ffc301f9ffc301faffc301fcffc301feffc30200ff9a02070052020b0052025fffec0261ffec026aff85026bffc3026cffd7026dffc30272ff85027dffc3027effd7027fff850281
ffc30283ffc30284ffd70285ff850286ffd70287ff850288ffd70289ff85028affd7028bffc3028cffd7028dff850296ffc3029affc302a0ffc302a4ffc302a6ffc302aaffc302acffc302aeffc302b0
ffc302b1ffd702b2ff8502b3ffd702b4ff8502b6ffc302b8ffc302baffc302bcffc302c0ffc302c2ffc302c4ffc302c5ffae02c6ff7102c7ffae02cbffc302ceff8502cfff5c02d1ffc302d3ffc302d5
ffc302d7ffc302d9ff8502daff8502dbff8502dcff8502ddff8502e0ff8502e1ffd702e3ffd702e6ffc302ecffc302eeffc302efffd702f0ff8502f1ffd702f2ff8502f3ffd702f4ff8502f8ffc302fa
ffc302fcffc302feffc30300ffc30306ffc30308ffc30309ff5c030aff85030bff5c030cff85030effc30310ffc30311ff850312ff5c0314ffc30315ffd70316ff850318ffc30319ffd7031aff85031b
ff85031cff5c0349ffec034bffec034dffec034fffec0351ffec0353ffec0355ffec0357ffec0359ffec035bffec035dffec035fffec0361ffec0363ffec0365ffec0367ffec0369ffec036bffec036d
ffec036fff9a0371ff9a0373ff9a001f00050029000a0029019fffd701b8ffec01bbffd701beffec01deffd701e1ffd702070029020b0029026cffec027dffd7027effec0284ffec0286ffec0288ffec
028affec028cffec02b1ffec02b3ffec02c0ffd702c2ffd702d5ffd702e1ffec02e3ffec02efffec02f1ffec02f3ffec02feffd70315ffec0319ffec000501a0ffec01dcffd702aaffd702b6ffd70318
ffd7001a019fffec01dcffd701ddffec01e1ffd701e4ffec026dffec027dffd70281ffec0283ffec028bffec02aaffd702b6ffd702b8ffec02baffec02c0ffd702c2ffd702c5ffd702c7ffd702d5ffd7
02f8ffec02faffec02fcffec02feffd7030effec0310ffec0318ffd7003700050029000a00290026ffec002affec0032ffec0034ffec0089ffec0094ffec0095ffec0096ffec0097ffec0098ffec009a
ffec00c8ffec00ceffec00deffec00e0ffec00e2ffec00e4ffec010effec0112ffec0114ffec019fffec01b8ffec01bbffec020b0029025fffec026cffec027effec0284ffec0286ffec0288ffec028a
ffec028cffec02b1ffec02b3ffec02e1ffec02e3ffec02efffec02f1ffec02f3ffec0315ffec0319ffec0349ffec034bffec034dffec034fffec0351ffec0353ffec0355ffec0357ffec0359ffec035b
ffec035dffec035fffec00340005ff9a000aff9a01a6ff8501a8ffd701b0ffd701b5ffec01bcff8501bdffec01bfffec01c1ffae01c4ff8501dcffd701e1ffd701e4ffc30207ff9a020bff9a0276ffd7
027cffae027dffd70280ffae0282ffae029fffd702a9ff8502aaffd702b5ff8502b6ffd702b7ff8502b9ff8502bbffd702bdff8502bfffae02c0ffd702c1ffae02c2ffd702caffd702ceffec02d4ffae
02d5ffd702e5ffd702f7ffec02f9ffec02fbffec02fdffae02feffd70305ffd70307ffd7030dffae030fffae0311ffec0317ff850318ffd7031bffec00310005ff9a000aff9a019dff8501a6ff8501a8
ffd701b0ffec01bcff8501bdffd701c1ffae01c4ff8501dcffd701dfffec01e1ffec01e4ffd70207ff9a020bff9a0276ffec027cffae027dffec0280ffae0282ffae029fffec02a9ff8502aaffd702b5
ff8502b6ffd702b7ff8502b9ff8502bbffec02bdff8502bfffae02c0ffec02c1ffae02c2ffec02caffec02d4ffae02d5ffec02e5ffec02f7ffd702f9ffd702fbffd702fdffae02feffec0305ffec0307
ffec030dffae030fffae0317ff850318ffd7001d019dff9a01a6ff9a01c4ff8501dcffd701ddffec01e1ffd701e4ffc301f6ffec026dffec027dffd70281ffec0283ffec028bffec02aaffd702b6ffd7
02b8ffec02baffec02c0ffd702c2ffd702c5ffd702c7ffd702d5ffd702f8ffec02faffec02fcffec02feffd7030effec0310ffec0318ffd7006d00050029000a00290026ffd7002affd70032ffd70034
ffd70089ffd70094ffd70095ffd70096ffd70097ffd70098ffd7009affd700c8ffd700ceffd700deffd700e0ffd700e2ffd700e4ffd7010effd70112ffd70114ffd7019fffd701a4002901b5002901b8
ffd701bbffd701beffae01cbffec01ce001401cfffd701d8ffd701dbffd701dcffd701ddffd701deffd701e1ffc301e4ffd701eaffd701edffd702070029020b0029025fffd7026affd7026cffd7026d
ffd7027dffc3027effd7027fffd70281ffd70283ffd70284ffd70285ffd70286ffd70287ffd70288ffd70289ffd7028affd7028bffd7028cffd7028dffd702aaffd702b1ffd702b2ffd702b3ffd702b4
ffd702b6ffd702b8ffd702baffd702c0ffc302c2ffc302ce002902d5ffc302e0ffd702e1ffd702e3ffd702efffd702f0ffd702f1ffd702f2ffd702f3ffd702f4ffd702f8ffd702faffd702fcffd702fe
ffc3030affd7030cffd7030effd70310ffd7031100290315ffd70316ffd70318ffd70319ffd7031affd7031b00290349ffd7034bffd7034dffd7034fffd70351ffd70353ffd70355ffd70357ffd70359
ffd7035bffd7035dffd7035fffd7006700050052000a005201a4ff9a01aaff8501aeff7101b5ff9a01bbffd701bc002901beffd701c4001401c9ffec01caffae01ccffd701cdffd701ceff5c01cfffae
01d1ffd701d2ffd701d3ffd701d4ffd701d5ff5c01d6ffd701d7ffd701d8ffae01d9ffd701daffd701dbffae01deffae01e0ffd701e1ffd701e2ffd701e3ffd701e5ffd701e6ffd701e8ffd701e9ffd7
01eaffd701ecffd701edffae01eeffd701f0005201f2ff7101f3ffd701f5ffd701f7ffd701f9ffd702070052020b0052026affae026bffd70272ff85027dffd7027fffae0285ffae0287ffae0289ffae
028dffae0296ffd7029affd702a4ffd702a6ffd702a9002902acffd702aeffd702b0ffd702b2ffae02b4ffae02b5002902b7002902b9002902bd002902c0ffd702c2ffd702c4ffd702ceff9a02cfff5c
02d1ffd702d3ffd702d5ffd702d7ffd702d9ff8502daffae02dbff8502dcffae02ddff8502e0ffae02ecffd702eeffd702f0ffae02f2ffae02f4ffae02feffd70300ffd7030affae030cffae0311ff9a
0312ff5c0314ffd70316ffae03170029031affae031bff9a031cff5c00890005ffae000affae0026ffec002affec0032ffec0034ffec0037ff850038ffec0039ffc3003affd7003cff9a0089ffec0094
ffec0095ffec0096ffec0097ffec0098ffec009affec009bffec009cffec009dffec009effec009fff9a00c8ffec00ceffec00deffec00e0ffec00e2ffec00e4ffec010effec0112ffec0114ffd70124
ff850126ff85012cffec0130ffec0132ffec0134ffec0136ffd70138ff9a013aff9a019dff9a019fffec01a4003d01a6ff8501ae002901b5002901b8ffec01bbffec01bcff8501beffd701c1ffae01c4
ff9a01d5002901dcffd701e1ffc301e4ffc301e7002901f2002901faffd701fcffd701feffd70200ff9a0207ffae020bffae025fffec0261ffec026cffec027cffae027dffc3027effec0280ffae0282
ffae0284ffec0286ffec0288ffec028affec028cffec02a9ff8502aaffd702b1ffec02b3ffec02b5ff8502b6ffd702b7ff8502b9ff8502bdff8502bfffae02c0ffc302c1ffae02c2ffc302c5ffae02c6
ffc302c7ffae02ce002902cf002902d4ffae02d5ffc302e1ffec02e3ffec02efffec02f1ffec02f3ffec02fdffae02feffc3030dffae030fffae03110029031200290315ffec0317ff850318ffd70319
ffec031b0029031c00290349ffec034bffec034dffec034fffec0351ffec0353ffec0355ffec0357ffec0359ffec035bffec035dffec035fffec0361ffec0363ffec0365ffec0367ffec0369ffec036b
ffec036dffec036fff9a0371ff9a0373ff9a002a019dffd701bcffd701bdffd701bfffec01c1ffc301c4ffd701d0ffec01dcffd701e1ffd701e4ffd7027cffc3027dffd70280ffc30282ffc302a0ffec
02a9ffd702aaffd702b5ffd702b6ffd702b7ffd702b9ffd702bcffec02bdffd702bfffc302c0ffd702c1ffc302c2ffd702cbffec02d4ffc302d5ffd702e6ffec02f7ffd702f9ffd702fbffd702fdffc3
02feffd70306ffec0308ffec030dffc3030fffc30317ffd70318ffd700210037ffec0039ffec003bffec003cffec009fffec0124ffec0126ffec0138ffec013affec01b0ffec01bcffec01bdffec01bf
ffec0200ffec0276ffec029fffec02a9ffec02b5ffec02b7ffec02b9ffec02bbffec02bdffec02caffec02e5ffec02f7ffec02f9ffec02fbffec0305ffec0307ffec0317ffec036fffec0371ffec0373
ffec008f00050052000a00520010ff5c019fffec01a4ff9a01aaff8501aeff8501b5ff8501b8ffd701beffc301caff8501ccffc301cdffc301ceff5c01cfff8501d0ffc301d1ffc301d2ffc301d3ffc3
01d4ffc301d5ff5c01d6ffc301d7ffc301d8ff8501d9ffc301daffc301dbff8501dcffc301ddffc301deff8501dfffc301e0ffc301e1ffc301e2ffc301e3ffc301e4ffc301e5ffc301e6ffc301e7ffc3
01e8ffc301e9ff9a01eaff8501ecffc301edff8501eeffae01f0003d01f2ff5c01f3ffc301f5ffc301f7ffc301f9ffc302070052020b0052026aff85026bffc3026cffd7026dffc30272ff85027dffc3
027effd7027fff850281ffc30283ffc30284ffd70285ff850286ffd70287ff850288ffd70289ff85028affd7028bffc3028cffd7028dff850296ffc3029affc302a0ffc302a4ffc302a6ffc302aaffc3
02acffc302aeffc302b0ffc302b1ffd702b2ff8502b3ffd702b4ff8502b6ffc302b8ffc302baffc302bcffc302c0ffc302c2ffc302c4ffc302c5ffae02c6ff7102c7ffae02cbffc302ceff8502cfff5c
02d1ffc302d3ffc302d5ffc302d7ffc302d9ff8502daff8502dbff8502dcff8502ddff8502e0ff8502e1ffd702e3ffd702e6ffc302ecffc302eeffc302efffd702f0ff8502f1ffd702f2ff8502f3ffd7
02f4ff8502f8ffc302faffc302fcffc302feffc30300ffc30306ffc30308ffc30309ff5c030aff85030bff5c030cff85030effc30310ffc30311ff850312ff5c0314ffc30315ffd70316ff850318ffc3
0319ffd7031aff85031bff85031cff5c0042019dffd701a300f601a4002901a6ffd701aa001401ae002901b5002901b8ffec01bbffec01bcffd701beffec01c1ffd701c4ffd701ce002901d5002901e1
ffd701e7002901f1006601f20029026cffec02720014027cffd7027dffd7027effec0280ffd70282ffd70284ffec0286ffec0288ffec028affec028cffec02a9ffd702b1ffec02b3ffec02b5ffd702b7
ffd702b9ffd702bdffd702bfffd702c0ffd702c1ffd702c2ffd702c6ffd702ce002902cf002902d4ffd702d5ffd702d9001402db001402dd001402e1ffec02e3ffec02efffec02f1ffec02f3ffec02fd
ffd702feffd7030dffd7030fffd703110029031200290315ffec0317ffd70319ffec031b0029031c002900310005003d000a003d0010ffd7019fffd701a4002901b5002901b8ffd701bbffd701beffc3
01cbffd701d5002901dcffd701e1ffae01f200290207003d020b003d026cffd7027dffae027effd70284ffd70286ffd70288ffd7028affd7028cffd702aaffd702b1ffd702b3ffd702b6ffd702c0ffae
02c2ffae02c5ffc302c6ff9a02c7ffc302ce002902cf002902d5ffae02e1ffd702e3ffd702efffd702f1ffd702f3ffd702feffae03110029031200290315ffd70318ffd70319ffd7031b0029031c0029
002600050029000a002901b1ffec01b5ffd701bcffd701bdffec01beffec01bfffd701c1ffec01c4ffec01c7ffec02070029020b0029027cffec0280ffec0282ffec02a1ffec02a9ffd702b5ffd702b7
ffd702b9ffd702bdffd702bfffec02c1ffec02ceffd702d4ffec02e7ffec02e9ffec02f5ffec02f7ffec02f9ffec02fbffec02fdffec030dffec030fffec0311ffd70317ffd7031bffd7004c00050029
000a00290010ffd70026ffd7002affd70032ffd70034ffd70089ffd70094ffd70095ffd70096ffd70097ffd70098ffd7009affd700c8ffd700ceffd700deffd700e0ffd700e2ffd700e4ffd7010effd7
0112ffd70114ffd7019fffd701a4003d01b5002901b8ffd701bbffd701beffae01cbffd701d5002901e1ffae01e4ffd701f2002902070029020b0029025fffd7026cffd7027dffae027effd70284ffd7
0286ffd70288ffd7028affd7028cffd702b1ffd702b3ffd702c0ffae02c2ffae02ce002902cf002902d5ffae02e1ffd702e3ffd702efffd702f1ffd702f3ffd702feffae03110029031200290315ffd7
0319ffd7031b0029031c00290349ffd7034bffd7034dffd7034fffd70351ffd70353ffd70355ffd70357ffd70359ffd7035bffd7035dffd7035fffd70056000fffc30011ffc30024ffec002cffec0037
ffc30039ffd7003affec003bffd7003cffd7003dffec0082ffec0083ffec0084ffec0085ffec0086ffec0087ffec0088ffd7008effec008fffec0090ffec0091ffec009fffd700c2ffec00c4ffec00c6
ffec00ecffec00f0ffec00f2ffec0124ffc30126ffc30136ffec0138ffd7013affd7013bffec013dffec013fffec0143ffec019dffd701a4ffd701a6ffd701aaffec01aeffec01b0ffd701bcffc301bf
ffec01c4ffd701faffec01fcffec01feffec0200ffd70228ffec0258ffec0272ffec0276ffd7029fffd702a9ffc302b5ffc302b7ffc302b9ffc302bbffd702bdffc302caffd702d9ffec02dbffec02dd
ffec02e5ffd70305ffd70307ffd70317ffc3031dffec031fffec0321ffec0323ffec0325ffec0327ffec0329ffec032bffec032dffec032fffec0331ffec0333ffec0345ffec0347ffec036fffd70371
ffd70373ffd700450005003d000a003d000fff330011ff330024ffae0026ffec003bffec003cffec003dffd70082ffae0083ffae0084ffae0085ffae0086ffae0087ffae0088ff710089ffec009fffec
00c2ffae00c4ffae00c6ffae00c8ffec00ceffec0138ffec013affec013bffd7013dffd7013fffd70143ffae01a4ffae01aaffae01aeff9a01b5ff9a01bbffec01ceff9a01d5ffae01f2ffae0200ffec
0207003d020b003d0228ffae0258ffae0272ffae02ceff9a02cfffae02d9ffae02dbffae02ddffae0309ffae030bffae0311ff9a0312ffae031bff9a031cffae031dffae031fffae0321ffae0323ffae
0325ffae0327ffae0329ffae032bffae032dffae032fffae0331ffae0333ffae036fffec0371ffec0373ffec004200050029000a0029000c00290026ffd7002affd70032ffd70034ffd7004000290060
00290089ffd70094ffd70095ffd70096ffd70097ffd70098ffd7009affd700c8ffd700ceffd700deffd700e0ffd700e2ffd700e4ffd7010effd70112ffd7019fffd701b8ffd701bbffd701beffc301e1
ffc302070029020b0029025fffd7026cffd7027dffc3027effd70284ffd70286ffd70288ffd7028affd7028cffd702b1ffd702b3ffd702c0ffc302c2ffc302c6ffae02d5ffc302e1ffd702e3ffd702ef
ffd702f1ffd702f3ffd702feffc30315ffd70319ffd70349ffd7034bffd7034dffd7034fffd70351ffd70353ffd70355ffd70357ffd70359ffd7035bffd7035dffd7035fffd7015200050052000a0052
000fff9a0010ff9a0011ff9a002200290024ff850026ffc3002affc30032ffc30034ffc30036ffec003700140044ff850046ff850047ff850048ff85004aff9a0050ffae0051ffae0052ff850053ffae
0054ff850055ffae0056ff850058ffae0059ffc3005affc3005bffc3005cffc3005dffc30082ff850083ff850084ff850085ff850086ff850087ff850088ff710089ffc30094ffc30095ffc30096ffc3
0097ffc30098ffc3009affc300a2ff8500a3ff8500a4ff8500a5ff8500a6ff8500a7ff8500a8ff8500a9ff8500aaff8500abff8500acff8500adff8500b3ffae00b4ff8500b5ff8500b6ff8500b7ff85
00b8ff8500baff8500bbffae00bcffae00bdffae00beffae00bfffc300c1ffc300c2ff8500c3ff8500c4ff8500c5ff8500c6ff8500c7ff8500c8ffc300c9ff8500cbff8500cdff8500ceffc300cfff85
00d5ff8500d7ff8500d9ff8500dbff8500ddff8500deffc300e0ffc300e2ffc300e4ffc30106ffae0108ffae010affae010effc3010fff850111ff850112ffc30113ff850114ffc30115ff85011cffec
011dff85011fff850120ffec0121ff850122ffec0123ff8501240014012600140137ffc30139ffc30140ffc30143ff850144ff85014aff85019fffc301a0ffec01a4ffae01aaff8501aeff9a01b5ff9a
01b8ffc301bbffc301bc001401beffc301c4001401caff8501ccffc301cdffc301ceff7101cfff8501d0ffc301d1ffc301d2ffc301d4ffc301d5ff7101d6ffc301d7ffc301d8ff8501d9ffc301daffc3
01dbff8501dcffc301ddffc301deff8501dfffc301e0ffc301e1ffc301e2ffc301e3ffc301e5ffc301e6ffc301e8ffc301e9ffc301eaff8501eb002901ecffc301edff8501eeff8501f0005201f2ff85
01f3ffc301f5ffc301f7ffc301f9ffc301fbffc301fdffc301ffffc30201ffc302070052020b00520228ff850258ff850259ff85025fffc30260ff85026aff85026bffc3026cffc3026dffc30272ff85
027dffc3027effc3027fff850281ffc30283ffc30284ffc30285ff850286ffc30287ff850288ffc30289ff85028affc3028bffc3028cffc3028dff850296ffc3029affc302a0ffc302a4ffc302a6ffc3
02a9001402aaffc302acffc302aeffc302b0ffc302b1ffc302b2ff8502b3ffc302b4ff8502b5001402b6ffc302b7001402b8ffc302b9001402baffc302bcffc302bd001402c0ffc302c2ffc302c4ffc3
02c5ffc302c6ff8502c7ffc302cbffc302ceff9a02cfff7102d1ffc302d3ffc302d5ffc302d7ffc302d9ff8502daff8502dbff8502dcff8502ddff8502e0ff8502e1ffc302e3ffc302e6ffc302ecffc3
02eeffc302efffc302f0ff8502f1ffc302f2ff8502f3ffc302f4ff8502f8ffc302faffc302fcffc302feffc30300ffc30306ffc30308ffc30309ff85030aff85030bff85030cff85030effc30310ffc3
0311ff9a0312ff710314ffc30315ffc30316ff85031700140318ffc30319ffc3031aff85031bff9a031cff71031dff85031eff85031fff850321ff850322ff850323ff850324ff850325ff850326ff85
0327ff850328ff850329ff85032aff85032bff85032cff85032dff85032eff85032fff850330ff850331ff850332ff850333ff850334ff850336ff850338ff85033aff85033cff85033eff850340ff85
0342ff850344ff850349ffc3034aff85034bffc3034cff85034dffc3034eff85034fffc30350ff850351ffc30352ff850353ffc30354ff850355ffc30356ff850357ffc30358ff850359ffc3035aff85
035bffc3035cff85035dffc3035eff85035fffc30360ff850362ffae0364ffae0366ffae0368ffae036affae036cffae036effae0370ffc30372ffc30374ffc300630005003d000a003d0010ffd7019f
ffd701a4ffae01aaff9a01aeff8501b5ff9a01bbffd701beffec01c4002901caffae01ccffc301cdffc301ceff7101cfffc301d2ffc301d3ffc301d4ffc301d5ff7101d6ffc301d7ffc301d8ff9a01d9
ffc301daffc301dbffc301deffae01e0ffc301e1ffc301e2ffc301e3ffc301e5ffc301e6ffc301e8ffc301e9ffc301eaffc301ecffc301edffc301eeffc301f0003d01f2ff8501f3ffc301f5ffc301f7
ffc301f9ffc30207003d020b003d026aff9a026bffc30272ff9a027dffc3027fff9a0285ff9a0287ff9a0289ff9a028dff9a0296ffc3029affc302a4ffc302a6ffc302acffc302aeffc302b0ffc302b2
ff9a02b4ff9a02c0ffc302c2ffc302c4ffc302c6ffae02ceff9a02cfff7102d1ffc302d3ffc302d5ffc302d7ffc302d9ff9a02daffae02dbff9a02dcffae02ddff9a02e0ff9a02ecffc302eeffc302f0
ff9a02f2ff9a02f4ff9a02feffc30300ffc30309ff85030aff9a030bff85030cff9a0311ff9a0312ff710314ffc30316ff9a031aff9a031bff9a031cff71002a019dffd701a4ffd701a6ffc301a8ffec
01aaffd701aeffd701b0ffc301b5ffd701bcffc301bfffd701c4ffc301c7ffd701ceffc301d5ffc301f2ffc30272ffd70276ffc3029fffc302a1ffd702a9ffc302b5ffc302b7ffc302b9ffc302bbffc3
02bdffc302caffc302ceffd702cfffc302d9ffd702dbffd702ddffd702e5ffc302e7ffd702e9ffd702f5ffd70305ffc30307ffc30311ffd70312ffc30317ffc3031bffd7031cffc3002e00050029000a
0029019fffd701a4003d01ae002901b5002901b8ffec01bbffd701beffd701c1ffec01e1ffc302070029020b0029026cffec027cffec027dffc3027effec0280ffec0282ffec0284ffec0286ffec0288
ffec028affec028cffec02b1ffec02b3ffec02bfffec02c0ffc302c1ffec02c2ffc302ce002902d4ffec02d5ffc302e1ffec02e3ffec02efffec02f1ffec02f3ffec02fdffec02feffc3030dffec030f
ffec031100290315ffec0319ffec031b0029003001a300f601a4002901aa001401ae002901b5002901bcffec01beffec01bf001401c1ffec01ce002901d5001401e1ffd701e7002901f1006601f20029
02720014027cffec027dffd70280ffec0282ffec02a9ffec02b5ffec02b7ffec02b9ffec02bdffec02bfffec02c0ffd702c1ffec02c2ffd702c5ffd702c6ffd702c7ffd702ce002902cf001402d4ffec
02d5ffd702d9001402db001402dd001402fdffec02feffd7030dffec030fffec03110029031200140317ffec031b0029031c0014003e019dffec01a300e101a4003d01aa002901ae002901b5002901bc
ffec01bd002901beffec01bf001401c1ffec01c4ffec01c7001401ce003d01d1001401d5002901dcffec01e1ffd701e4ffec01e7002901f1006601f2002902720029027cffec027dffd70280ffec0282
ffec02a1001402a9ffec02aaffec02b5ffec02b6ffec02b7ffec02b9ffec02bdffec02bfffec02c0ffd702c1ffec02c2ffd702ce002902cf002902d4ffec02d5ffd702d9002902db002902dd002902e7
001402e9001402f5001402f7002902f9002902fb002902fdffec02feffd7030dffec030fffec03110029031200290317ffec0318ffec031b0029031c0029001e0005ffc3000affc3019dff8501a6ff71
01a8ffd701bcff8501c1ffc301c4ff8501dcffd701e4ffd70207ffc3020bffc3027cffc30280ffc30282ffc302a9ff8502aaffd702b5ff8502b6ffd702b7ff8502b9ff8502bdff8502bfffc302c1ffc3
02d4ffc302fdffc3030dffc3030fffc30317ff850318ffd700200005ff9a000aff9a019dff8501a6ff8501a8ffd701bcff8501c1ffc301c4ff8501dcffd701e4ffd70207ff9a020bff9a027cffc30280
ffc30282ffc302a9ff8502aaffd702b5ff8502b6ffd702b7ff8502b9ff8502bdff8502bfffc302c1ffc302c5ffc302c7ffc302d4ffc302fdffc3030dffc3030fffc30317ff850318ffd70023019dffd7
01a4ffd701a6ffd701a8ffd701aeffd701b0ffc301b1ffec01b5ffc301bcffd701bdffec01bfffd701d5ffd701f2ffd70276ffc3029fffc302a9ffd702b5ffd702b7ffd702b9ffd702bbffc302bdffd7
02caffc302ceffc302cfffd702e5ffc302f7ffec02f9ffec02fbffec0305ffc30307ffc30311ffc30312ffd70317ffd7031bffc3031cffd7001d019dffd701a4ffd701a6ffd701a8ffec01aeffd701b0
ffd701b1ffec01b5ffd701bdffec01bfffd701ceffd701d5ffd701f2ffd70276ffd7029fffd702bbffd702caffd702ceffd702cfffd702e5ffd702f7ffec02f9ffec02fbffec0305ffd70307ffd70311
ffd70312ffd7031bffd7031cffd70019000affd701dcffd701ddffec01e1ffec01e4ffd701f6ffd7026dffec027dffec0281ffec0283ffec028bffec02aaffd702b6ffd702b8ffec02baffec02c0ffec
02c2ffec02d5ffec02f8ffec02faffec02fcffec02feffec030effec0310ffec0318ffd7002101ceffd701d0ffd701dcffd701ddffd701dfffd701e1ffec01e4ffd701f6ffd7026dffd7027dffec0281
ffd70283ffd7028bffd702a0ffd702aaffd702b6ffd702b8ffd702baffd702bcffd702c0ffec02c2ffec02cbffd702d5ffec02e6ffd702f8ffd702faffd702fcffd702feffec0306ffd70308ffd7030e
ffd70310ffd70318ffd700240005003d000a003d01d0ffec01dcffd701ddffd701dfffd701e1ffec01e4ffd701f6ffd70207003d020b003d026dffd7027dffec0281ffd70283ffd7028bffd702a0ffec
02aaffd702b6ffd702b8ffd702baffd702bcffec02c0ffec02c2ffec02cbffec02d5ffec02e6ffec02f8ffd702faffd702fcffd702feffec0306ffec0308ffec030effd70310ffd70318ffd7000701ce
ff9a01d5ff9a01edffec01f2ff9a02cfff9a0312ff9a031cff9a001001ca001401ce002901dcffd701e1ffd701e4ffc301f1007b027dffd702aaffd702b6ffd702c0ffd702c2ffd702d5ffd702da0014
02dc001402feffd70318ffd7000d000affd701d0ffd701dcffec01dfffec02a0ffd702aaffec02b6ffec02bcffd702cbffd702e6ffd70306ffd70308ffd70318ffec002000050029000a002901cfffd7
01d8ffd701dbffec01deffd701e1ffd701eaffd701edffd702070029020b0029026affd7027dffd7027fffd70285ffd70287ffd70289ffd7028dffd702b2ffd702b4ffd702c0ffd702c2ffd702d5ffd7
02e0ffd702f0ffd702f2ffd702f4ffd702feffd7030affd7030cffd70316ffd7031affd7001d0005003d000a003d01d1ffec01dcffec01ddffec01dfffec01e1ffd701f6ffec0207003d020b003d026d
ffec027dffd70281ffec0283ffec028bffec02aaffec02b6ffec02b8ffec02baffec02c0ffd702c2ffd702d5ffd702f8ffec02faffec02fcffec02feffd7030effec0310ffec0318ffec002100050029
000a002901cbffd701cfffd701d8ffd701dbffd701deffd701e1ffd701eaffd701edffd702070029020b0029026affd7027dffd7027fffd70285ffd70287ffd70289ffd7028dffd702b2ffd702b4ffd7
02c0ffd702c2ffd702d5ffd702e0ffd702f0ffd702f2ffd702f4ffd702feffd7030affd7030cffd70316ffd7031affd70022005bffd7005dffec0140ffec01d0ffd701d1ffec01d5ffec01dcffd701dd
ffec01dfffd701f2ffec01f6ffec026dffec0281ffec0283ffec028bffec02a0ffd702aaffd702b6ffd702b8ffec02baffec02bcffd702cbffd702cfffec02e6ffd702f8ffec02faffec02fcffec0306
ffd70308ffd7030effec0310ffec0312ffec0318ffd7031cffec001301d0ffd701d1ffec01d5ffec01dcffec01dfffec01e4ffec01f2ffec02a0ffd702aaffec02b6ffec02bcffd702cbffd702cfffec
02e6ffd70306ffd70308ffd70312ffec0318ffec031cffec00170005003d000a003d01cfffec01d8ffec01edffec0207003d020b003d026affec027fffec0285ffec0287ffec0289ffec028dffec02b2
ffec02b4ffec02e0ffec02f0ffec02f2ffec02f4ffec030affec030cffec0316ffec031affec003000050052000a00520049001401caffd701ceff9a01cfffec01d5ffc301d8ffd701dbffd701dd0014
01deffd701edffd701f2ff9a01f6001402070052020b0052026affd7026d0014027fffd702810014028300140285ffd70287ffd70289ffd7028b0014028dffd702b2ffd702b4ffd702b8001402ba0014
02cfffc302daffd702dcffd702e0ffd702f0ffd702f2ffd702f4ffd702f8001402fa001402fc0014030affd7030cffd7030e0014031000140312ffc30316ffd7031affd7031cffc300240005003d000a
003d0049001401caffec01ceffc301d5ffc301d8ffec01dc001401deffec01f2ffc30207003d020b003d026affec027fffec0285ffec0287ffec0289ffec028dffec02aa001402b2ffec02b4ffec02b6
001402cfffc302daffec02dcffec02e0ffec02f0ffec02f2ffec02f4ffec030affec030cffec0312ffc30316ffec03180014031affec031cffc3001c005bffd7005dffec0140ffec01d0ffd701d1ffec
01d5ffec01dcffd701dfffec01e1ffec01e4ffd701f2ffd7027dffec02a0ffd702aaffd702b6ffd702bcffd702c0ffec02c2ffec02cbffd702cfffec02d5ffec02e6ffd702feffec0306ffd70308ffd7
0312ffec0318ffd7031cffec001d01cbffec01cfffec01d8ffec01dbffec01deffec01e1ffec01eaffec01edffec026affec027dffec027fffec0285ffec0287ffec0289ffec028dffec02b2ffec02b4
ffec02c0ffec02c2ffec02d5ffec02e0ffec02f0ffec02f2ffec02f4ffec02feffec030affec030cffec0316ffec031affec001501ce002901d5002901dcffec01e1ffec01e4ffd701e7002901e90014
01f1006601f2002901f60014027dffec02aaffec02b6ffec02c0ffec02c2ffec02cf002902d5ffec02feffec031200290318ffec031c0029001601ce003d01d5002901dcffec01e1ffec01e4ffec01e7
001401eaffec01edffec01f1006601f2002901f60014027dffec02aaffec02b6ffec02c0ffec02c2ffec02cf002902d5ffec02feffec031200290318ffec031c0029001901dcff9a01ddffd701dfffec
01e1ffd701e4ff7101f6ffc3026dffd7027dffd70281ffd70283ffd7028bffd702aaff9a02b6ff9a02b8ffd702baffd702c0ffd702c2ffd702d5ffd702f8ffd702faffd702fcffd702feffd7030effd7
0310ffd70318ff9a001c0005003d000a003d01dcff9a01ddffd701dfffec01e1ffd701e4ff710207003d020b003d026dffd7027dffd70281ffd70283ffd7028bffd702aaff9a02b6ff9a02b8ffd702ba
ffd702c0ffd702c2ffd702d5ffd702f8ffd702faffd702fcffd702feffd7030effd70310ffd70318ff9a001101ceffd701d0ffd701d1ffec01d5ffec01dfffec01e4ffd701f2ffec01f6ffd702a0ffd7
02bcffd702cbffd702cfffec02e6ffd70306ffd70308ffd70312ffec031cffec001001ceffd701d0ffd701d1ffec01d5ffec01dfffec01e4ffd701f6ffec02a0ffd702bcffd702cbffd702cfffec02e6
ffd70306ffd70308ffd70312ffec031cffec00030005ff98000affd701dfffec001c01ceff9a01cfffd701d5ff9a01d8ffd701dbffec01deffd701eaffd701edffec01f2ff9a026affd7027fffd70285
ffd70287ffd70289ffd7028dffd702b2ffd702b4ffd702cfff9a02e0ffd702f0ffd702f2ffd702f4ffd7030affd7030cffd70312ff9a0316ffd7031affd7031cff9a000201eb003d01f4003d002101d0
ffd701dcff9a01ddffc301deffec01dfffec01e1ffd701e4ff9a01f6ffd7026dffc3027dffd70281ffc30283ffc3028bffc302a0ffd702aaff9a02b6ff9a02b8ffc302baffc302bcffd702c0ffd702c2
ffd702cbffd702d5ffd702e6ffd702f8ffc302faffc302fcffc302feffd70306ffd70308ffd7030effc30310ffc30318ff9a001f01d0ffd701dcff9a01ddffc301dfffec01e1ffd701e4ff9a026dffc3
027dffd70281ffc30283ffc3028bffc302a0ffd702aaff9a02b6ff9a02b8ffc302baffc302bcffd702c0ffd702c2ffd702cbffd702d5ffd702e6ffd702f8ffc302faffc302fcffc302feffd70306ffd7
0308ffd7030effc30310ffc30318ff9a000101f6ffd7001c00050029000a002901cbffec01cfffd701d8ffd701dbffec01deffd701eaffd701edffd701f6ffec02070029020b0029026affd7027fffd7
0285ffd70287ffd70289ffd7028dffd702b2ffd702b4ffd702e0ffd702f0ffd702f2ffd702f4ffd7030affd7030cffd70316ffd7031affd7001d01caffd701d5ffc301d8ffec01dc001401f2ffd7026a
ffec027fffec0285ffec0287ffec0289ffec028dffec02aa001402b2ffec02b4ffec02b6001402cfffc302daffd702dcffd702e0ffec02f0ffec02f2ffec02f4ffec030affec030cffec0312ffc30316
ffec03180014031affec031cffc300da00050052000a00520010ff5c0026ffec002affec0032ffec0034ffec0037ff850038ffec0039ffae003affc3003cff9a0089ffec0094ffec0095ffec0096ffec
0097ffec0098ffec009affec009bffec009cffec009dffec009effec009fff9a00c8ffec00ceffec00deffec00e0ffec00e2ffec00e4ffec010effec0112ffec0114ffec0124ff850126ff85012cffec
0130ffec0132ffec0134ffec0136ffc30138ff9a013aff9a019fffec01a4ff9a01aaff8501aeff8501b5ff8501b8ffd701bbffec01bcff8501beffc301caff8501ccffc301cdffc301ceff5c01cfff85
01d0ffc301d1ffc301d2ffc301d3ffc301d4ffc301d5ff5c01d6ffc301d7ffc301d8ff8501d9ffc301daffc301dbff8501dcffc301ddffc301deff8501dfffc301e0ffc301e1ffc301e2ffc301e3ffc3
01e4ffc301e5ffc301e6ffc301e7ffc301e8ffc301e9ff9a01eaff8501ecffc301edff8501eeffae01f0003d01f2ff5c01f3ffc301f5ffc301f7ffc301f9ffc301faffc301fcffc301feffc30200ff9a
02070052020b0052025fffec0261ffec026aff85026bffc3026cffd7026dffc30272ff85027dffc3027effd7027fff850281ffc30283ffc30284ffd70285ff850286ffd70287ff850288ffd70289ff85
028affd7028bffc3028cffd7028dff850296ffc3029affc302a0ffc302a4ffc302a6ffc302a9ff8502aaffc302acffc302aeffc302b0ffc302b1ffd702b2ff8502b3ffd702b4ff8502b5ff8502b6ffc3
02b7ff8502b8ffc302b9ff8502baffc302bcffc302bdff8502c0ffc302c2ffc302c4ffc302c5ffae02c6ff7102c7ffae02cbffc302ceff8502cfff5c02d1ffc302d3ffc302d5ffc302d7ffc302d9ff85
02daff8502dbff8502dcff8502ddff8502e0ff8502e1ffd702e3ffd702e6ffc302ecffc302eeffc302efffd702f0ff8502f1ffd702f2ff8502f3ffd702f4ff8502f8ffc302faffc302fcffc302feffc3
0300ffc30306ffc30308ffc30309ff5c030aff85030bff5c030cff85030effc30310ffc30311ff850312ff5c0314ffc30315ffd70316ff850317ff850318ffc30319ffd7031aff85031bff85031cff5c
0349ffec034bffec034dffec034fffec0351ffec0353ffec0355ffec0357ffec0359ffec035bffec035dffec035fffec0361ffec0363ffec0365ffec0367ffec0369ffec036bffec036dffec036fff9a
0371ff9a0373ff9a00030037ff9a0171ff9a0172ffc300c30024ffae002c00290037005200390052003a0066003b0029003c0052003d00290046ffc30047ffc30048ffc3004affd70052ffc30054ffc3
0057002900590029005a00140082ffae0083ffae0084ffae0085ffae0086ffae0087ffae0088ff5c008e0029008f00290090002900910029009f005200a8ffc300a9ffc300aaffc300abffc300acffc3
00adffc300b4ffc300b5ffc300b6ffc300b7ffc300b8ffc300baffc300c2ffae00c4ffae00c6ffae00c9ffc300cbffc300cdffc300cfffc300d5ffc300d7ffc300d9ffc300dbffc300ddffc300ec0029
00f0002900f20029010fffc30111ffc30113ffc30115ffc30124005201260052013600660137001401380052013a0052013b0029013d0029013f00290143ffae015fffae0169ffae017100520179ffc3
017effc30182ffc3018a0029018cffc3018effc30190ffc3019100290193ffc3019400290196ffc30199ffc3019bffc3019d005201a4ff9a01a6005201a8003d01aaffae01aeff8501b0003d01b10014
01b5ff8501bc005201bd003d01bf002901c4005201cfffc301d8ffc301dbffc301dc002901deffc301eaffc301edffc301fa006601fb001401fc006601fd001401fe006601ff0014020000520228ffae
0258ffae0260ffc3026affc30272ffae0276003d027fffc30285ffc30287ffc30289ffc3028dffc3029f003d02a9005202aa002902b2ffc302b4ffc302b5005202b6002902b7005202b9005202bb003d
02bd005202ca003d02ceff8502d9ffae02dbffae02ddffae02e0ffc302e5003d02f0ffc302f2ffc302f4ffc302f7003d02f9003d02fb003d0305003d0307003d030affc3030cffc30311ff850316ffc3
0317005203180029031affc3031bff85031dffae031fffae0321ffae0323ffae0325ffae0327ffae0329ffae032bffae032dffae032fffae0331ffae0333ffae0336ffc30338ffc3033affc3033cffc3
033effc30340ffc30342ffc30344ffc30345002903470029034affc3034cffc3034effc30350ffc30352ffc30354ffc30356ffc30358ffc3035affc3035cffc3035effc30360ffc3036f005203710052
0373005200100045003d00490066004b003d004c003d004d003d004e003d004f003d0057006600590066005a0066005b0029005c0066005d002903700066037200660374006600100045003d00490066
004b003d004c003d004d003d004e003d004f003d0057006600590066005a0052005b0029005c0066005d001403700066037200660374006600020309ff85030bff8500020096000500050000000a000b
0001000f0011000300240027000600290029000a002c002c000b002e002f000c00320035000e0037003e001200440046001a0048004b001d004e004e00210050005200220055005700250059005e0028
006d006d002e007d007d002f008200870030008900920036009400980040009a009f004500a200ad004b00b300b8005700ba00bf005d00c100c9006300cb00cb006c00cd00cf006d00d100d1007000d5
00d5007100d700d7007200d900db007300dd00dd007600ec00ec007700f000f0007800f200f2007900f800f8007a00fa00fb007b00fd00fd007d00ff0101007e01030103008101060106008201080108
0083010a010a0084010e010f008501110113008701150115008a011d011d008b011f011f008c01210124008d012601270091012c012c00930130013000940132013200950134013400960136013b0097
013d013d009d013f0140009e0143014400a0014a014a00a20156015600a3015a016200a40164016400ad0166016900ae016c016d00b2016f017c00b4017e018400c20186018800c9018a018e00cc0190
019100d10193019700d30199019900d8019b01a200d901a401a800e101aa01ae00e601b001b100eb01b401b400ed01b801b800ee01ba01c000ef01c301c400f601c601c800f801ca01d100fb01d401d4
010301d801d8010401da01e0010501e301ea010c01ec01ee011401f001f0011701f201f6011801f80203011d020602060129020a020a012a02280228012b02580258012c02600260012d02620262012e
026a026a012f026c027201300274027401370276027c0138027e028c013f0295029d014e029f02a0015702a202af015902b102c0016702c302c3017702c502c5017802c702c7017902c902cc017a02ce
02cf017e02d202d3018002d602d9018202db02db018602de02de018702e002e9018802ef02fc019202ff030001a00303030801a2030b031d01a8031f031f01bb0321032101bc0323032301bd03250325
01be0327032701bf0329032901c0032b032b01c1032d032d01c2032f032f01c30331033101c40333033301c50336033601c60338033801c7033a033a01c8033c033c01c9033e033e01ca0340034001cb
0342034201cc0344034501cd0347034701cf0349035601d00358035801de035a035a01df035c035c01e0035e035e01e10360036101e20363036301e40366036601e50368036801e6036a036a01e7036c
036c01e8036e037401e9000000010000000a001c001e00016c61746e0008000400000000ffff000000000000
]],
["fonts/Karla-Regular.ttf"] = [[
0001000000110100000400104453494700000001000041c8000000084646544d5e5927d200003d280000001c47444546002800cc00003d440000002847504f5303802f7500003d6c0000040047535542
dc42ea3b0000416c0000005c4f532f3284373af00000019800000060636d61702dfec69900000494000001e2676173700000001000003d2000000008676c796674bfc695000007d000002e5068656164
f88661be0000011c000000366868656106c003300000015400000024686d74784b84229d000001f80000029c6c6f63618bbf97a400000680000001506d61787000ee004200000178000000206e616d65
eba6ecf300003620000004f2706f7374a6ad4d3e00003b14000002097072657068068c8500000678000000070001000000010000ff1f20465f0f3cf5000b03e800000000cabc7b1f00000000cb82a341
ffaaff0a037e036f000000080002000000000000000100000395ff04000003acffaaffd8037e0001000000000000000000000000000000a70001000000a7003f00050000000000020000000100010000
0040000000000000000301fd01900005000802bc028a0000008c02bc028a000001dd003200fa00000000000000000000000080000027080000421400000000000000707972730040002022120395ff04
0000039500fc000000010000000001de027500000020000202290000000000000229000000ee0000011600520132001c027300350278004502d6003d039400590088001b016400490164001c019c0038
020f001200d2001e015d002600b3001e01b0003902520033014b003102440040024f0034022f001a02350041023d003301d60018026700370245004200e700380106003901c6002801e8004f01c6002f
01f3001e03ac0043023f001a026e0065026500330295006402320064020c006502850033029d006501180065019400080261006501d400650350006502ad006302860033022a0063028c003302650065
025d004501f50006028e005702330012037f000e0282002f0225000a02500049013d006501b0002d011dfff201c0001502d60037013b00180230004602480065020300320252003c0203003201570031
0235001c025c0065012b005e013affaa023400650118006503a70064025b0064022200320252006502490033017d0064020e003c01780022025e005701f7001202d500120200001a01e4000501db0035
015d004b01180065015d0008020300350235001401430018013b0017023f001a023f001a023f001a023f001a023f001a0234006402340064023400690234006401180007011800060118ffd901180004
02ae00630286003302860033028600330286003302860033028e0057028e0057028e0057028e00570227003c0227003c0227003c0227003c0227003c02020033020200330202003602020033012b000c
012b000b012bffde012b0009025c00650223003302230033022300330223003302230033025e0057025e0057025e0057025e00570118006500b8002401970018016e001701da003702d6003700d60024
00d6000f0245005c01e8004f02130033014b0031023f00400232001c0230001802390041023d003301d60018026700370244004200000003000000030000001c00010000000000dc000300010000001c
000400c00000002c00200004000c007e00a000a300a800b400c400cf00d600dc00e400ef00f600fc013102bc02c602dc2014201920b92212ffff0000002000a000a300a800b400c000c800d100d900e0
00e800f100f9013102bc02c602dc2013201820b92212ffffffe3ff63ffbfffbbffb0ffa5ffa2ffa1ff9fff9cff99ff98ff96ff62fdd8fdcffdbae084e081dfe2de8a0001000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000010600000100000000000000010200000002000000000000000000000000000000010000030405060708090a0b0c
0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c
5d5e5f6061006900006b72777b7d7c7e807f00008281838486858788898b8a8c8e8d908f9192000000620000000000000064630000000000000000000000000000000000000000000000000000000000
03656876000097980000999a000000000000000000000000000000676c666d6a6f70716e74750073797a7893959600000000000000000000b801ff85b0048d0000000000000000000000001a002e0060
00a800f001480156016c018201a001b601ce01dc01ee01fc021c0230025e029202ae02d60308031c035e039803b603da03ee04020416044e04a604be04f005160536054e0564058e05a605b405cc05e8
05f80614062c0650067006a406ca06fe0710072e07420760077a079007a807ba07c807d807ea07f80806083a0862088608ae08d808fa09540972098c09b409cc09da0a080a260a480a6e0aa20abc0aee
0b100b2e0b420b600b780b9a0bb20bde0bec0c180c380c6e0c8e0c9c0cbc0cdc0cfe0d320d620d820da00dc20df20e060e1a0e320e580e8c0eb80ee40f120f520f8e0fb20fd810001036107010ac10ea
113c118811ba11e8121c125e12721286129e12c41300132a1354138213c213fe1424144a147214aa14b814d014e2150615141522153c15581584159215b015c415f0162616421668169a16ae16f01728
00020052fff700c902730003000b0000133303231626343632161406635309400621213323230273fe3db9212f21212f210000000002001c01cd011602a900030007000013072327330723276e0b3c0b
fa0b3c0b02a9dcdcdcdc000000020035001f024a0251001b001f00002523072337233733372337333733073337330733072307330723072327333723017c9a0e4a0e630564136506640f4a0f9a0f490f
670666136706670e4a85991399aa8b8b36b2358a8a8a8a35b2368bc1b200000000030045ffa5023302c9001f0026002d0000011616140607152335262635331417352726263436373533151616172326
2715173427271536360206141617173501a1454d72663e5f794d8b20535565633e575e0b4e1161805c243f41f73a282d1e014a174d8565065152086757710deb0c1a488659074b4c075644500cd7ba4a
1d0cdc053e01b23649290e0bc70000000005003dfff5029b02800009001300170021002d0000121615140622263534361626220615141632363525330123013216140623222634361722061514163332
36353426f14c4b6c494a76243a24243a24011952fe595101aa364b4b36364949361d24241d1d242402804c44444b4c43434d612c2c2f2f2a2a2f85fd8b01144b894b4c874c352c2f2f2a2a2f2f2c0000
00030059fff50365027f0024002b00370000010607171616323637330606232226272706232226343637272635343632161406071736370406141632372737342622061416171736373602d52d462b14
1f3127034a0443402742271a6480698e5f5c0e4d69ad674e589e442cfe5a4953ac51ba9b3763371a1e1b681006015e8955251110252d484f2023165969944b0f0d43503f54547756268b527d66366045
42a3c824303740321a193034110000000001001b01c9006d02a500030000130723276d0b3c0b02a5dcdc000000010049ff8f014802d800090000121017072626343637179bad3962646462390200fe66
af2859d8e7d75a290001001cff8f011b02d80009000036102737161614060727c9ad39626464623966019aaf295ad7e7d8592800000100380184016602ac000e00001327371727330737170717072707
27ad75186a0a3e0a6a18745a2c50502d0204283636808036362859276868270000010012000001fd01eb000b0000011533152315233523353335012fcece4fcece01ebd443d4d443d40000000001001e
ff9300b20061000b0000162634363216140607273637521e1f39262c2d3b3e0d031a2c1e2d4447161d1e2d00000100260101013601450003000013211521260110fef001454400000001001efff70094
00680007000016263436321614063f212133222209212f21212f210000010039ffb7018402db0003000001330323013450fc4f02dbfcdc0000020033fff5022002800007000f00000016100622261036
0426220614163236019c8484e48585011654a15454a1540280a8fec5a8a6013fa6cb8383f583830000010031000000e6027700080000133637331123110607314332404e32350244092afd8902192003
00010040000002010280001c000037211521353437363737363534262206072336363216140607070606158f016dfe44541b217c6042774b0657057abf784f457f302f49495d6f34110d322657303d32
32525868995a1b331433290000010034fff502110280002100000114071616151406222635331616323634262323353332363426220607233636321601f96e3c4a7ece9152025b8c4d4a534831485444
704b0d520b74b77d01d46427114e3d53656b61404443633f3f39603e27355351610000000002001a00000219027e000a000d0000011133152315233521350103331101ac6d6d4dfebb0149ebe9027efe
5e4498983d01a9fe57012c0000010041fff501fd0275001800002534232313211521073633321614062226353314171633323601ab9ba91b015dfeef11342f67767cc47c4f541a1f404ecc77013248b5
095bb879635752190751000000020033fff4020302800015001e00000126220615141736363216140623222610363332161704061416323635342601a911b4610506659e727660708a8c79546a08fef7
4b4a724b4b01d662948035235c536fad6bb40125b35e4ca1476e43423a3a420000010018000001c40275000600000115012301213501c4fee06a012efeb0027549fdd402294c000000030037fff50231
02800015001f002a000013263534363216151406071716161514062226353436170606141632363426272714161717363634262206c97d7ccd84454311454a87e58e518c3f4c609b5b302ae62c29493c
4e528254013e39624b5c624d34520a061a473d4c5c664d3e4e130741603c344a2d0fd81b27101c023b5f3b3900020042fff502120280001c002500000132161716151407062322263533161632373635
342706062226353436162206151416323634011a335c2148882f37677853024468205b050a619d727698724a4b714b02802c29588df54517705539441339c8292151506f56576a49423a3a42476e0000
00020038001700ae01ba0007000f00003626343632161406022634363216140659212133222233212133222217212f21212f210132212f21212f210000020039ff9300cd01ba000b0013000016263436
321614060727363702263436321614066d1e1f39262c2d3b3e0d112121332222031a2c1e2d4447161d1e2d014e212f21212f21000001002800280198023c00060000010505072535250198fee0012034
fec4013c0200cfcc3de745e80002004f00750199017900030007000025152135251521350199feb6014afeb6b94444c0454500000001002f0028019e023c000600001305150527252562013cfec43301
1efee2023ce845e73dcccf000002001efff501c502800019002300001322072336363216151406070606070615152335343e02342603321614062322263436f8751154076cc371343a19220a134e2a6e
263f571a22221a1a202002385c4a5a60453445200e160e192f272d3f433c2d4c33fe2e222f20202f2200000000020043ff4b037e0280000a003d00000134232206151416323637171433323637363534
2623220607061016331522272626343e02321e021514062322262727060623222635343633321615023f3b3b5e375234174227142f132ea28b52862f64d4b9d5863f444276a3b68e643881562b340503
123c284a5f845b3c45012154614a353a2f2d2d331b1a3d5f779a382f64fed6b940733695b2986f3e365d7e437a962422102630624b638748490000000002001a0000022502750007000a000013331323
27230723373303f260d35236fa36539ed1680275fd8ba0a0de01360000030065000002290275000f00180020000013333216151406071616151407062323373236353426232315113332363426232365
eb636b3b3c3b47712a3decea483e44429b9a3a45433c9a02755e4736500e0a523474290f453a303039d301173964380000010033fff50236028000160000013216170726262322061416333236353314
0622261036013f657b17551455394f685d5a4b575587eb919602806551113b4584eb8e4f416374b4012ea900000200640000025602750007001000000132161006232311133332363534262323011d8e
abab8eb94f6a697c7d686a0275abfee1ab0275fdd08b6b6b8900000000010064000001e70275000b0000132115211521152115211521640183fecc0122fede0134fe7d027544d343d645000000010065
000001e30275000900001321152115211521112365017efed1011afee64f027544d144fee400000000010033fff5023b027f001900002506222610363332161707262206141633323635352735331123
01ea2af29ba97e4a6f284c41ae79685848589eeb406c77b0012ab03c421a518ee38a605e020634fec900000000010065000002380275000b0000131121113311231121112311b401354f4ffecb4f0275
feeb0115fd8b011cfee4027500010065000000b402750003000013331123654f4f0275fd8b00000000010008fff501440275000b000024062227351632363511331101445d904f467b2c4f4f5a2a5230
3c3901bffe410000000100650000024c0275000b0000131101330101230307152311b501196dfeed012466f33e500275fedd0123fee5fea6012340e30275000000010065000001cc0275000500003721
15211133b40118fe994f45450275000000010065000002eb0275000c00001b0233112311032303112311d6d4d26f4fd835db4f0275fe5601aafd8b0205fe4a01bdfdf402750000000001006300000248
02750009000013011133112301112311c101384f58fec3500275fe0701f9fd8b0200fe000275000000020033fff502540280000a0014000001321610062322263534361722061416333236342601437a
97967b7b95957b586666585867670280a7fec1a5a79e9fa74784f88080f884000002006300000203027500090011000000161406232315231133113236342623231501966d6d687c4fcb414343417c02
7569b267f30275fec2427345fa00000000020033ff59025402800015001f000005062322272726261036333216151406071716173237012206141633323634260254282c553d2f7993957b7a976a5b1f
1d282b36feef586666585867679413594302a6013ca7a79f84a1172c270219027784f88080f88400000200650000022b0275000c00140000000607132303231123113332160732363426232315021b53
51b466a36e4fdb6a71e1474947438c0173600efefb0101feff027566d7457143f900000000010045fff502180280001f0000012623220614161717161614062226273314163236342627272626343632
161701b411763f442a2f83434f7cd085024d57895031306f4e5070ca6b0801dc5c374d280f2d17509163695f3e423d533010291a488b5d594b00000000010006000001ef027500070000132115231123
11230601e9cd4fcd027544fdcf02310000010057fff502350275001000001311141633323635113311140622263511a6584848584f82db810275fe6e584e4e580192fe6e767878760192000000010012
000002210275000600001b023303230365b4b553df52de0275fdef0211fd8b02750000000001000e000003700275000c00001b023313133303230303230366999b359ab057de53898e53c70275fdf501
cafe36020bfd8b0193fe6d02750000000001002f000002510275000b0000212327072313033313133303025162b6a960dedf5fb4a65fd9fbfb0138013dfeff0101fec4000001000a0000021b02750008
00001b0233031123110368a9ad5de250df0275fedf0121fe8dfefe010201730000010049000002170275000900003701253521150105152149016bfe9501cefe95016bfe324701e3034847fe1d034800
00010065ff9a012b02db00070000133315231133152365c67777c602db46fd4b460000000001002dffb7017802db00030000133313232d4ffc5002dbfcdc00000001fff2ff9a00b802db000700001723
353311233533b8c67777c6664602b5460001001500f501b2024a000600003713331323270715b03db0587775f50155feabf6f60000010037ff84029fffc80003000017211521370268fd983844000000
0001001802a50124035c000300001317072739eb17f5035c8532710000020046fff501db01ea00180021000001342622061523343736333216151123270623222634363217152622061514333236018d
36673f5776242a596444082c734d5d679a46476a42663a5301364138292c6e1a085a5cfecc505b5680481a321426295a5300000000020065fff5021602a9000d00170000133632161406232226270723
113312060715141632363426b430b87a7c59304d1612374f4e4c024f70525101975389e18b302f5402a9fefb49414e424f63a46200010032fff501cd01ea001500000016170726262322061416323635
33140623222634360159640f500d3a23404f4d7339516b565b7f7e01ea5741072a2f60a56435314d5f8ae5860002003cfff501ec02a9000d001700000111331123270606232226343632060614163236
37353426019e4e400a174c2d5b7b79b9924f50704f024e01970112fd57502d2e8ce1884662a4634b3f4e434e00020032fff501d701ea0010001800002532373306062322263436321607211416132623
2206073334010a630e510b694e607879c9630ffeb9487b17213d4706ff3557484f89e4889c704a5f016a0c533e640000000100310000015b02b100140000012623220615153315231123112335333534
363217014d2113232863634e4f4f4e662702660d222b4836fe5801a8364947430d0000000003001cff0a0225023b00280034003e00003726353436372635343632173636330722071615140623222706
06151433333216151406222635343617141632363534262323060600262206151416333236793c2f23366f983507473a075804236f53392c142058ba434e99ea86331b53a9712122c1333601263e6d3e
3f36363e0f1d3c1f2f08304e4e602435404e4a2b3c4d6317041d193539334c7257422b365e263749311721022d01a4414134344344000000000100650000020702a90011000013363332161511231134
2622061515231133b4306f555f4e417a4a4f4f0186647061fee701194546675be202a9000002005e000000c602b20003000b00001333112312263436321614066a4f4f0f1b1b301d1d01dffe21024f1b
2d1b1b2d1b0000000002ffaaff0a00d502b2000900170000122634363332161406230332351133111407062322273716881b1b18181d1d187f584f5a1c223e48043f024f1b2d1b1b2d1bfd006e0223fd
dd89200a25452500000100650000021402a9000b0000370715231133113733071323f4404f4fe76edce761fd34c902a9fe74c1b3fed5000000010065000000b402a90003000013331123654f4f02a9fd
57000000000100640000035201ea001e000013363332161736333216151123113426220615152311342622060715231133b12f723e54132d7a565e4e417a434e41784a024f440182683d36737061fee7
011945466458e8011945466257eb01de000100640000020601ea00110000133633321615112311342622060715231133b12f72555f4e41784a024f440182687061fee7011945466257eb01de00020032
fff501f001ea000b0013000001321615140623222635343616262206141632360111647b7b64647b7bf3488b4a468b4c01ea8279788282787982a56161ab616100020065ff15021601ea000b00150000
13363216140622271123113312363426220607151416af2fbb7d7eb82c4f41ca5551744a024c0195558ae28945fedb02c9fe5d60a762493e5b3f480000020033ff0a022c01ea0013001f000001321737
331114161617072626353506222634361606141633323736373534260108662e1136171d151a443929bb7e7d2551563952230c014c01ea5a4efdd131210f073d124e4a985789e28a4662a7604917205b
424c0000000100640000016901e7000d00000122071523113315363332170726012871044f4f245b1d1a041e019bb2e901df5c64094d0a000001003cfff501d101ea001f000012363216172326232206
141617171616140622262733161632363426272726354c65ac61024d09653135282c603d416abe6a034c02456844282d5d7d01a4464837412c33230d20133b675355432d2d2c3d200c1f255d00010022
ffe6015f02630015000025062726272635112335333533153315231114333237015f564733150b4d4d4e969644252e0e28171033192301263c85853cfedb491200010057fff701f901df001100002506
2322263511331114333236373533112301aa316e51634f704151024f4f5f685a5e0130fed679644ef1fe210000010012000001e501de000600001b02330323036b909159be57be01defe75018bfe2201
de00000000010012000002c001de000c00001b02331313330323030323036474724271754e9c4d6e6c4c9f01defe830166fe960181fe22014ffeb101de0000000001001a000001e601df000b00002123
2707233727331737330701e560898260b5b560898360b7b5b5eff0b4b4f0000000010005ff0a01cb01de00110000171633323736373703331313330306062227053136281d15110ec458917150b3174b
733e8b26281d3b3101defe83017dfdc3494e230000010035000001a701de0009000037012135211501211521350119fee70172feeb0115fe8e40015e4040fea2400000000001004bff76015502ec001f
000013161515141633152235353426232335333236353534373633152206151514079a343d4ad511131111131172283b4a3d34012f19585d515545ea5c2b244c232c5ba831124555525c561b00010065
ff8600b402ee0003000013331123654f4f02eefc9800000000010008ff76011202ec001f00001326353534262335321515141633331523220615151407062335323635353437c3343d4ad51113111113
1172293a4a3d3401331b565c525545eb5b2c234c242b5ca830124555515d58190001003500df01ce0167001100001322072736333216333237170623222627269e25182c214a22861a28192b234e1760
0e27011a3b176f37391b6a24060e000000010014000002110280002500000126232206151533152315140733323637330606232135333236353523353335343633321617019010562a39b6b625b7312d
065406545efebb1c26235b5b5d5b475a0b01ea4e40486d3868441a292c4d4d452c2e6c386d636d4c4a00000000020018030b012a036f000900110000122634363332161406233226343632161406351d
1d17171f1f17921e1e2d1f1f030b1d2a1d1d2a1d1d2a1d1d2a1d00000001001702a50122035c00030000010727370122f417eb03167132850003001a00000225035c0007000a000e0000133313232723
072337330303170727f260d35236fa36539ed16863eb17f50275fd8ba0a0de0136014885327100000003001a00000225035c0007000a000e0000133313232723072337330313072737f260d35236fa36
539ed16886f417eb0275fd8ba0a0de0136010271328500000003001a0000022503680007000a001000001333132327230723373303370727072737f260d35236fa36539ed168b432818232b40275fd8b
a0a0de0136b8276c6c279c000003001a00000225034a0007000a001e0000133313232723072337330327220727363632171616323637170606222e02f260d35236fa36539ed168442019200c2e34222e
1b1a1c0a230b3129223c150275fd8ba0a0de0136ee3313333511170c191b1730340c1f080004001a0000022503430007000a0014001c0000133313232723072337330326263436333216140623322634
3632161406f260d35236fa36539ed168561d1d17171f1f17921e1e2d1f1f0275fd8ba0a0de0136cb1d2a1d1d2a1d1d2a1d1d2a1d00020064000001e7035c000b000f0000132115211521152115211521
13170727640183fecc0122fede0134fe7d5eeb17f5027544d343d645035c85327100000000020064000001e7035c000b000f000033213521352135213521352125072737640183fecc0122fede0134fe
7d0147f417eb45d643d344a17132850000020069000001ec0368000b00110000132115211521152115211521010727072737690183fecc0122fede0134fe7d017632818232b4027544d343d64502cc27
6c6c279c00030064000001e70343000b0015001d0000132115211521152115211521122634363332161406233226343632161406640183fecc0122fede0134fe7d571d1d17171f1f17921e1e2d1f1f02
7544d343d64502df1d2a1d1d2a1d1d2a1d1d2a1d0002000700000113035c0003000700001333112303170727654f4f3deb17f50275fd8b035c8532710002000600000111035c00030007000033331123
37072737654f4facf417eb0275a17132850000000002ffd900000140036800030009000013331123130727072737654f4fdb32818232b40275fd8b02cc276c6c279c0000000300040000011603430003
000d0015000013331123022634363332161406233226343632161406654f4f441d1d17171f1f17921e1e2d1f1f0275fd8b02df1d2a1d1d2a1d1d2a1d1d2a1d000002006300000248034a0009001d0000
1311331101331123110137220727363632171616323637170606222e026350013d584ffec8512019200c2e34212f1b1a1c0a230b3129223c150275fd8b0200fe000275fe0701f98d3313333511170c19
1b1730340c1f080000030033fff50254035c000a0014001800000132161006232226353436172206141633323634260317072701437a97967b7b95957b58666658586767bceb17f50280a7fec1a5a79e
9fa74784f88080f8840123853271000000030033fff50254035c000a0014001800000122061514163332361026073216140623222634363707273701437b95957b7b96977a58676758586666ddf417eb
0280a79f9ea7a5013fa74784f88080f884dd71328500000000030033fff502540368000a0014001a000001321610062322263534361722061416333236342637072707273701437a97967b7b95957b58
6666585867675b32818232b40280a7fec1a5a79e9fa74784f88080f88493276c6c279c0000030033fff50254034a000a0014002800000122061514163332361026073216140623222634363722072736
3632171616323637170606222e0201437b95957b7b96977a58676758586666132019200c2e34212f1b1a1c0a230b3129223c150280a79f9ea7a5013fa74784f88080f884c93313333511170c191b1730
340c1f0800040033fff502540343000a0014001e0026000001321610062322263534361722061416333236342e023436333216140623322634363216140601437a97967b7b95957b58666658586767c4
1d1d17171f1f17921e1e2d1f1f0280a7fec1a5a79e9fa74784f88080f884a61d2a1d1d2a1d1d2a1d1d2a1d0000020057fff50235035c0010001400001311141633323635113311140622263511371707
27a6584848584f82db818beb17f50275fe6e584e4e580192fe6e767878760192e785327100020057fff50235035c0010001400001311141632363511231114062322263511250727375781db824f5848
48580125f417eb0275fe6e767878760192fe6e584e4e580192a171328500000000020057fff5023503680010001600001311141633323635113311140622263511250727072737a6584848584f82db81
01a232818232b40275fe6e584e4e580192fe6e76787876019257276c6c279c0000030057fff5023503430010001a00220000131114163332363511331114062226351136263436333216140623322634
3632161406a6584848584f82db81831d1d17171f1f17921e1e2d1f1f0275fe6e584e4e580192fe6e7678787601926a1d2a1d1d2a1d1d2a1d1d2a1d000003003cfff501d102db00180021002500000134
262206152334373633321615112327062322263436321715262206151433323603170727018336673f5776242a596444082c734d5d679a46476a42663a53cdeb17f501364138292c6e1a085a5cfecc50
5b5680481a321426295a5302568532710003003cfff501d102db0018002200260000013426220615233437363332161511232706232226343632171526220615143332363713072737018336673f5776
242a596444082c734d5d679a46476a42663952021cf417eb01364138292c6e1a085a5cfecc505b5680481a321426295a503f01d4713285000003003cfff501d102e70018002100270000013426220615
23343736333216151123270623222634363217152622061514333236130727072737018336673f5776242a596444082c734d5d679a46476a42663a534a32818232b401364138292c6e1a085a5cfecc50
5b5680481a321426295a5301c6276c6c279c00000003003cfff501d102c90018002200360000013426220615233437363332161511232706232226343632171526220615143332363703220727363632
171616323637170606222e02018336673f5776242a596444082c734d5d679a46476a4266395202ae2019200c2e34222e1b1a1c0a230b3129223c1501364138292c6e1a085a5cfecc505b5680481a3214
26295a503f01c03313333511170c191b1730340c1f0800000004003cfff501d102c100180021002b00330000013426220615233437363332161511232706232226343632171526220615143332360226
34363332161406233226343632161406018336673f5776242a596444082c734d5d679a46476a42663a53d51d1d17171f1f17921e1e2d1f1f01364138292c6e1a085a5cfecc505b5680481a321426295a
5301d81d2a1d1d2a1d1d2a1d1d2a1d0000030033fff501d802e000100018001c00002532373306062322263436321607211416132623220607333403170727010b630e510b694e607879c9630ffeb948
7a16203e4706ffe3eb17f53557484f89e4889c704a5f016a0c533e64016285327100000000030033fff501d802e000100016001a00002506232226352136262206141633323637021615233636370727
37017c0e63414801470f63c97978604e690b8237ff0647b8f417eb8c575f4a709c88e4894f48011f533e3e53ef71328500030036fff501db02ec00100018001e00002532373306062322263436321607
2114161326232206073334370727072737010e630e510b694e607879c9630ffeb9487b17213d4706ff3532818232b43557484f89e4889c704a5f016a0c533e64d2276c6c279c000000040033fff501d8
02c1001000180022002a000025323733060623222634363216072114161326232206073334262634363332161406233226343632161406010b630e510b694e607879c9630ffeb9487a16203e4706ffea
1d1d17171f1f17921e1e2d1f1f3557484f89e4889c704a5f016a0c533e64df1d2a1d1d2a1d1d2a1d1d2a1d000002000c0000011802db00030007000013331123031707276a4f4f3deb17f501dffe2102
db8532710002000b0000011602db00030007000033331123370727376a4f4facf417eb01dfb67132850000000002ffde0000014502e7000300090000133311231307270727376a4f4fdb32818232b401
dffe21024b276c6c279c0000000300090000011b02c10003000d00150000133311230226343633321614062332263436321614066a4f4f441d1d17171f1f17921e1e2d1f1f01dffe21025d1d2a1d1d2a
1d1d2a1d1d2a1d00000200650000020702c900110025000001220727231133353636321615113311342627220727363632171616323637170606222e020153722f09444f024a78414e5fbb2019200c2e
34222e1b1a1c0a230b3129223c1501ea685cfe22eb57624645fee701196170973313333511170c191b1730340c1f080000030033fff501f102db000b0013001700000132161514062322263534361626
220614163236031707270112647b7b64647b7bf3488b4a468b4cf3eb17f501ea8279788282787982a56161ab616102418532710000030033fff501f102db000b00130017000001220615141633323635
34260636321614062226010727370112647b7b64647b7bf24a8b484c8b460113f417eb01ea8279788282787982a56161ab616101fb71328500030033fff501f102e7000b001300190000013216151406
23222635343616262206141632361307270727370112647b7b64647b7bf3488b4a468b4c2432818232b401ea8279788282787982a56161ab616101b1276c6c279c00000000030033fff501f102c9000b
001300270000012206151416333236353426063632161406222613220727363632171616323637170606222e020112647b7b64647b7bf24a8b484c8b464a2019200c2e34212f1b1a1c0a230b3129223c
1501ea8279788282787982a56161ab616101e73313333511170c191b1730340c1f08000000040033fff501f102c1000b0013001d00250000013216151406232226353436162622061416323602263436
33321614062332263436321614060112647b7b64647b7bf3488b4a468b4cfa1d1d17171f1f17921e1e2d1f1f01ea8279788282787982a56161ab616101c31d2a1d1d2a1d1d2a1d1d2a1d000000020057
fff701f903010011001500002506232226351133111433323637353311230317072701aa316e51634f704151024f4fe2eb17f55f685a5e0130fed679644ef1fe210301853271000000020057fff701f9
030100110015000005323715331123151406232235112311141613072737010b6e314f4f5242704f63f7f417eb09685f01dfe9536779012afed05e5a02c471328500000000020057fff701f9030d0011
0017000025062322263511331114333236373533112313072707273701aa316e51634f704151024f4f3632818232b45f685a5e0130fed679644ef1fe210271276c6c279c00030057fff701f902c10011
001b0023000025062322263511331114333236373533112302263436333216140623322634363216140601aa316e51634f704151024f4fe91d1d17171f1f17921e1e2d1f1f5f685a5e0130fed679644e
f1fe21025d1d2a1d1d2a1d1d2a1d1d2a1d00000000010065000000b401df0003000013331123654f4f01dffe210000000001002401a300b80271000b0000122634363216140607273637581e1f39262c
2d3b3e0d020d1a2c1e2d4447161d1e2d0001001802a5017f036800050000010727072737017f32818232b402cc276c6c279c00000001001702cf0153034a001300001322072736363217161632363717
0606222e02702019200c2e34212f1b1a1c0a230b3129223c1503023313333511170c191b1730340c1f08000000010037010101a30145000300001321152137016cfe940145440000000100370101029f
01450003000013211521370268fd9801454400000001002401a200b80272000c000012062226343637170607161615a21f38272f2d383e0d171e01c11f2e4547161d202c021a16000001000f01a200a2
0272000d00001236321615140607273637262635242039252f2c383e0d171f02541e2c232347171e202c021a160000000001005c000001fc0275001b0000132115231617331523060706070523253533
323637233533262623235c01a0b7450f635e07702837011270fef23d575403ebe70b534c3d027531244330732c0f05faf8304c39302e39000001004f01020199014500030000011521350199feb60145
4343000000020033fff501e10227000900110000123216151406222635340534232215143332a7c57575c574015d8686868602279287879291888888d4d4d30000010031000000e6021d000800001336
37331123110607314530404e333401e90b29fde301be200200010040000001f50227001a00003721152135343637373636342622060723363632161406070706158f0165fe4c4a4c6735334074490657
0574bd744a4c6f61494954505311190c304e362e2f4e55618449121b164d00000001001cff8e01ed02270024000001220723363332161514060716161514062226353316163236353427262323353332
36342601046e155316bf5d7439333d4881cc845002538358591e2a3d2b48534401e35da15d4a333e13145f3f526a68624541473658210b3f34623b0000020018ff9702180222000a000d000001113315
2315233521350103331101ab6d6d4efebb0149f1ef0222fe57439f9f3d01affe5101330000010041ff8e01fa0221001600001336333216140622263533141632363426232313211521bb2e30697886be
754d467e58514fa21c0159fef001280a6abc7e6459393c547f4801304700000000020033fff4020302800015001e00000126220615141736363216140623222610363332161704061416323635342601
a911b4610506659e727660708a8c79546a08fef74b4a724b4b01d662948035235c536fad6bb40125b35e4ca1476e43423a3a420000010018ff9e01c40227000600000115012301213501c4fed461013a
fea7022743fdba024346000000030037fff5023102800015001f002a000013263534363216151406071716161514062226353436170606141632363426272714161717363634262206c97d7ccd844543
11454a87e58e518c3f4c609b5b302ae62c29493c4e528254013e39624b5c624d34520a061a473d4c5c664d3e4e130741603c344a2d0fd81b27101c023b5f3b3900020042ff8e02120227001a00230000
013216171610062322263533161632373635342706062226353436172206141632363426011e355d204282775d76530241691f5d05085e9d77765f3c4748754a480227332c59fed4b56f5639441439cb
2d234d557056576e464575454a70450000000016010e00010000000000000078000000010000000000010005007800010000000000020007007d0001000000000003002a008400010000000000040005
00780001000000000005000d00ae0001000000000006000d00bb0001000000000009001000c8000100000000000c000e00d8000100000000000d004c00e6000100000000000e001a0132000300010409
000000f0014c0003000104090001000a023c0003000104090002000e02460003000104090003005402540003000104090004000a023c0003000104090005001a02a80003000104090006001a02c20003
000104090009002002dc000300010409000c001c02fc000300010409000d00980318000300010409000e003403b0436f707972696768742028632920323031312c204a6f6e617468616e2050696e686f
726e202870696e686f726e2e7479706564657369676e40676d61696c2e636f6d292c207769746820526573657276656420466f6e74204e616d657320224b61726c612220616e6420224b61726c612054
616d696c222e4b61726c61526567756c6172466f6e74466f72676520322e30203a204b61726c6120526567756c6172203a2031332d31302d3230313156657273696f6e20312e3030304b61726c612d52
6567756c61724a6f6e617468616e2050696e686f726e6a6f6e70696e686f726e2e636f6d5468697320466f6e7420536f667477617265206973206c6963656e73656420756e646572207468652053494c
204f70656e20466f6e74204c6963656e73652c2056657273696f6e20312e312e687474703a2f2f736372697074732e73696c2e6f72672f4f464c0043006f007000790072006900670068007400200028
0063002900200032003000310031002c0020004a006f006e0061007400680061006e002000500069006e0068006f0072006e0020002800700069006e0068006f0072006e002e00740079007000650064
0065007300690067006e00400067006d00610069006c002e0063006f006d0029002c0020007700690074006800200052006500730065007200760065006400200046006f006e00740020004e0061006d
0065007300200022004b00610072006c0061002200200061006e006400200022004b00610072006c0061002000540061006d0069006c0022002e004b00610072006c00610052006500670075006c0061
00720046006f006e00740046006f00720067006500200032002e00300020003a0020004b00610072006c006100200052006500670075006c006100720020003a002000310033002d00310030002d0032
00300031003100560065007200730069006f006e00200031002e003000300030004b00610072006c0061002d0052006500670075006c00610072004a006f006e0061007400680061006e002000500069
006e0068006f0072006e006a006f006e00700069006e0068006f0072006e002e0063006f006d005400680069007300200046006f006e007400200053006f006600740077006100720065002000690073
0020006c006900630065006e00730065006400200075006e0064006500720020007400680065002000530049004c0020004f00700065006e00200046006f006e00740020004c006900630065006e0073
0065002c002000560065007200730069006f006e00200031002e0031002e0068007400740070003a002f002f0073006300720069007000740073002e00730069006c002e006f00720067002f004f0046
004c00000002000000000000ffb50032000000000000000000000000000000000000000000a70000000100020003000400050006000700080009000a000b000c000d000e000f00100011001200130014
00150016001700180019001a001b001c001d001e001f0020002100220023002400250026002700280029002a002b002c002d002e002f0030003100320033003400350036003700380039003a003b003c
003d003e003f0040004100420043004400450046004700480049004a004b004c004d004e004f0050005100520053005400550056005700580059005a005b005c005d005e005f006000610085008e008d
00ad00c900c700ae006200cb006500c800ca00cf00cc00cd00ce006600d300d000d100af006700d600d400d50068006a0069006b006d006c007100700072007300750074007600770078007a0079007b
007d007c007f007e0080008100d7010200d800d900b200b300b600b7010300ef010401050106010701080109010a010b010c010d0a61706f7374726f7068650b727570656573796d626f6c0c7a65726f
2e70726f706f6c640b6f6e652e70726f706f6c640b74776f2e70726f706f6c640d74687265652e70726f706f6c640c666f75722e70726f706f6c640c666976652e70726f706f6c640b7369782e70726f
706f6c640d736576656e2e70726f706f6c640d65696768742e70726f706f6c640c6e696e652e70726f706f6c6400000000010001ffff000f0000000100000000c9896f3100000000ca151d3000000000
caba9b7000010000000c00000016001e00020001000100a6000100040000000100000002000100000000000000010000000a001e002c00016c61746e0008000400000000ffff0001000000016b65726e
000800000001000000010004000200000001000800010052000400000024009800ba00c0010601280136013c01420148019e01d8020e02140282029402a202b002be02d802e202ec02f20300030a0314
03260330033e034c0366036c0376038c039e03a803be0002000b002400240000002700270001002900290002002f002f00030032003400040036003700070039003c000900440046000d0048004b0010
004d004e00140050005d001600080037ffd80039ffe4003affe4003cffe60049fff40059ffea005affe8005cffe40001003cffe700110024ffc7003afff8003bfff0003cfff80044ffe80046ffe40047
ffe80048ffe4004affd30052ffe40054ffe40058ffe80059ffe0005affdc005bffe4005cffe4005dffe80008002d00100037ffcf0039ffd3003affd7003cffbb0059ffe8005affe8005cffec00030039
fff8003bfff4003cfff000010024ffda0001003cfff00001003cffe600150024ffd8002dffbb0044ffa90046ffb50047ffab0048ffb50049ffcb004affa90050ffbb0051ffbb0052ffb10053ffbb0054
ffb50055ffb70056ff9f0058ffbb0059ffb7005affd8005bffc7005cffce005dffcb000e0024ffdf0026fff8002dffbb0032fff80044ffdf0046ffdf0047ffdf0048ffdf0049ffd8004affcf0052ffdf
0054ffe30055ffef0056ffdf000d0024ffdf002dffbb0044ffdf0046ffdf0047ffdf0048ffdf0049ffd8004affcf0052ffdf0054ffdf0055ffdf0056ffd80058ffe400010032fff4001b0024ffbb0026
ffef002bfff8002dffae0032fff00034fff00036ffe40044ffc30046ffbf0047ffbf0048ffbf0049ffbf004affb30050ffbf0051ffbf0052ffbf0053ffbf0054ffbf0055ffbf0056ffbf0057ffdb0058
ffbd0059ffd3005affd3005bffcb005cffc7005dffbf00040037ffa70039ffdb003affe4003cffcb00030037ffec0039ffe3003cffef00030037ffc30039ffe4003cffdf00030037ffc30039ffe8003c
ffd700060024fff0002dffcb0046fff0004affe6004dfff00056ffe80002004d0049005c001000020037fff8003cffd300010049ffea00030049fff0004dfff8005cfff000020037ffdc003cffd70002
0037ffd4003cffd700040037ffb90039ffdf003affdf003cffbf00020037ffcb003cffdc00030037ffb7003cffd1004d00510003002dffd80037ffd0004affec00060037ffa70039ffe4003affdc003c
ffc70049ffe4005cffec0001003cffdb00020037ffc7003cffcf00050024ffe8002dffcb0037ffc4003cffd3004afff800040024ffe4002dffd70037ffe0003cffe000020037ffd7003cffcf00050024
fff4002dffe80037ffc8003cffe4004afff800020037ffe8003cffd700010000000a001e002c00016c61746e0008000400000000ffff0001000000016f6e756d00080000000100000001000400010000
000100080002001a000a0000000000000000000000000000000000000000000200010013001c00000000000100000000
]],
["fonts/ProggyTiny.ttf"] = [[
00010000000c0080000300404f532f3287eb7310000001480000004e636d617002122375000003a0000001526376742000000000000004fc00000002676c7966d3ee60d00000070400007cd468656164
d71168b2000000cc000000366868656106c201c30000010400000024686d747858804c8000000198000002066c6f636145f364f400000500000002046d61787001a600ba00000128000000206e616d65
01fc6ebc000083d80000019b706f7374a6ac83ef00008574000005d27072657069020112000004f4000000080001000000010000f6a410ea5f0f3cf50003080000000000b767778400000000bd92a933
0000ff00030004000000000600020000000000000001000003c0fec0000003000000000003000001000000000000000000000000000000020001000001010070001c00000000000200080040000a0000
00760008000000000000030001900005000002bc028a0000008f02bc028a000001c50032018000000000040900000000000000000000000000000000000000000000416c74730040000020ac08000000
0000040001000000030000000300030003000300030003000300030003000300030003000300030003000300030003000300030003000300030003000300030003000300030003000300030003000300
0100008000000000000000000100008000800000000000800000010000000000008000000000000000000000000000000000010000800000000000000000000000000000000000000000000000000000
0080000000000000000000000000000000000000000000000000000000000000000000000080000000800000000001000000000000000000000000000000000000800000008000800000000000000000
0000000000000080000000000000000000000000008001000080000003000000030001000080008000000000000000800000000000800000030000000300030001000100008000800100000000000080
0000000001000000030000000000030001000080000000000000010000000080000000800000008003000000000000800000008000800100000000000100010000800080000000000000000000000000
0000000000000000000000000000000000000000000000800080008000800000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000
00000000000000800080008000800000000000000000000000000000000000000000000000000000000000000000000000000003000000000000001c000100000000004c000300010000001c00040030
00000008000800020000007f00ff20acffff00000000008120acffff00010001dfd500010000000000000000000001060000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000810000000000000000000000000000000000000000000000000000000000000000000000000000b100018db801ff8500000000
0000009c009c009c009c009c009c009c009c009c009c009c009c009c009c009c009c009c009c009c009c009c009c009c009c009c009c009c009c009c009c009c009c009c009c00c600ee015001b00204
0266027e02b802f003320368037a039803a403e00442047e04d20522057405d00626066606c8071e0732074c077c07b207e2081a089e08ec095a09a60a040a640ab20b140b780bb80bfe0c520c920d02
0d780dd60e2c0e940efa0f4e0f8e0fe81032109410e4112c118011cc1202124e1272129412a612f41354139213f41446149214f2154a157815ba16081640169616de1726178217de1814185a189a18e2
191c1964199e19fa1a421a7c1ab61aee1b141b141b7e1b7e1b901bde1bfa1c101c5c1cbc1cd41d181d7a1d9e1e0a1e0a1e641e641e641e7c1e941ebc1ee41f001f1e1f401f5e1fb4200c203020862086
20e021302130215a21a221fc225822ae22e2234a235c23da240c244c2476247624ec250a2528257625a625d625ea263e26cc26da26f62728275227922802286e28ec2924296c29b62a062a5a2aa42af8
2b682bc02c202c822cea2d4a2d8c2dce2e162e582ec42f322f842fd83032309030e2311c317831c83218326e32be330e336033c83420347a34da353e359635fa364e369836f6375437b838163848387c
38b638e8394239a239f43a483aa23b003b523b7e3bda3c2e3c843cde3d323d9c3e023e6a001c0000ff000300040000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043
0047004b004f00530057005b005f00630067006b006f00001135331531353315313533153135331531353315313533150535331521353315053533152135331505353315213533150535331521353315
0535331521353315053533152135331505353315213533150535331521353315053533153135331531353315313533153135331531353315808080808080fd0080020080fd0080020080fd0080020080
fd0080020080fd0080020080fd0080020080fd0080020080fd0080020080fd00808080808080038080808080808080808080808080808080808080808080808080808080808080808080808080808080
808080808080808080808080808080808080808080808080800000000006010000000180038000030007000b000f00130017000001353315073533150735331507353315073533150335331501008080
80808080808080808003008080808080808080808080808080ff0080800000000006008002800200040000030007000b000f001300170000133533153335331505353315333533150535331533353315
80808080fe80808080fe808080800380808080808080808080808080808000000012000000000280030000030007000b000f00130017001b001f00230027002b002f00330037003b003f004300470000
13353315333533150535331531353315313533153135331531353315053533153335331505353315333533150535331531353315313533153135331531353315053533153335331580808080fe008080
808080fe00808080fe80808080fe008080808080fe0080808002808080808080808080808080808080808080808080808080808080808080808080808080808080808080001100000000028003800003
0007000b000f00130017001b001f00230027002b002f00330037003b003f0043000001353315053533153135331531353315313533150535331533353315053533153135331531353315053533153335
33150535331531353315313533153135331505353315010080ff0080808080fd80808080ff00808080ff00808080fd8080808080ff008003008080808080808080808080808080808080808080808080
8080808080808080808080808080808080000000000e000000000300030000030007000b000f00130017001b001f00230027002b002f0033003700001335331521353315053533153335331533353315
0535331533353315053533153335331505353315333533153335331505353315213533158080018080fd008080808080fe00808080ff00808080fe008080808080fd0080018080028080808080808080
808080808080808080808080808080808080808080808080808000000011000000000300038000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430000133533153135
3315053533152135331505353315213533150535331531353315053533152135331533353315053533152135331505353315313533153135331533353315808080fe8080010080fe0080010080fe8080
80fe80800100808080fd0080018080fe0080808080800300808080808080808080808080808080808080808080808080808080808080808080808080808080800003010002800180040000030007000b
000001353315073533150735331501008080808080038080808080808080800000090080ff800200040000030007000b000f00130017001b001f00230000013533150535331505353315073533150735
3315073533150735331d0135331d01353315018080ff0080ff0080808080808080808080800380808080808080808080808080808080808080808080808080808000000000090080ff80020004000003
0007000b000f00130017001b001f002300001335331d0135331d01353315073533150735331507353315073533150535331505353315808080808080808080808080ff0080ff00800380808080808080
8080808080808080808080808080808080808080000b000000800280030000030007000b000f00130017001b001f00230027002b00000135331505353315333533153335331505353315313533153135
331505353315333533153335331505353315010080fe808080808080fe00808080fe008080808080fe808002808080808080808080808080808080808080808080808080808080000009000000800280
030000030007000b000f00130017001b001f002300000135331507353315053533153135331531353315313533153135331505353315073533150100808080fe808080808080fe808080800280808080
808080808080808080808080808080808080800000020080ff80018000800003000700002135331505353315010080ff00808080808080000005000001800280020000030007000b000f001300001135
33153135331531353315313533153135331580808080800180808080808080808080800000010100000001800080000300002135331501008080800000090000ff800280040000030007000b000f0013
0017001b001f002300000135331507353315053533150735331505353315073533150535331507353315053533150200808080ff00808080ff00808080ff00808080ff00800380808080808080808080
80808080808080808080808080808080800000000011000000000280038000030007000b000f00130017001b001f00230027002b002f00330037003b003f004300001335331531353315313533150535
33152135331505353315213533150535331533353315333533150535331521353315053533152135331505353315313533153135331580808080fe0080018080fd8080018080fd808080808080fd8080
018080fd8080018080fe00808080030080808080808080808080808080808080808080808080808080808080808080808080808080808080000a008000000200038000030007000b000f00130017001b
001f00230027000001353315053533153135331507353315073533150735331507353315053533153135331531353315010080ff0080808080808080808080ff00808080030080808080808080808080
80808080808080808080808080808080000f000000000280038000030007000b000f00130017001b001f00230027002b002f00330037003b000013353315313533153135331505353315213533150735
331505353315313533150535331505353315073533153135331531353315313533153135331580808080fe00800180808080fe808080fe8080ff00808080808080800300808080808080808080808080
808080808080808080808080808080808080808080808080000e000000000280038000030007000b000f00130017001b001f00230027002b002f00330037000013353315313533153135331505353315
2135331507353315053533153135331d01353315053533152135331505353315313533153135331580808080fe00800180808080fe80808080fd8080018080fe00808080030080808080808080808080
808080808080808080808080808080808080808080808080000e000000000280038000030007000b000f00130017001b001f00230027002b002f00330037000001353315053533153135331505353315
33353315053533152135331505353315313533153135331531353315313533150535331507353315018080ff008080fe80808080fe0080010080fe008080808080ff0080808003008080808080808080
808080808080808080808080808080808080808080808080808000000011000000000280038000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430000113533153135
331531353315313533153135331505353315073533150735331531353315313533153135331d0135331505353315213533150535331531353315313533158080808080fd80808080808080808080fd80
80018080fe0080808003008080808080808080808080808080808080808080808080808080808080808080808080808080808000000f000000000280038000030007000b000f00130017001b001f0023
0027002b002f00330037003b000001353315313533150535331505353315073533153135331531353315313533150535331521353315053533152135331505353315313533153135331501008080fe80
80ff00808080808080fe0080018080fd8080018080fe008080800300808080808080808080808080808080808080808080808080808080808080808080808080000b000000000280038000030007000b
000f00130017001b001f00230027002b0000113533153135331531353315313533153135331507353315053533150735331505353315073533150535331580808080808080ff00808080ff00808080ff
00800300808080808080808080808080808080808080808080808080808080800011000000000280038000030007000b000f00130017001b001f00230027002b002f00330037003b003f004300001335
33153135331531353315053533152135331505353315213533150535331531353315313533150535331521353315053533152135331505353315313533153135331580808080fe0080018080fd808001
8080fe00808080fe0080018080fd8080018080fe008080800300808080808080808080808080808080808080808080808080808080808080808080808080808080800000000f00000000028003800003
0007000b000f00130017001b001f00230027002b002f00330037003b00001335331531353315313533150535331521353315053533152135331505353315313533153135331531353315073533150535
3315053533153135331580808080fe0080018080fd8080018080fe00808080808080ff0080fe808080030080808080808080808080808080808080808080808080808080808080808080808080808000
000201000080018002800003000700000135331503353315010080808002008080fe8080800000000003008000000180028000030007000b00000135331503353315053533150100808080ff00800200
8080fe8080808080800000000008000000800280030000030007000b000f00130017001b001f0000013533150535331531353315053533153135331d013533153135331d01353315020080fe808080fe
00808080808002808080808080808080808080808080808080808080000a000001000280028000030007000b000f00130017001b001f0023002700001135331531353315313533153135331531353315
01353315313533153135331531353315313533158080808080fd808080808080020080808080808080808080ff00808080808080808080800008000000800280030000030007000b000f00130017001b
001f00001135331d013533153135331d01353315313533150535331531353315053533158080808080fe008080fe80800280808080808080808080808080808080808080808000000009000000000280
038000030007000b000f00130017001b001f0023000013353315313533153135331505353315213533150735331505353315053533150335331580808080fe00800180808080ff0080ff008080800300
8080808080808080808080808080808080808080ff0080800019000000000300038000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f00530057005b
005f006300001335331531353315313533153135331505353315213533150535331533353315313533153335331505353315333533153135331533353315053533153335331531353315313533153135
33150535331d01353315313533153135331531353315313533158080808080fd8080020080fd00808080808080fd00808080808080fd00808080808080fd008080808080800300808080808080808080
808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808000000d000000000280038000030007000b000f00130017001b001f00230027002b
002f00330000013533150735331505353315333533150535331533353315053533153135331531353315053533152135331505353315213533150100808080ff00808080fe80808080fe80808080fe00
80018080fd8080018080030080808080808080808080808080808080808080808080808080808080808080800014000000000280038000030007000b000f00130017001b001f00230027002b002f0033
0037003b003f00430047004b004f000011353315313533153135331531353315053533152135331505353315213533150535331531353315313533153135331505353315213533150535331521353315
0535331531353315313533153135331580808080fe0080018080fd8080018080fd8080808080fe0080018080fd8080018080fd8080808080030080808080808080808080808080808080808080808080
808080808080808080808080808080808080808080808080000d000000000280038000030007000b000f00130017001b001f00230027002b002f00330000133533153135331531353315053533152135
3315053533150735331507353315073533152135331505353315313533153135331580808080fe0080018080fd8080808080808080018080fe0080808003008080808080808080808080808080808080
808080808080808080808080808080000010000000000280038000030007000b000f00130017001b001f00230027002b002f00330037003b003f00001135331531353315313533150535331521353315
0535331521353315053533152135331505353315213533150535331521353315053533153135331531353315808080fe8080010080fe0080018080fd8080018080fd8080018080fd8080010080fe0080
80800300808080808080808080808080808080808080808080808080808080808080808080808080808000000012000000000280038000030007000b000f00130017001b001f00230027002b002f0033
0037003b003f0043004700001135331531353315313533153135331531353315053533150735331507353315313533153135331531353315053533150735331507353315313533153135331531353315
313533158080808080fd808080808080808080fe0080808080808080808003008080808080808080808080808080808080808080808080808080808080808080808080808080808080800000000e0000
00000280038000030007000b000f00130017001b001f00230027002b002f0033003700001135331531353315313533153135331531353315053533150735331507353315313533153135331531353315
0535331507353315073533158080808080fd808080808080808080fe00808080808003008080808080808080808080808080808080808080808080808080808080808080808000000011000000000280
038000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430000133533153135331531353315053533152135331505353315073533152135331531353315053533152135
331505353315213533150535331531353315313533153135331580808080fe0080018080fd8080808001008080fd8080018080fd8080018080fe00808080800300808080808080808080808080808080
808080808080808080808080808080808080808080808080800000000011000000000280038000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430000113533152135
331505353315213533150535331521353315053533153135331531353315313533153135331505353315213533150535331521353315053533152135331580018080fd8080018080fd8080018080fd80
8080808080fd8080018080fd8080018080fd8080018080030080808080808080808080808080808080808080808080808080808080808080808080808080808080000000000b00800000020003800003
0007000b000f00130017001b001f00230027002b0000133533153135331531353315053533150735331507353315073533150735331505353315313533153135331580808080ff008080808080808080
80ff00808080030080808080808080808080808080808080808080808080808080808080000c000000000280038000030007000b000f00130017001b001f00230027002b002f00000135331531353315
31353315073533150735331507353315073533150735331505353315313533153135331531353315010080808080808080808080808080fd808080808003008080808080808080808080808080808080
80808080808080808080808080000000000e000000000280038000030007000b000f00130017001b001f00230027002b002f003300370000113533152135331505353315213533150535331533353315
053533153135331505353315333533150535331521353315053533152135331580018080fd8080010080fe00808080fe808080ff00808080fe8080010080fe0080018080030080808080808080808080
808080808080808080808080808080808080808080808080000b000000000280038000030007000b000f00130017001b001f00230027002b000011353315073533150735331507353315073533150735
3315073533153135331531353315313533153135331580808080808080808080808080808080800300808080808080808080808080808080808080808080808080808080800000000014000000000280
038000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f0000113533152135331505353315213533150535331531353315333533153135331505353315
313533153335331531353315053533153335331533353315053533153335331533353315053533152135331580018080fd8080018080fd808080808080fd808080808080fd808080808080fd80808080
8080fd80800180800300808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080800015000000000280038000030007000b000f00130017001b
001f00230027002b002f00330037003b003f00430047004b004f005300001135331531353315213533150535331531353315213533150535331533353315333533150535331533353315333533150535
331533353315333533150535331521353315313533150535331521353315313533158080010080fd808080010080fd808080808080fd808080808080fd808080808080fd808001008080fd8080010080
800300808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080000010000000000280038000030007000b000f00130017001b001f0023
0027002b002f00330037003b003f000013353315313533153135331505353315213533150535331521353315053533152135331505353315213533150535331521353315053533153135331531353315
80808080fe0080018080fd8080018080fd8080018080fd8080018080fd8080018080fe008080800300808080808080808080808080808080808080808080808080808080808080808080808080808000
000f000000000280038000030007000b000f00130017001b001f00230027002b002f00330037003b00001135331531353315313533153135331505353315213533150535331521353315053533153135
3315313533153135331505353315073533150735331580808080fe0080018080fd8080018080fd8080808080fe0080808080800300808080808080808080808080808080808080808080808080808080
8080808080808080800000000012000000000280038000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043004700001335331531353315313533150535331521353315
0535331521353315053533152135331505353315213533150535331521353315313533150535331531353315313533153135331580808080fe0080018080fd8080018080fd8080018080fd8080018080
fd808001008080fe008080808003008080808080808080808080808080808080808080808080808080808080808080808080808080808080800000000012000000000280038000030007000b000f0013
0017001b001f00230027002b002f00330037003b003f00430047000011353315313533153135331531353315053533152135331505353315213533150535331531353315313533153135331505353315
213533150535331521353315053533152135331580808080fe0080018080fd8080018080fd8080808080fe0080018080fd8080018080fd80800180800300808080808080808080808080808080808080
808080808080808080808080808080808080808080808080000f000000000280038000030007000b000f00130017001b001f00230027002b002f00330037003b00001335331531353315313533150535
3315213533150535331d01353315313533153135331d01353315053533152135331505353315313533153135331580808080fe0080018080fd808080808080fd8080018080fe00808080030080808080
8080808080808080808080808080808080808080808080808080808080808080000b000000000280038000030007000b000f00130017001b001f00230027002b00001135331531353315313533153135
3315313533150535331507353315073533150735331507353315073533158080808080fe8080808080808080808080800300808080808080808080808080808080808080808080808080808080800000
000f000000000280038000030007000b000f00130017001b001f00230027002b002f00330037003b00001135331521353315053533152135331505353315213533150535331521353315053533152135
3315053533152135331505353315313533153135331580018080fd8080018080fd8080018080fd8080018080fd8080018080fd8080018080fe0080808003008080808080808080808080808080808080
8080808080808080808080808080808080808000000c000000000280038000030007000b000f00130017001b001f00230027002b002f0000113533152135331505353315213533150535331521353315
05353315333533150535331533353315053533150735331580018080fd8080018080fd8080018080fe00808080fe80808080ff0080808003008080808080808080808080808080808080808080808080
80808080808080000011000000000280038000030007000b000f00130017001b001f00230027002b002f00330037003b003f004300001135331521353315053533153335331533353315053533153335
33153335331505353315333533153335331505353315333533150535331533353315053533153335331580018080fd808080808080fd808080808080fd808080808080fe00808080fe80808080fe8080
8080030080808080808080808080808080808080808080808080808080808080808080808080808080808080000d000000000280038000030007000b000f00130017001b001f00230027002b002f0033
00001135331521353315053533152135331505353315333533150535331505353315333533150535331521353315053533152135331580018080fd8080018080fe00808080ff0080ff00808080fe0080
018080fd80800180800300808080808080808080808080808080808080808080808080808080808080808000000c000000000280038000030007000b000f00130017001b001f00230027002b002f0000
11353315213533150535331521353315053533152135331505353315313533153135331505353315073533150735331580018080fd8080018080fd8080018080fe00808080ff00808080808003008080
80808080808080808080808080808080808080808080808080808080000f000000000280038000030007000b000f00130017001b001f00230027002b002f00330037003b000011353315313533153135
331531353315313533150735331505353315053533150535331505353315073533153135331531353315313533153135331580808080808080ff0080ff0080ff0080ff00808080808080800300808080
808080808080808080808080808080808080808080808080808080808080808080000000000d0080ff800200040000030007000b000f00130017001b001f00230027002b002f00330000133533153135
3315313533150535331507353315073533150735331507353315073533150735331507353315313533153135331580808080fe8080808080808080808080808080808080800380808080808080808080
8080808080808080808080808080808080808080808080808000000000090000ff800280040000030007000b000f00130017001b001f00230000113533150735331d013533150735331d013533150735
331d013533150735331d01353315808080808080808080808080800380808080808080808080808080808080808080808080808080808000000d0080ff800200040000030007000b000f00130017001b
001f00230027002b002f00330000133533153135331531353315073533150735331507353315073533150735331507353315073533150535331531353315313533158080808080808080808080808080
80808080fe808080800380808080808080808080808080808080808080808080808080808080808080808080800000000005000002000280038000030007000b000f0013000001353315053533153335
33150535331521353315010080ff00808080fe00800180800300808080808080808080808080000000060000ff800300000000030007000b000f00130017000015353315313533153135331531353315
31353315313533158080808080808080808080808080808080808000000201000280020003800003000700000135331d01353315010080800300808080808000000e000000000280028000030007000b
000f00130017001b001f00230027002b002f00330037000013353315313533153135331d0135331505353315313533153135331531353315053533152135331505353315313533153135331531353315
8080808080fe0080808080fd8080018080fe0080808080020080808080808080808080808080808080808080808080808080808080808080800000000011000000000280040000030007000b000f0013
0017001b001f00230027002b002f00330037003b003f00430000113533150735331507353315073533153135331531353315313533150535331521353315053533152135331505353315213533150535
331531353315313533153135331580808080808080808080fe0080018080fd8080018080fd8080018080fd80808080800380808080808080808080808080808080808080808080808080808080808080
808080808080808080808000000b000000000280028000030007000b000f00130017001b001f00230027002b00001335331531353315313533153135331505353315073533150735331d013533153135
331531353315313533158080808080fd808080808080808080800200808080808080808080808080808080808080808080808080808000000011000000000280040000030007000b000f00130017001b
001f00230027002b002f00330037003b003f0043000001353315073533150735331505353315313533153135331531353315053533152135331505353315213533150535331521353315053533153135
3315313533153135331502008080808080fe0080808080fd8080018080fd8080018080fd8080018080fe0080808080038080808080808080808080808080808080808080808080808080808080808080
808080808080808080800000000f000000000280028000030007000b000f00130017001b001f00230027002b002f00330037003b00001335331531353315313533150535331521353315053533153135
33153135331531353315313533150535331d0135331531353315313533153135331580808080fe0080018080fd808080808080fd80808080808002008080808080808080808080808080808080808080
80808080808080808080808080800000000d000000000280040000030007000b000f00130017001b001f00230027002b002f003300000135331531353315313533150535331507353315053533153135
33153135331531353315053533150735331507353315073533150100808080fe00808080ff0080808080fe80808080808080800380808080808080808080808080808080808080808080808080808080
808080808080000000110000ff000280028000030007000b000f00130017001b001f00230027002b002f00330037003b003f004300001335331531353315313533150535331521353315053533152135
33150535331521353315053533153135331531353315313533150735331505353315313533153135331580808080fe0080018080fd8080018080fd8080018080fe00808080808080fe00808080020080
80808080808080808080808080808080808080808080808080808080808080808080808080808000000f000000000280040000030007000b000f00130017001b001f00230027002b002f00330037003b
000011353315073533150735331507353315313533153135331531353315053533152135331505353315213533150535331521353315053533152135331580808080808080808080fe0080018080fd80
80018080fd8080018080fd8080018080038080808080808080808080808080808080808080808080808080808080808080808080808080000007008000000180038000030007000b000f00130017001b
000001353315013533153135331507353315073533150735331507353315010080ff008080808080808080808003008080ff008080808080808080808080808080808000000b0000ff00020003800003
0007000b000f00130017001b001f00230027002b00000135331501353315313533150735331507353315073533150735331507353315053533153135331531353315018080ff00808080808080808080
808080fe0080808003008080ff008080808080808080808080808080808080808080808080808080000d008000000280040000030007000b000f00130017001b001f00230027002b002f003300001335
33150735331507353315073533152135331505353315333533150535331531353315053533153335331505353315213533158080808080808080010080fe00808080fe808080ff00808080fe80800100
8003808080808080808080808080808080808080808080808080808080808080808080800009008000000180040000030007000b000f00130017001b001f002300001335331531353315073533150735
3315073533150735331507353315073533150735331580808080808080808080808080808080800380808080808080808080808080808080808080808080808080800000000f00000000028002800003
0007000b000f00130017001b001f00230027002b002f00330037003b00001135331531353315333533150535331533353315333533150535331533353315333533150535331533353315333533150535
3315333533153335331580808080fe008080808080fd808080808080fd808080808080fd8080808080800200808080808080808080808080808080808080808080808080808080808080808080800000
000c000000000280028000030007000b000f00130017001b001f00230027002b002f00001135331531353315313533153135331505353315213533150535331521353315053533152135331505353315
2135331580808080fe0080018080fd8080018080fd8080018080fd80800180800200808080808080808080808080808080808080808080808080808080800000000c000000000280028000030007000b
000f00130017001b001f00230027002b002f000013353315313533153135331505353315213533150535331521353315053533152135331505353315313533153135331580808080fe0080018080fd80
80018080fd8080018080fe0080808002008080808080808080808080808080808080808080808080808080808000000000100000ff000280028000030007000b000f00130017001b001f00230027002b
002f00330037003b003f00001135331531353315313533153135331505353315213533150535331521353315053533152135331505353315313533153135331531353315053533150735331580808080
fe0080018080fd8080018080fd8080018080fd8080808080fe008080800200808080808080808080808080808080808080808080808080808080808080808080808080808000000000100000ff000280
028000030007000b000f00130017001b001f00230027002b002f00330037003b003f00001335331531353315313533153135331505353315213533150535331521353315053533152135331505353315
31353315313533153135331507353315073533158080808080fd8080018080fd8080018080fd8080018080fe008080808080808080020080808080808080808080808080808080808080808080808080
808080808080808080808080800000000009000000000280028000030007000b000f00130017001b001f0023000011353315333533153135331505353315313533152135331505353315073533150735
331580808080fe008080010080fd808080808080020080808080808080808080808080808080808080808080000d000000000280028000030007000b000f00130017001b001f00230027002b002f0033
0000133533153135331531353315313533150535331d01353315313533153135331d01353315053533153135331531353315313533158080808080fd808080808080fd80808080800200808080808080
808080808080808080808080808080808080808080808080000b008000000280038000030007000b000f00130017001b001f00230027002b000013353315073533150735331531353315313533150535
3315073533150735331d0135331531353315313533158080808080808080fe8080808080808080800300808080808080808080808080808080808080808080808080808080800000000c000000000280
028000030007000b000f00130017001b001f00230027002b002f000011353315213533150535331521353315053533152135331505353315213533150535331531353315313533153135331580018080
fd8080018080fd8080018080fd8080018080fe008080808002008080808080808080808080808080808080808080808080808080808000000009000000000280028000030007000b000f00130017001b
001f0023000011353315213533150535331521353315053533153335331505353315333533150535331580018080fd8080018080fe00808080fe80808080ff0080020080808080808080808080808080
808080808080808080000000000c000000000280028000030007000b000f00130017001b001f00230027002b002f00001135331521353315053533153335331533353315053533153335331533353315
0535331533353315053533153335331580018080fd808080808080fd808080808080fe00808080fe80808080020080808080808080808080808080808080808080808080808080808080000000090000
00000280028000030007000b000f00130017001b001f0023000011353315213533150535331533353315053533150535331533353315053533152135331580018080fe00808080ff0080ff00808080fe
008001808002008080808080808080808080808080808080808080808000000000100000ff000280028000030007000b000f00130017001b001f00230027002b002f00330037003b003f000011353315
21353315053533152135331505353315213533150535331521353315053533153135331531353315313533150735331505353315313533153135331580018080fd8080018080fd8080018080fd808001
8080fe00808080808080fe008080800200808080808080808080808080808080808080808080808080808080808080808080808080808000000d000000000280028000030007000b000f00130017001b
001f00230027002b002f00330000113533153135331531353315313533153135331505353315053533150535331505353315313533153135331531353315313533158080808080ff0080ff0080ff0080
ff00808080808002008080808080808080808080808080808080808080808080808080808080800000090080ff800200040000030007000b000f00130017001b001f0023000001353315053533150735
3315073533150535331d01353315073533150735331d01353315018080ff008080808080ff00808080808080800380808080808080808080808080808080808080808080808080808000000000090100
ff800180040000030007000b000f00130017001b001f00230000013533150735331507353315073533150735331507353315073533150735331507353315010080808080808080808080808080808080
800380808080808080808080808080808080808080808080808080808000000000090080ff800200040000030007000b000f00130017001b001f002300001335331d01353315073533150735331d0135
3315053533150735331507353315053533158080808080808080ff008080808080ff0080038080808080808080808080808080808080808080808080808080800006000001000300020000030007000b
000f001300170000133533153135331521353315053533152135331531353315808080010080fd0080010080800180808080808080808080808080800013000000000300038000030007000b000f0013
0017001b001f00230027002b002f00330037003b003f00430047004b00000135331531353315313533150535331521353315053533153135331531353315313533150535331505353315313533153135
33153135331505353315213533150535331531353315313533150100808080fe0080018080fd0080808080fe8080ff0080808080fe8080018080fe008080800300808080808080808080808080808080
808080808080808080808080808080808080808080808080808080808000000000020100ff80020000800003000700002135331505353315018080ff0080808080808000000d0080ff00028004000003
0007000b000f00130017001b001f00230027002b002f00330000013533153135331505353315073533150535331531353315313533150535331507353315073533150735331507353315053533150180
8080fe80808080ff00808080ff00808080808080808080ff0080038080808080808080808080808080808080808080808080808080808080808080808080800000040080ff800200008000030007000b
000f00003335331533353315053533153335331580808080fe8080808080808080808080808000000003000000000280008000030007000b000031353315333533153335331580808080808080808080
80000000000d0000ff800280040000030007000b000f00130017001b001f00230027002b002f003300000135331507353315053533153135331531353315313533153135331505353315073533150735
33150735331507353315073533150100808080fe808080808080fe8080808080808080808080800380808080808080808080808080808080808080808080808080808080808080808080800000110000
ff800280040000030007000b000f00130017001b001f00230027002b002f00330037003b003f004300000135331507353315053533153135331531353315313533153135331505353315053533153135
3315313533153135331531353315053533150735331507353315073533150100808080fe808080808080fe8080fe808080808080fe808080808080808003808080808080808080808080808080808080
808080808080808080808080808080808080808080808080800000000003008003000200040000030007000b0000013533150535331533353315010080ff0080808003808080808080808000000b0000
00800300030000030007000b000f00130017001b001f00230027002b000013353315213533150535331533353315053533150535331533353315333533150535331521353315333533158080010080fe
00808080ff0080ff008080808080fd008001008080800280808080808080808080808080808080808080808080808080808000000012000000000280040000030007000b000f00130017001b001f0023
0027002b002f00330037003b003f004300470000133533153335331505353315013533153135331531353315313533150535331507353315313533153135331531353315313533150735331505353315
31353315313533153135331580808080ff0080ff0080808080fd80808080808080808080fd8080808080038080808080808080ff00808080808080808080808080808080808080808080808080808080
80808080808080000005008000000200028000030007000b000f0013000001353315053533150535331d0135331d01353315018080ff0080ff0080808002008080808080808080808080808080000000
0013000000000300038000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b0000133533153135331533353315313533150535331521353315053533152135
33150535331521353315313533150535331521353315053533152135331505353315313533153335331531353315808080808080fd0080010080fe0080010080fe008001008080fd8080010080fe0080
010080fe808080808080030080808080808080808080808080808080808080808080808080808080808080808080808080808080808080800010000000000280040000030007000b000f00130017001b
001f00230027002b002f00330037003b003f0000133533153335331505353315013533153135331531353315313533153135331505353315053533150535331505353315313533153135331531353315
3135331580808080ff0080fe808080808080ff0080ff0080ff0080ff008080808080038080808080808080ff008080808080808080808080808080808080808080808080808080808080800000030100
02800200040000030007000b0000013533150735331d013533150100808080800380808080808080808000000003010002800200040000030007000b00000135331507353315053533150180808080ff
0080038080808080808080800006008002800280040000030007000b000f00130017000013353315333533150535331533353315053533153335331580808080fe80808080ff00808080038080808080
8080808080808080808000000006008002800280040000030007000b000f0013001700000135331533353315053533153335331505353315333533150100808080fe80808080fe008080800380808080
8080808080808080808080000004010001000200020000030007000b000f00000135331531353315053533153135331501008080ff008080018080808080808080808000000500000100028001800003
0007000b000f0013000011353315313533153135331531353315313533158080808080010080808080808080808080000006000001000300018000030007000b000f0013001700001135331531353315
3135331531353315313533153135331580808080808001008080808080808080808080800004008001000280020000030007000b000f0000013533153335331505353315333533150100808080fe0080
808001808080808080808080800000000010000000800300028000030007000b000f00130017001b001f00230027002b002f00330037003b003f00001135331531353315313533153135331531353315
3135331505353315333533153135331531353315053533153335331533353315053533153335331533353315808080808080fd808080808080fd808080808080fd808080808080020080808080808080
808080808080808080808080808080808080808080808080808080800010000000000280040000030007000b000f00130017001b001f00230027002b002f00330037003b003f00001335331533353315
05353315013533153135331531353315313533150535331d01353315313533153135331d013533150535331531353315313533153135331580808080ff0080ff0080808080fd808080808080fd808080
8080038080808080808080ff00808080808080808080808080808080808080808080808080808080808080000005010000000280028000030007000b000f001300000135331d0135331d013533150535
3315053533150100808080ff0080ff008002008080808080808080808080808080000000000f000000000300028000030007000b000f00130017001b001f00230027002b002f00330037003b00001335
33153135331533353315313533150535331521353315053533152135331531353315053533152135331505353315313533153335331531353315808080808080fd0080010080fe008001008080fd8080
010080fe80808080808002008080808080808080808080808080808080808080808080808080808080808080808000000010000000000280040000030007000b000f00130017001b001f00230027002b
002f00330037003b003f00001335331533353315053533150135331531353315313533153135331531353315053533150535331505353315053533153135331531353315313533153135331580808080
ff0080fe808080808080ff0080ff0080ff0080ff008080808080038080808080808080ff0080808080808080808080808080808080808080808080808080808080808000000d00000000028004000003
0007000b000f00130017001b001f00230027002b002f00330000133533153335331501353315213533150535331521353315053533152135331505353315313533153135331505353315073533158080
8080fe0080018080fd8080018080fd8080018080fe00808080ff00808080038080808080ff00808080808080808080808080808080808080808080808080808080000000000601000000018003800003
0007000b000f0013001700000135331503353315073533150735331507353315073533150100808080808080808080808003008080ff008080808080808080808080808080000000000c008000000280
030000030007000b000f00130017001b001f00230027002b002f0000013533150535331531353315313533150535331533353315053533153335331505353315313533153135331505353315018080ff
00808080fe00808080fe80808080ff00808080ff0080028080808080808080808080808080808080808080808080808080808080800000000010000000000280040000030007000b000f00130017001b
001f00230027002b002f00330037003b003f0000013533153135331505353315073533150535331531353315313533153135331505353315053533150535331507353315313533153135331531353315
3135331501808080fe80808080ff0080808080fe8080ff0080ff008080808080808003808080808080808080808080808080808080808080808080808080808080808080808080808080800000100000
00000300030000030007000b000f00130017001b001f00230027002b002f00330037003b003f000011353315213533150535331531353315313533153135331505353315213533150535331521353315
05353315313533153135331531353315053533152135331580020080fd8080808080fe0080010080fe0080010080fe0080808080fd808002008002808080808080808080808080808080808080808080
8080808080808080808080808080808080000000000f000000000280038000030007000b000f00130017001b001f00230027002b002f00330037003b0000113533152135331505353315213533150535
33153135331531353315053533150535331531353315313533153135331531353315053533150735331580018080fd8080018080fe00808080ff0080fe808080808080fe808080800300808080808080
808080808080808080808080808080808080808080808080808080808080000000080100ff800180040000030007000b000f00130017001b001f00000135331507353315073533150735331503353315
073533150735331507353315010080808080808080808080808080808003808080808080808080808080ff0080808080808080808080800000120000ff000280040000030007000b000f00130017001b
001f00230027002b002f00330037003b003f0043004700000135331531353315053533150735331505353315313533153135331505353315213533150535331521353315053533153135331531353315
0535331507353315053533153135331501808080fe80808080ff00808080fe0080018080fd8080018080fe00808080ff00808080fe808080038080808080808080808080808080808080808080808080
8080808080808080808080808080808080808080808080000002008003000200038000030007000013353315333533158080808003008080808000000017000000000300038000030007000b000f0013
0017001b001f00230027002b002f00330037003b003f00430047004b004f00530057005b0000133533153135331531353315313533150535331521353315053533153335331531353315333533150535
33153335331521353315053533153335331531353315333533150535331521353315053533153135331531353315313533158080808080fd8080020080fd00808080808080fd00808080010080fd0080
8080808080fd0080020080fd8080808080030080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080000008008001800200
040000030007000b000f00130017001b001f00000135331d0135331505353315313533150535331533353315053533153135331501008080ff008080fe80808080ff0080800380808080808080808080
808080808080808080808000000a000000000300028000030007000b000f00130017001b001f002300270000013533152135331505353315213533150535331521353315053533152135331505353315
21353315010080010080fd8080010080fd8080010080fe8080010080fe808001008002008080808080808080808080808080808080808080808080800007008000000280020000030007000b000f0013
0017001b0000133533153135331531353315313533150735331507353315073533158080808080808080808080018080808080808080808080808080808080800015000000000300038000030007000b
000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f00530000133533153135331531353315313533150535331521353315053533152135331533353315053533153335
3315213533150535331533353315213533150535331521353315053533153135331531353315313533158080808080fd8080020080fd00800100808080fd00808080010080fd00808080010080fd0080
020080fd80808080800300808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080000005000003000280038000030007000b000f0013
000011353315313533153135331531353315313533158080808080030080808080808080808080000004008002000200038000030007000b000f000001353315053533153335331505353315010080ff
00808080ff008003008080808080808080808000000e000000000280030000030007000b000f00130017001b001f00230027002b002f0033003700000135331507353315053533153135331531353315
3135331531353315053533150735331505353315313533153135331531353315313533150100808080fe808080808080fe80808080fe8080808080800280808080808080808080808080808080808080
808080808080808080808080808080000008008001800200040000030007000b000f00130017001b001f0000133533153135331d01353315053533150535331507353315313533153135331580808080
ff0080ff008080808080038080808080808080808080808080808080808080800008008001800200040000030007000b000f00130017001b001f0000133533153135331d01353315053533153135331d
01353315053533153135331580808080fe80808080fe8080800380808080808080808080808080808080808080808000000201000300020004000003000700000135331505353315018080ff00800380
8080808080000000000e0000ff000300028000030007000b000f00130017001b001f00230027002b002f0033003700001335331521353315053533152135331505353315213533150535331521353315
0535331531353315313533153335331505353315053533158080010080fe0080010080fe0080010080fe0080010080fe008080808080fd8080ff00800200808080808080808080808080808080808080
80808080808080808080808080808080001a0000ff000300038000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f00530057005b005f006300670000
1335331531353315313533153135331531353315053533153135331531353315333533150535331531353315313533153335331505353315313533153335331505353315333533150535331533353315
053533153335331505353315333533150535331533353315808080808080fd008080808080fd808080808080fe0080808080fe80808080fe80808080fe80808080fe80808080fe808080800300808080
8080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080800000000001010001800180020000030000013533150100
800180808000000000040100ff000200008000030007000b000f000021353315313533150735331505353315010080808080ff008080808080808080808080000008008001800200040000030007000b
000f00130017001b001f00000135331505353315313533150735331507353315053533153135331531353315010080ff00808080808080ff008080800380808080808080808080808080808080808080
808000000006008002000200040000030007000b000f001300170000013533150535331533353315053533153335331505353315010080ff00808080fe80808080ff0080038080808080808080808080
8080808080000000000a000000000300028000030007000b000f00130017001b001f00230027000011353315213533150535331521353315053533152135331505353315213533150535331521353315
80010080fe8080010080fe8080010080fd8080010080fd80800100800200808080808080808080808080808080808080808080808080000000130000ff800300040000030007000b000f00130017001b
001f00230027002b002f00330037003b003f00430047004b0000013533150535331521353315053533152135331505353315213533150535331521353315053533153335331531353315053533153335
33153135331531353315053533152135331505353315020080fd8080018080fd8080010080fe0080010080ff0080010080fe0080808080fd808080808080fd8080018080fd0080038080808080808080
8080808080808080808080808080808080808080808080808080808080808080808080808080800000120000ff800300040000030007000b000f00130017001b001f00230027002b002f00330037003b
003f004300470000013533150535331521353315053533152135331505353315213533150535331533353315313533150535331521353315053533152135331505353315213533153135331505353315
020080fd8080018080fd8080010080fe0080010080ff0080808080fe0080010080fd8080010080fe008001008080fd008003808080808080808080808080808080808080808080808080808080808080
8080808080808080808080808080800000160000ff800300040000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b004f0053005700001135331521353315
0535331521353315053533153135331533353315053533153335331505353315333533152135331505353315333533153135331505353315333533153135331531353315053533152135331505353315
80018080fe0080010080fd8080808080fe80808080fe00808080010080fe0080808080fd808080808080fd8080018080fd00800380808080808080808080808080808080808080808080808080808080
808080808080808080808080808080808080808080808080800000000009000000000280038000030007000b000f00130017001b001f00230000013533150335331d0135331d01353315053533152135
331505353315313533153135331501008080808080fd8080018080fe0080808003008080ff0080808080808080808080808080808080808080800000000c000000000280040000030007000b000f0013
0017001b001f00230027002b002f00001335331d01353315033533150535331533353315053533153135331531353315053533152135331505353315213533158080808080ff00808080fe80808080fe
0080018080fd808001808003808080808080ff00808080808080808080808080808080808080808080808080000c000000000280040000030007000b000f00130017001b001f00230027002b002f0000
013533150535331511353315053533153335331505353315313533153135331505353315213533150535331521353315010080ff008080ff00808080fe80808080fe0080018080fd8080018080038080
80808080ff008080808080808080808080808080808080808080808080800000000d000000000280040000030007000b000f00130017001b001f00230027002b002f0033000001353315053533153335
331501353315053533153335331505353315313533153135331505353315213533150535331521353315010080ff00808080ff0080ff00808080fe80808080fe0080018080fd80800180800380808080
80808080ff008080808080808080808080808080808080808080808080800000000e000000000280040000030007000b000f00130017001b001f00230027002b002f0033003700000135331533353315
0535331533353315013533150535331533353315053533153135331531353315053533152135331505353315213533150100808080fe00808080ff0080ff00808080fe80808080fe0080018080fd8080
0180800380808080808080808080ff00808080808080808080808080808080808080808080808080000c000000000280038000030007000b000f00130017001b001f00230027002b002f000013353315
333533150135331505353315333533150535331531353315313533150535331521353315053533152135331580808080ff0080ff00808080fe80808080fe0080018080fd8080018080030080808080ff
00808080808080808080808080808080808080808080808080000000000e000000000280040000030007000b000f00130017001b001f00230027002b002f003300370000013533150535331533353315
0535331507353315053533153335331505353315313533153135331505353315213533150535331521353315010080ff00808080ff00808080ff00808080fe80808080fe0080018080fd808001808003
8080808080808080808080808080808080808080808080808080808080808080808080800014000000000300038000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043
0047004b004f0000013533153135331531353315313533150535331533353315053533153335331505353315313533153135331531353315053533152135331505353315213533150535331521353315
3135331531353315010080808080fd80808080fe80808080fe8080808080fd8080010080fe0080010080fe00800100808080030080808080808080808080808080808080808080808080808080808080
8080808080808080808080808080808080800000000f0000ff000280038000030007000b000f00130017001b001f00230027002b002f00330037003b0000133533153135331531353315053533152135
33150535331507353315073533150735331521353315053533153135331531353315053533150535331580808080fe0080018080fd8080808080808080018080fe00808080ff0080ff00800300808080
8080808080808080808080808080808080808080808080808080808080808080808080000012000000000280040000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043
004700001335331d0135331501353315313533153135331531353315313533150535331507353315313533153135331531353315053533150735331531353315313533153135331531353315808080fe
808080808080fd80808080808080fe008080808080808003808080808080ff00808080808080808080808080808080808080808080808080808080808080808080808080001200000000028004000003
0007000b000f00130017001b001f00230027002b002f00330037003b003f0043004700000135331505353315013533153135331531353315313533153135331505353315073533153135331531353315
31353315053533150735331531353315313533153135331531353315010080ff0080ff008080808080fd80808080808080fe008080808080808003808080808080ff0080808080808080808080808080
8080808080808080808080808080808080808080808080000013000000000280040000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b0000013533150535
33153335331501353315313533153135331531353315313533150535331507353315313533153135331531353315053533150735331531353315313533153135331531353315010080ff00808080fe00
8080808080fd80808080808080fe0080808080808080038080808080808080ff008080808080808080808080808080808080808080808080808080808080808080808080800000000012000000000280
038000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047000013353315333533150135331531353315313533153135331531353315053533150735331531353315
313533153135331505353315073533153135331531353315313533153135331580808080fe008080808080fd80808080808080fe0080808080808080030080808080ff00808080808080808080808080
808080808080808080808080808080808080808080808080000b008000000200040000030007000b000f00130017001b001f00230027002b00001335331d013533150135331531353315313533150535
33150735331507353315053533153135331531353315808080ff00808080ff008080808080ff0080808003808080808080ff0080808080808080808080808080808080808080808080000000000b0080
00000200040000030007000b000f00130017001b001f00230027002b00000135331505353315033533153135331531353315053533150735331507353315053533153135331531353315010080ff0080
80808080ff008080808080ff0080808003808080808080ff008080808080808080808080808080808080808080808000000c008000000200040000030007000b000f00130017001b001f00230027002b
002f0000013533150535331533353315013533153135331531353315053533150735331507353315053533153135331531353315010080ff00808080fe80808080ff008080808080ff00808080038080
808080808080ff00808080808080808080808080808080808080808080800000000b008000000200038000030007000b000f00130017001b001f00230027002b00001335331533353315013533153135
33153135331505353315073533150735331505353315313533153135331580808080fe80808080ff008080808080ff00808080030080808080ff00808080808080808080808080808080808080808080
800000000013000000000300038000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430047004b00001335331531353315313533150535331521353315053533152135
331505353315313533153135331531353315333533150535331521353315053533152135331505353315313533153135331580808080fe8080010080fe0080018080fd00808080808080fd8080018080
fd8080010080fe00808080030080808080808080808080808080808080808080808080808080808080808080808080808080808080808080800000000013000000000280040000030007000b000f0013
0017001b001f00230027002b002f00330037003b003f00430047004b00000135331533353315053533153335331501353315313533152135331505353315333533153335331505353315333533153335
33150535331521353315313533150535331521353315313533150100808080fe00808080fe008080010080fd808080808080fd808080808080fd808001008080fd808001008080038080808080808080
8080ff00808080808080808080808080808080808080808080808080808080808080808080800000000e000000000280040000030007000b000f00130017001b001f00230027002b002f003300370000
1335331d01353315013533153135331531353315053533152135331505353315213533150535331521353315053533153135331531353315808080ff00808080fe0080018080fd8080018080fd808001
8080fe0080808003808080808080ff0080808080808080808080808080808080808080808080808080808080000e000000000280040000030007000b000f00130017001b001f00230027002b002f0033
003700000135331505353315033533153135331531353315053533152135331505353315213533150535331521353315053533153135331531353315010080ff008080808080fe0080018080fd808001
8080fd8080018080fe0080808003808080808080ff00808080808080808080808080808080808080808080808080808080800000000f000000000280040000030007000b000f00130017001b001f0023
0027002b002f00330037003b0000013533150535331533353315013533153135331531353315053533152135331505353315213533150535331521353315053533153135331531353315010080ff0080
8080fe80808080fe0080018080fd8080018080fd8080018080fe00808080038080808080808080ff00808080808080808080808080808080808080808080808080808080800000000010000000000280
040000030007000b000f00130017001b001f00230027002b002f00330037003b003f00000135331533353315053533153335331501353315313533153135331505353315213533150535331521353315
05353315213533150535331531353315313533150100808080fe00808080fe80808080fe0080018080fd8080018080fd8080018080fe008080800380808080808080808080ff00808080808080808080
8080808080808080808080808080808080808000000e000000000280038000030007000b000f00130017001b001f00230027002b002f0033003700001335331533353315013533153135331531353315
05353315213533150535331521353315053533152135331505353315313533153135331580808080fe80808080fe0080018080fd8080018080fd8080018080fe00808080030080808080ff0080808080
8080808080808080808080808080808080808080808080800009000000800280030000030007000b000f00130017001b001f002300001135331521353315053533153335331505353315053533153335
3315053533152135331580018080fe00808080ff0080ff00808080fe00800180800280808080808080808080808080808080808080808080800000000010000000000300030000030007000b000f0013
0017001b001f00230027002b002f00330037003b003f00000135331531353315333533150535331521353315053533153335331531353315053533153135331533353315053533152135331505353315
3335331531353315010080808080fd8080010080fe0080808080fe0080808080fe0080010080fd8080808080028080808080808080808080808080808080808080808080808080808080808080808080
80808000000d000000000280040000030007000b000f00130017001b001f00230027002b002f003300001335331d01353315013533152135331505353315213533150535331521353315053533152135
3315053533153135331531353315808080fe8080018080fd8080018080fd8080018080fd8080018080fe0080808003808080808080ff0080808080808080808080808080808080808080808080808080
80000000000d000000000280040000030007000b000f00130017001b001f00230027002b002f003300000135331505353315013533152135331505353315213533150535331521353315053533152135
3315053533153135331531353315010080ff0080ff0080018080fd8080018080fd8080018080fd8080018080fe0080808003808080808080ff0080808080808080808080808080808080808080808080
80808080000e000000000280040000030007000b000f00130017001b001f00230027002b002f003300370000013533150535331533353315013533152135331505353315213533150535331521353315
0535331521353315053533153135331531353315010080ff00808080fe0080018080fd8080018080fd8080018080fd8080018080fe00808080038080808080808080ff00808080808080808080808080
80808080808080808080808080800000000d000000000280038000030007000b000f00130017001b001f00230027002b002f003300001335331533353315013533152135331505353315213533150535
331521353315053533152135331505353315313533153135331580808080fe0080018080fd8080018080fd8080018080fd8080018080fe00808080030080808080ff0080808080808080808080808080
80808080808080808080808080000000000d000000000280040000030007000b000f00130017001b001f00230027002b002f003300000135331505353315053533152135331505353315213533150535
3315213533150535331531353315313533150535331507353315018080ff0080fe8080018080fd8080018080fd8080018080fe00808080ff008080800380808080808080808080808080808080808080
80808080808080808080808080808000000e0080ff800280038000030007000b000f00130017001b001f00230027002b002f003300370000133533150735331507353315313533153135331505353315
21353315053533152135331505353315313533153135331505353315073533158080808080808080fe8080010080fe0080010080fe00808080fe80808080030080808080808080808080808080808080
808080808080808080808080808080808080800000120000ff800280038000030007000b000f00130017001b001f00230027002b002f00330037003b003f004300470000133533153135331531353315
05353315213533150535331521353315053533153135331531353315053533152135331505353315213533150535331531353315313533150535331580808080fe8080010080fe0080010080fe008080
80fe8080010080fe0080010080fe00808080fe00800300808080808080808080808080808080808080808080808080808080808080808080808080808080808080800000001000000000028004000003
0007000b000f00130017001b001f00230027002b002f00330037003b003f00001335331d0135331501353315313533153135331d01353315053533153135331531353315313533150535331521353315
05353315313533153135331531353315808080ff0080808080fe0080808080fd8080018080fe008080808003808080808080ff0080808080808080808080808080808080808080808080808080808080
808080800010000000000280040000030007000b000f00130017001b001f00230027002b002f00330037003b003f0000013533150535331503353315313533153135331d013533150535331531353315
3135331531353315053533152135331505353315313533153135331531353315010080ff00808080808080fe0080808080fd8080018080fe008080808003808080808080ff0080808080808080808080
8080808080808080808080808080808080808080808000000011000000000280040000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043000001353315053533153335
331501353315313533153135331d0135331505353315313533153135331531353315053533152135331505353315313533153135331531353315010080ff00808080fe8080808080fe0080808080fd80
80018080fe0080808080038080808080808080ff0080808080808080808080808080808080808080808080808080808080808080800000000012000000000280040000030007000b000f00130017001b
001f00230027002b002f00330037003b003f0043004700000135331533353315053533153335331501353315313533153135331d01353315053533153135331531353315313533150535331521353315
053533153135331531353315313533150100808080fe00808080fe8080808080fe0080808080fd8080018080fe00808080800380808080808080808080ff008080808080808080808080808080808080
808080808080808080808080808080000010000000000280038000030007000b000f00130017001b001f00230027002b002f00330037003b003f0000133533153335331501353315313533153135331d
013533150535331531353315313533153135331505353315213533150535331531353315313533153135331580808080fe8080808080fe0080808080fd8080018080fe0080808080030080808080ff00
80808080808080808080808080808080808080808080808080808080808080800012000000000280040000030007000b000f00130017001b001f00230027002b002f00330037003b003f004300470000
0135331505353315333533150535331505353315313533153135331d0135331505353315313533153135331531353315053533152135331505353315313533153135331531353315010080ff00808080
ff0080ff0080808080fe0080808080fd8080018080fe0080808080038080808080808080808080808080808080808080808080808080808080808080808080808080808080808080000f000000000300
028000030007000b000f00130017001b001f00230027002b002f00330037003b000013353315313533153335331505353315333533150535331531353315313533153135331505353315213533150535
33153135331533353315313533158080808080ff00808080fd8080808080fd8080010080fe80808080808002008080808080808080808080808080808080808080808080808080808080808080808000
000d0000ff000280028000030007000b000f00130017001b001f00230027002b002f003300001335331531353315313533153135331505353315073533150735331d0135331531353315313533153135
331505353315053533158080808080fd80808080808080808080fe8080ff0080020080808080808080808080808080808080808080808080808080808080808080800000001100000000028004000003
0007000b000f00130017001b001f00230027002b002f00330037003b003f004300001335331d013533150135331531353315313533150535331521353315053533153135331531353315313533153135
33150535331d01353315313533153135331531353315808080ff00808080fe0080018080fd808080808080fd80808080808003808080808080ff00808080808080808080808080808080808080808080
808080808080808080808080800000000011000000000280040000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430000013533150535331503353315313533153135
3315053533152135331505353315313533153135331531353315313533150535331d01353315313533153135331531353315010080ff008080808080fe0080018080fd808080808080fd808080808080
03808080808080ff0080808080808080808080808080808080808080808080808080808080808080808080000012000000000280040000030007000b000f00130017001b001f00230027002b002f0033
0037003b003f004300470000013533150535331533353315013533153135331531353315053533152135331505353315313533153135331531353315313533150535331d013533153135331531353315
31353315010080ff00808080fe80808080fe0080018080fd808080808080fd808080808080038080808080808080ff008080808080808080808080808080808080808080808080808080808080808080
808000000011000000000280038000030007000b000f00130017001b001f00230027002b002f00330037003b003f00430000133533153335331501353315313533153135331505353315213533150535
3315313533153135331531353315313533150535331d0135331531353315313533153135331580808080fe80808080fe0080018080fd808080808080fd808080808080030080808080ff008080808080
80808080808080808080808080808080808080808080808080808080800000000008008000000180040000030007000b000f00130017001b001f00001335331d01353315013533153135331507353315
073533150735331507353315808080ff008080808080808080808003808080808080ff00808080808080808080808080808080800008008000000180040000030007000b000f00130017001b001f0000
0135331505353315033533153135331507353315073533150735331507353315010080ff0080808080808080808080808003808080808080ff0080808080808080808080808080808080000000090080
00000200040000030007000b000f00130017001b001f00230000013533150535331533353315013533153135331507353315073533150735331507353315010080ff00808080fe808080808080808080
8080038080808080808080ff00808080808080808080808080808080800000000008008000000200038000030007000b000f00130017001b001f00001335331533353315013533153135331507353315
07353315073533150735331580808080fe8080808080808080808080030080808080ff00808080808080808080808080808080800010000000000280038000030007000b000f00130017001b001f0023
0027002b002f00330037003b003f00001335331531353315333533150535331d013533150535331531353315313533153135331505353315213533150535331521353315053533153135331531353315
8080808080ff008080fe0080808080fd8080018080fd8080018080fe00808080030080808080808080808080808080808080808080808080808080808080808080808080808080800010000000000280
040000030007000b000f00130017001b001f00230027002b002f00330037003b003f00000135331533353315053533153335331501353315313533153135331531353315053533152135331505353315
21353315053533152135331505353315213533150100808080fe00808080fe0080808080fe0080018080fd8080018080fd8080018080fd80800180800380808080808080808080ff0080808080808080
808080808080808080808080808080808080808080000000000e000000000280040000030007000b000f00130017001b001f00230027002b002f0033003700001335331d013533150135331531353315
31353315053533152135331505353315213533150535331521353315053533153135331531353315808080ff00808080fe0080018080fd8080018080fd8080018080fe0080808003808080808080ff00
80808080808080808080808080808080808080808080808080808080000e000000000280040000030007000b000f00130017001b001f00230027002b002f003300370000013533150535331503353315
3135331531353315053533152135331505353315213533150535331521353315053533153135331531353315010080ff008080808080fe0080018080fd8080018080fd8080018080fe00808080038080
80808080ff00808080808080808080808080808080808080808080808080808080800000000f000000000280040000030007000b000f00130017001b001f00230027002b002f00330037003b00000135
33150535331533353315013533153135331531353315053533152135331505353315213533150535331521353315053533153135331531353315010080ff00808080fe80808080fe0080018080fd8080
018080fd8080018080fe00808080038080808080808080ff00808080808080808080808080808080808080808080808080808080800000000010000000000280040000030007000b000f00130017001b
001f00230027002b002f00330037003b003f0000133533153335331505353315333533150135331531353315313533150535331521353315053533152135331505353315213533150535331531353315
3135331580808080fe00808080ff00808080fe0080018080fd8080018080fd8080018080fe008080800380808080808080808080ff008080808080808080808080808080808080808080808080808080
80800000000e000000000280038000030007000b000f00130017001b001f00230027002b002f003300370000133533153335331501353315313533153135331505353315213533150535331521353315
053533152135331505353315313533153135331580808080fe80808080fe0080018080fd8080018080fd8080018080fe00808080030080808080ff008080808080808080808080808080808080808080
80808080808080800007000000800280030000030007000b000f00130017001b000001353315013533153135331531353315313533153135331501353315010080fe808080808080fe808002808080ff
0080808080808080808080ff008080000010000000000300030000030007000b000f00130017001b001f00230027002b002f00330037003b003f00000135331531353315333533150535331521353315
0535331533353315313533150535331531353315333533150535331521353315053533153335331531353315010080808080fd8080010080fe0080808080fe0080808080fe0080010080fd8080808080
02808080808080808080808080808080808080808080808080808080808080808080808080808000000e000000000280040000030007000b000f00130017001b001f00230027002b002f003300370000
1335331d01353315013533152135331505353315213533150535331521353315053533152135331505353315313533153135331531353315808080fe8080018080fd8080018080fd8080018080fd8080
018080fe008080808003808080808080ff00808080808080808080808080808080808080808080808080808080800000000e000000000280040000030007000b000f00130017001b001f00230027002b
002f0033003700000135331505353315013533152135331505353315213533150535331521353315053533152135331505353315313533153135331531353315010080ff0080ff0080018080fd808001
8080fd8080018080fd8080018080fe008080808003808080808080ff0080808080808080808080808080808080808080808080808080808080000000000f000000000280040000030007000b000f0013
0017001b001f00230027002b002f00330037003b000001353315053533153335331501353315213533150535331521353315053533152135331505353315213533150535331531353315313533153135
3315010080ff00808080fe0080018080fd8080018080fd8080018080fd8080018080fe0080808080038080808080808080ff008080808080808080808080808080808080808080808080808080808000
000e000000000280038000030007000b000f00130017001b001f00230027002b002f00330037000013353315333533150135331521353315053533152135331505353315213533150535331521353315
0535331531353315313533153135331580808080fe0080018080fd8080018080fd8080018080fd8080018080fe0080808080030080808080ff0080808080808080808080808080808080808080808080
808080808080000000120000ff000280040000030007000b000f00130017001b001f00230027002b002f00330037003b003f004300470000013533150535331501353315213533150535331521353315
053533152135331505353315213533150535331531353315313533153135331507353315053533153135331531353315010080ff0080ff0080018080fd8080018080fd8080018080fd8080018080fe00
808080808080fe0080808003808080808080ff008080808080808080808080808080808080808080808080808080808080808080808080808080000000120000ff000280038000030007000b000f0013
0017001b001f00230027002b002f00330037003b003f00430047000011353315073533150735331531353315313533153135331505353315213533150535331521353315053533152135331505353315
31353315313533153135331505353315073533158080808080808080fe0080018080fd8080018080fd8080018080fd8080808080fe008080800300808080808080808080808080808080808080808080
80808080808080808080808080808080808080808080800000120000ff000280038000030007000b000f00130017001b001f00230027002b002f00330037003b003f0043004700001335331533353315
0135331521353315053533152135331505353315213533150535331521353315053533153135331531353315313533150735331505353315313533153135331580808080fe0080018080fd8080018080
fd8080018080fd8080018080fe00808080808080fe00808080030080808080ff008080808080808080808080808080808080808080808080808080808080808080808080808080000000001501020000
000000000000002400470000000000000001001800810000000000000002000e006b00000000000000030018008100000000000000040018008100000000000000050014000000000000000000060018
00810001000000000000001200140001000000000001000c003100010000000000020007002600010000000000030010002d0001000000000004000c00310001000000000005000a003d000100000000
0006000c00310003000104090000002400470003000104090001001800810003000104090002000e006b0003000104090003002000790003000104090004001800810003000104090005001400000003
000104090006001800810032003000300034002f00300034002f003100356279205472697374616e204772696d6d6572526567756c61725454582050726f67677954696e795454323030342f30342f31
35006200790020005400720069007300740061006e0020004700720069006d006d006500720052006500670075006c00610072005400540058002000500072006f00670067007900540069006e007900
54005400000200000000000000000014000000010000000000000000000000000000000001010000000101020103010401050106010701080109010a010b010c010d010e010f01100111011201130114
01150116011701180119011a011b011c011d011e011f01200003000400050006000700080009000a000b000c000d000e000f0010001100120013001400150016001700180019001a001b001c001d001e
001f0020002100220023002400250026002700280029002a002b002c002d002e002f0030003100320033003400350036003700380039003a003b003c003d003e003f0040004100420043004400450046
004700480049004a004b004c004d004e004f0050005100520053005400550056005700580059005a005b005c005d005e005f00600061012101220123012401250126012701280129012a012b012c012d
012e012f0130013101320133013401350136013701380139013a013b013c013d013e013f0140014100ac00a30084008500bd009600e80086008e008b009d00a900a400ef008a00da0083009300f200f3
008d0097008800c300de00f1009e00aa00f500f400f600a200ad00c900c700ae006200630090006400cb006500c800ca00cf00cc00cd00ce00e9006600d300d000d100af006700f0009100d600d400d5
006800eb00ed0089006a0069006b006d006c006e00a0006f0071007000720073007500740076007700ea0078007a0079007b007d007c00b800a1007f007e0080008100ec00ee00ba0e756e69636f6465
233078303030310e756e69636f6465233078303030320e756e69636f6465233078303030330e756e69636f6465233078303030340e756e69636f6465233078303030350e756e69636f64652330783030
30360e756e69636f6465233078303030370e756e69636f6465233078303030380e756e69636f6465233078303030390e756e69636f6465233078303030610e756e69636f6465233078303030620e756e
69636f6465233078303030630e756e69636f6465233078303030640e756e69636f6465233078303030650e756e69636f6465233078303030660e756e69636f6465233078303031300e756e69636f6465
233078303031310e756e69636f6465233078303031320e756e69636f6465233078303031330e756e69636f6465233078303031340e756e69636f6465233078303031350e756e69636f64652330783030
31360e756e69636f6465233078303031370e756e69636f6465233078303031380e756e69636f6465233078303031390e756e69636f6465233078303031610e756e69636f6465233078303031620e756e
69636f6465233078303031630e756e69636f6465233078303031640e756e69636f6465233078303031650e756e69636f6465233078303031660664656c657465044575726f0e756e69636f6465233078
303038310e756e69636f6465233078303038320e756e69636f6465233078303038330e756e69636f6465233078303038340e756e69636f6465233078303038350e756e69636f6465233078303038360e
756e69636f6465233078303038370e756e69636f6465233078303038380e756e69636f6465233078303038390e756e69636f6465233078303038610e756e69636f6465233078303038620e756e69636f
6465233078303038630e756e69636f6465233078303038640e756e69636f6465233078303038650e756e69636f6465233078303038660e756e69636f6465233078303039300e756e69636f6465233078
303039310e756e69636f6465233078303039320e756e69636f6465233078303039330e756e69636f6465233078303039340e756e69636f6465233078303039350e756e69636f6465233078303039360e
756e69636f6465233078303039370e756e69636f6465233078303039380e756e69636f6465233078303039390e756e69636f6465233078303039610e756e69636f6465233078303039620e756e69636f
6465233078303039630e756e69636f6465233078303039640e756e69636f6465233078303039650e756e69636f6465233078303039660000
]],
["fonts/Roboto-Medium.ttf"] = [[
00010000001101000004001047504f537daa718c000208a80000590c475355424c9c28e0000261b4000019684f532f32a10bb1b60000019800000060636d61704026487200001a6c000012c863767420
04972b4a00002fbc000000566670676d7bf961ab00002d34000001bc67617370000800130002089c0000000c676c7966ae9e62e9000039dc0001cbcc68646d783d3f3c2000001580000004ec68656164
f87bab080000011c00000036686865610aef0a9b0000015400000024686d747824f344f5000001f8000013886c6f6361ddde66ad00003014000009c66d617870071202f500000178000000206e616d65
3d636f4c000205a8000002d4706f7374ff6d00640002087c00000020707265701bb1f83600002ef0000000cc00010000000200001140a46d5f0f3cf5001b080000000000c4f0112e00000000d0db4e94
fa24fdd5095c087300000009000200000000000000010000076cfe0c0000096bfa24fe41095c0001000000000000000000000000000004e20001000004e2008f0016004e0005000100000000000e0000
02000216000600010003049501f400050000059a05330000011f059a0533000003d100660200000002000000000000000000e0000aff5000217f0000002100000000474f4f4700400000fffd0600fe00
0066079a02002000019f00000000043a05b0002000200002038c0064000000000000000001fe000001fe00000225008f0298006504e20060048c006405e00063051d0056015a005202ca008002d20028
0389001b0475004401c2001c02a00047023c0087032a0002048c0069048c00a8048c0051048c004f048c0034048c0081048c0075048c0045048c0068048c005d021f008201e7002e0411003f047a0091
042a008003e4003c0728005b05530012050c009405390066053a009404860094046500940572006a05af0094024200a30471002d050b0094045400940701009405ae009405860066051d009405860060
04fe009404d4004a04db002d0537007d052d0012070a00300510002904e0000704d1005002310084035800140231000c036b0035039c0003029400310454005a0481007c0430004f0484004f044b0053
02d6002d0489005204710079020b007d0201ffb5042d007d020b008c06f6007c04730079048e004f0481007c048b004f02d0007c0421004b02a900080472007703f5001605f200210406001f03e5000c
0406005202af0038020200ae02af001b05510075021e0086047d006404b5005e059d005d04e0001901fc008804f8005a0385005d064400570391008d03e20057046d007f0644005703db0087030a007f
044a005f02f6003c02f60037029b007004bb009203ed00450242008e0210006d02f6008003a7007703e2005d05d00059062b00500657006703e400420785fff60444004d0584006904ca009404e70088
06c1004804a70067049100430488004f0497008205b0001f021a008f0498008e04640022024f0021059300900488007e07b40064073a005b020c008b0588005102d0ffe4058a0058049e004f05a4007d
04f200770226ffb5043c005903e6009403b0007203dc0087037c0075020b008102b20078024d002903d8007a031f0049026c00820000fc8e0000fd5e0000fc730000fd3e0000fc0c0000fd1c025d00c6
043c00670242008e0475009b05bf0019057a005b053800200490006c05b1009b0490004705ef004a05aa0044055b006b0484005604c60096040e00200488005404600060041a00610488007e04a10073
02aa00a9046a00160413006404f3002d048800800437005204900052042d003f0460008005d0004405c9004f0694006604b30076047bffe10671003305fe0022055900680888002d088f009b065b0031
05aa0092050800900606002407a2001604d6004905a8009405a9002d050a0039065f004f05f900920589008e079b009807f90098061a001806f9009b050700900550006b075400a004f70020047d005b
048f008f035a008504f600270676001e0416004d04980086046e008f049a00210603008f049700860498008603f5002305d3005404d300860466005f068e008606ec007e0517001f066f008f0468008f
043c005106840091047000270471ffdb043c005406d1001e06e400860489ffee0498008607490088064f00700467ffe00728009806010086050c001c0460000a074200ac0636009d06ed008005e60082
093200a307f9008f0420002803f00033057a005f0488004f051a0010040e0020057a005f0488004f074500880644007407490088064f0070051a0066044a005c04ff006d0000fc660000fc730000fd7b
0000fda50000fa240000fa4d0467ffe0051300940486007c046a008f03a1007e04b7009b0420007e052c009004ab008e0695003405a4003d07d0009405aa007e0847009b06f5007e062a006704ff0061
0731002d057000260574008004730074058700850624001604c3ffcb052100900478008e05af009b0488007e0588005104a6005b04a6005d04c700340353002d0507005206f1006806dd005e0653003c
0528002f047b0048043e007407be0042069d004007fd0094069e00770504005d042c005505aa0021051d004405550081032c00670414000008290000041400000829000002b90000020a0000015c0000
047f00000230000001a2000000d100000000000002a1004702a100470529009d06300081039d000401c0006301bc003301ce003201a8004a0314006c031b004003080032045d00400499005c02cb0088
03fa008a05a6008a016c004707a7004a0272006c02690054039c002d02f60035035c006904b5005f0670002106b800980893009407880035068c007c048c005e05f500210434002804a20021055e004f
057d002805e4007003e2004c082e00900509006d051400960635005906dd005406d1005b06a2005804910062059600a604d900400483009e04b2003b0845005e022dffaf048e0065047a00910411003c
042a0080040c0024025b00a10298006301f10045051b002d04a8001804bc002d0723002d0723002d0511002d06b7004b00000000083000590835005c0433003a0493004f0210ffb001b3005c03a10075
03a1007503a10075040b0075040b0075040bff4c040b007a03a1007502050094049e0009046000760480004f047a007603e0007603c5007604a6005404de007601fc008503d50024045b007603b90076
0606007604dd007604c0004f046d007604c0004c045c00760434003e043b002404840067047b000906070028045e0015043c0005042a004102f6004b02f6008002f6003c02f6003702f6003502f6004f
02f6004d02f6003602f6004b02f6004603b9009002b20096043b000a04bb00560544009b0528009b043000810539009b042d00810434003e04660038044d000e03b90076047b000904c0004f047b0009
0398004204d8007604190044059d00500554005004e4005f059100240480004f07540024075700760597002404d700760471007605590027063a001a0446004204e40076045c007604cb00240446001f
055d0076048c004106840076070a0076055a000a06200076046700760480003c06920076048800430422000a0692001a049d0076051a0076056e002405f0004f045a000504c4001506950024048c0041
048c007605fe000a04d2004f0446004204c0004f0466003803f700460836007604eb00280488007c043d00500498004f03a4005b04a1004c0494007c049f004f044b005304890051057a006b05a2006b
0586009b05e0006b05e2006b041b00970482006e03b900760457000f04be003502f6004b02f6003502f6004f02f6004d02f6003602f6004b02f60046046b0066042e00430698004f04b4007304eb0062
0226ffb50226ffb5021b008f021bfffb021b008f0460007601fe000002a000470558fff70558fff7048fffd404db002d02a9ffe805530012055300120553001205530012055300120553001205530012
05390066048600940486009404860094048600940242ffc8024200a30242ffcb0242ffbf05ae009405860066058600660586006605860066058600660537007d0537007d0537007d0537007d04e00007
0454005a0454005a0454005a0454005a0454005a0454005a0454005a0430004f044b0053044b0053044b0053044b0053021affb4021a008f021affb7021affab04730079048e004f048e004f048e004f
048e004f048e004f0472007704720077047200770472007703e5000c03e5000c055300120454005a055300120454005a055300120454005a053900660430004f053900660430004f053900660430004f
053900660430004f053a0094051a004f04860094044b005304860094044b005304860094044b005304860094044b005304860094044b00530572006a048900520572006a048900520572006a04890052
0572006a0489005205af0094047100790242ffb3021aff9f0242ffb9021affa50242ffdf021affcb02420017020b00000242009d06b300a3040c007d0471002d0226ffb5050b0094042d007d04540094
020b008a04540094020b00550454009402a1008c0454009402e7008c05ae00940473007905ae00940473007905ae0094047300790473ffa505860066048e004f05860066048e004f05860066048e004f
04fe009402d0007c04fe009402d0004f04fe009402d0003804d4004a0421004b04d4004a0421004b04d4004a0421004b04d4004a0421004b04d4004a0421004b04db002d02a9000804db002d02a90008
04db002d02d100080537007d047200770537007d047200770537007d047200770537007d047200770537007d047200770537007d04720077070a003005f2002104e0000703e5000c04e0000704d10050
0406005204d100500406005204d10050040600520785fff606c10048058400690488004f047affa6047affa6043b0024049e0009049e0009049e0009049e0009049e0009049e0009049e00090480004f
03e0007603e0007603e0007603e0007601fcffa601fc008301fcffa901fcff9d04dd007604c0004f04c0004f04c0004f04c0004f04c0004f04840067048400670484006704840067043c0005049e0009
049e0009049e00090480004f0480004f0480004f0480004f047a006a03e0007603e0007603e0007603e0007603e0007604a6005404a6005404a6005404a6005404de007601fcff9101fcff9701fcffbd
01fc001501fc007c03d50024045b007603b9007603b9007603b9007603b9007604dd007604dd007604dd007604c0004f04c0004f04c0004f045c0076045c0076045c00760434003e0434003e0434003e
0434003e043b0024043b0024043b002404840067048400670484006704840067048400670484006706070028043c0005043c0005042a0041042a0041042a00410553001204eaff4a0613ff5302a6ff56
059affa70544fee1056fffb202aaff8705530012050c00940486009404d1005005af0094024200a3050b00940701009405ae009405860066051d009404db002d04e00007051000290242ffbf04e00007
04840056046000600488007e02aa00a9046000800498008e048e004f04bb009203f500160406001f02aaffcc04600080048e004f0460008006940066048600940475009b04d4004a024200a30242ffbf
0471002d0528009b050b0094050a003905530012050c00940475009b0486009405a800940701009405af00940586006605b1009b051d00940539006604db002d051000290454005a044b005304980086
048e004f0481007c0430004f03e5000c0406001f044b0053035a00850421004b020b007d021affab0201ffb5046e008f03e5000c070a003005f20021070a003005f20021070a003005f2002104e00007
03e5000c015a005202980065044a008f0226ffb101bc00330701009406f6007c055300120454005a0486009405a80094044b00530498008605aa004405c9004f051a0010040efff10873004f096b0066
04d600490416004d053900660430004f04e00007040e0020024200a307a200160676001e024200a3055300120454005a055300120454005a0785fff606c1004804860094044b005305880051043c0059
043c005907a200160676001e04d600490416004d05a800940498008605a800940498008605860066048e004f057a005f0488004f057a005f0488004f0550006b043c0051050a003903e5000c050a0039
03e5000c050a003903e5000c0589008e0466005f06f9009b066f008f051000290406001f0484004f05a9002d049a0021055300120454005a055300120454005a055300120454005a055300100454ff9a
055300120454005a055300120454005a055300120454005a055300120454005a055300120454005a055300120454005a055300120454005a055300120454005a04860094044b005304860094044b0053
04860094044b005304860094044b00530486ffd5044bff8e04860094044b005304860094044b005304860094044b0053024200a3021a008f02420094020b007805860066048e004f05860066048e004f
05860066048e004f05860027048effa305860066048e004f05860066048e004f05860066048e004f058a0058049e004f058a0058049e004f058a0058049e004f058a0058049e004f058a0058049e004f
0537007d047200770537007d0472007705a4007d04f2007705a4007d04f2007705a4007d04f2007705a4007d04f2007705a4007d04f2007704e0000703e5000c04e0000703e5000c04e0000703e5000c
04a2004f04a2004f0528009b046e008f05af00940497008604db002d03f50023051000290406001f0589008e0466005f0589008e0466005f0475009b035a008507a200160676001e0624001604c3ffcb
047100790507ffd00507ffd00475fff0035affe2053cffe30444ffae05a800940498008605af009404970086070100940603008f05a9002d049a002104e00007040e0020051000290406001f04600060
0465000206300081048c0051048c004f048c0034048c008104a0005d04b4007d0572006a0489005205ae009404730079055300120454000d04860048044b00010242fef6021afee205860066048e0016
04fe003202d0ff6e053700710472000f04dffeac050c00940481007c053a00940484004f053a00940484004f05af009404710079050b0094042d007d050b0094042d007d04540094020b007807010094
06f6007c05ae009404730079051d00940481007c04fe009402d0007204d4004a0421004b04db002d02a90008052d001203f50016052d001203f50016070a003005f2002104d100500406005205ccfe1c
049e0009041cff2a051aff370238ff3904caff930478fee804eeffa4049e00090460007603e00076042a004104de007601fc0085045b00760606007604c0004f046d0076043b0024043c0005045e0015
01fcff9d043c000503e0007603b900760434003e01fc008501fcff9d03d50024045b00760446001f049e00090460007603b9007603e0007604e400760606007604de007604c0004f04d80076046d0076
0480004f043b0024045e00150446004204de00760480004f043c000505fe000a04e400760446001f059d0050055300120454005a04860094044b0053021a007800000001000004e4090b040000020202
0306050706020303040502030304050505050505050505050202050505040806060606050506060305060508060606060605050606080605050204020404030505050505030505020205020805050505
0305030504070504050302030602050506050206040704040507040305030303050403020304040707070408050605060805050505060205050306050908020603060506060205040404040203030404
0300000000000003050305060606050605070606050505050505050503050506050505050507070705050707060a0a070606070905060606070706090907080606080605050406070505050507050504
070505070806070505070505050808050508070508070605080708070a090504060506050605080708070605060000000000000506050504050506050706090609080706080606050607050605060506
0505050406080807060505090709070605060606040509050903020205020201000303060704020202020304030505030406020903030403040507080a08070507050506060704090606070808070506
0505050902050505050503030206050508080608000909050502020404040505050504020505050504040505020405040705050505050505050507050505030303030303030303030403050506060506
0505050504050505040505060606060508080605050607050605050506050708060705050705050705060607050507050507050505050409060505050405050505050606060707050504050503030303
0303030505070506020202020205020306060505030606060606060606050505050303030306060606060606060606050505050505050505050505050202020205050505050505050505040406050605
0605060506050605060506060505050505050505050506050605060506050605030203020302030203080505020605050205020503050306050605060505060506050605060306030603050505050505
0505050505030503050306050605060506050605060508070504050505050505050808060505050505050505050505050404040402020202050505050505050505050505050505050505050404040404
0505050505020202020204050404040405050505050505050505050505050505050505050505070505050505060607030606060306060505060306080606060505060305050505030505050504050305
0505070505050303050606060606050506080606060606050605050505050504050504050202020504080708070807050402030502020808060505060505060706050a0b050506050505030907030605
0605080805050605050907050506050605060506050605060506040604060406050807060505060506050605060506050605060506050605060506050605060505050505050505050505050505050505
0302030206050605060506050605060506050605060506050605060506050605060606060606060606060504050405040505060506050504060506050605050409070705050606050406050605060508
0706050505060505050705050505050506050605060505050302060506030605050605060506050605060506050502080806050605060305050503060406040807050507050506030505060505040505
020507050505050502050404050202040505050504040607050505050505050505050507060506060505050200000003000000030000001c000300010000001c0003000a000006880004066c000000ea
00800006006a00000002000d007e00a000ac00ad00bf00c600cf00e600ef00fe010f01110125012701300153015f0167017e017f018f019201a101b001f001ff021b0237025902bc02c702c902dd02f3
030103030309030f0323038a038c039203a103b003b903c903ce03d203d60425042f0445044f0462046f0479048604ce04d704e104f50501051005131e011e3f1e851ef11ef31ef91f4d200b20112015
201e2022202720302033203a203c20442074207f20a420aa20ac20b120ba20bd21052113211621222126212e215e22022206220f2212221a221e222b22482260226525caee02f6c3fb04fefffffdffff
000000000002000d002000a000a100ad00ae00c000c700d000e700f000ff01100112012601280131015401600168017f018f019201a001af01f001fa02180237025902bc02c602c902d802f303000303
0309030f03230384038c038e039303a303b103ba03ca03d103d60400042604300446045004630470047a048804cf04d804e204f6050205111e001e3e1e801ea01ef21ef41f4d20002010201320172020
2025203020322039203c20442074207f20a320a620ab20b120b920bc21052113211621222126212e215b22022206220f2211221a221e222b22482260226425caee01f6c3fb01fefffffcffff00010000
fff6ffe401a4ffc20198ffc10000018b0000018600000182000001800000017e0000017600000178ff15ff06ff04fef7feea01ba00000000fe64fe4300effdd7fdd6fdc8fdb3fda7fda6fda1fd9cfd89
0000ffcaffc900000000fd090000ffaafcfdfcfa0000fcb90000fcb10000fca60000fca00000fef40000fef10000fc490000e5aee56ee51fe54ee4b3e54ce55ce15be1570000e154e153e151e149e375
e141e36de138e109e0ff0000e0da0000e0d5e0cee0cde086e079e077e06cdf93e061e035df92deabdf86df85df7edf7bdf6fdf53df3cdf39dbd5139f0adf06a302ab01af000100000000000000000000
00000000000000da000000e40000010e0000012800000128000001280000016a0000000000000000000000000000016a0174000000000000000000000000000000000000000000000000016200000000
016a01860000019e00000000000001b6000001fe000002260000024800000258000002e2000002f20000030600000000000000000000000000000000000002f800000000000000000000000000000000
0000000002e8000002e8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024b024c024d024e024f025000810247
025b025c025d025e025f02600082008302610262026302640265008400850266026702680269026a026b008600870276027702780279027a027b00880089027c027d027e027f0280008a02460446008b
0248008c02af02b002b102b202b302b4008d02b502b602b702b802b902ba02bb02bc008e008f02bd02be02bf02c002c102c202c30090009102c402c502c602c702c802c90092009302d802d902dc02dd
02de02df0249024a0251026c02f702f802f902fa02d602d702da02db00ad00ae035200af03530354035500b000b1035c035d035e00b2035f036000b30361036200b4036300b5036400b60365036600b7
036700b800b903680369036a036b036c036d036e036f00c30371037200c4037000c500c600c700c800c900ca00cb037300cc00cd03b0037900d1037a00d2037b037c037d037e00d300d400d5038003b1
038100d6038200d70383038400d8038500d900da00db0386037f00dc038703880389038a038b038c038d00dd00de038e038f00e900ea00eb00ec039000ed00ee00ef039100f000f100f200f3039200f4
0393039400f5039500f6039603b203970101039801020399039a039b039c010301040105039d03b3039e010601070108045c03b403b5011601170118011903b603b703b903b80127012804610462045b
0129012a012b012c012d045d045e012e012f0456045703ba03bb0448044901300131045f046001320133044a044b01340135013601370138013903bc03bd044c044d03be03bf0469046a044e044f013a
013b04500451013c013d013e045a013f01400458045903c003c103c2014101420467046801430144046304640452045304650466014503cd03cc03ce03cf03d003d103d2014601470454045503e703e8
0148014903e903ea046b046c014a03eb046d03ec03ed0169016a046f046e017f04470185000c000000000c4000000000000001040000000000000000000000010000000200000002000000020000000d
0000000d00000003000000200000007e00000004000000a0000000a000000244000000a1000000ac00000063000000ad000000ad00000245000000ae000000bf0000006f000000c0000000c50000024b
000000c6000000c600000081000000c7000000cf00000252000000d0000000d000000247000000d1000000d60000025b000000d7000000d800000082000000d9000000dd00000261000000de000000df
00000084000000e0000000e500000266000000e6000000e600000086000000e7000000ef0000026d000000f0000000f000000087000000f1000000f600000276000000f7000000f800000088000000f9
000000fd0000027c000000fe000000fe0000008a000000ff0000010f0000028100000110000001100000024600000111000001110000044600000112000001250000029200000126000001260000008b
0000012700000127000002480000012800000130000002a600000131000001310000008c0000013200000137000002af00000138000001380000008d0000013900000140000002b50000014100000142
0000008e0000014300000149000002bd0000014a0000014b000000900000014c00000151000002c4000001520000015300000092000001540000015f000002ca0000016000000161000002d800000162
00000165000002dc000001660000016700000249000001680000017e000002e00000017f0000017f000000940000018f0000018f00000095000001920000019200000096000001a0000001a100000097
000001af000001b000000099000001f0000001f0000003aa000001fa000001fa00000251000001fb000001fb0000026c000001fc000001ff000002f70000021800000219000002d60000021a0000021b
000002da00000237000002370000009b00000259000002590000009c000002bc000002bc000003ab000002c6000002c70000009d000002c9000002c90000009f000002d8000002dd000000a0000002f3
000002f3000000a60000030000000301000000a70000030300000303000000a90000030900000309000000aa0000030f0000030f000000ab0000032300000323000000ac0000038400000385000000ad
0000038600000386000003520000038700000387000000af000003880000038a000003530000038c0000038c000003560000038e00000392000003570000039300000394000000b00000039500000397
0000035c0000039800000398000000b2000003990000039a0000035f0000039b0000039b000000b30000039c0000039d000003610000039e0000039e000000b40000039f0000039f00000363000003a0
000003a0000000b5000003a1000003a100000364000003a3000003a3000000b6000003a4000003a500000365000003a6000003a6000000b7000003a7000003a700000367000003a8000003a9000000b8
000003aa000003b000000368000003b1000003b9000000ba000003ba000003ba0000036f000003bb000003bb000000c3000003bc000003bd00000371000003be000003be000000c4000003bf000003bf
00000370000003c0000003c6000000c5000003c7000003c700000373000003c8000003c9000000cc000003ca000003ce00000374000003d1000003d2000000ce000003d6000003d6000000d000000400
00000400000003b00000040100000401000003790000040200000402000000d100000403000004030000037a0000040400000404000000d200000405000004080000037b000004090000040b000000d3
0000040c0000040c000003800000040d0000040d000003b10000040e0000040e000003810000040f0000040f000000d60000041000000410000003820000041100000411000000d70000041200000413
000003830000041400000414000000d80000041500000415000003850000041600000418000000d90000041900000419000003860000041a0000041a0000037f0000041b0000041b000000dc0000041c
00000422000003870000042300000424000000dd00000425000004250000038e000004260000042f000000df00000430000004300000038f0000043100000434000000e9000004350000043500000390
0000043600000438000000ed0000043900000439000003910000043a0000043d000000f00000043e0000043e000003920000043f0000043f000000f40000044000000441000003930000044200000442
000000f50000044300000443000003950000044400000444000000f6000004450000044500000396000004460000044f000000f70000045000000450000003b200000451000004510000039700000452
0000045200000101000004530000045300000398000004540000045400000102000004550000045800000399000004590000045b000001030000045c0000045c0000039d0000045d0000045d000003b3
0000045e0000045e0000039e0000045f000004610000010600000462000004620000045c000004630000046f000001090000047000000471000003b40000047200000475000001160000047600000477
000003b60000047800000478000003b90000047900000479000003b80000047a000004860000011a0000048800000489000001270000048a0000048b000004610000048c0000048c0000045b0000048d
000004910000012900000492000004930000045d00000494000004950000012e0000049600000497000004560000049800000499000003ba0000049a0000049b000004480000049c0000049d00000130
0000049e0000049f0000045f000004a0000004a100000132000004a2000004a30000044a000004a4000004a900000134000004aa000004ab000003bc000004ac000004ad0000044c000004ae000004af
000003be000004b0000004b100000469000004b2000004b30000044e000004b4000004b50000013a000004b6000004b700000450000004b8000004ba0000013c000004bb000004bb0000045a000004bc
000004bd0000013f000004be000004bf00000458000004c0000004c2000003c0000004c3000004c400000141000004c5000004c600000467000004c7000004c800000143000004c9000004ca00000463
000004cb000004cc00000452000004cd000004ce00000465000004cf000004d7000003c3000004d8000004d800000145000004d9000004d9000003cd000004da000004da000003cc000004db000004df
000003ce000004e0000004e100000146000004e2000004f5000003d3000004f6000004f700000454000004f8000004f9000003e7000004fa000004fb00000148000004fc000004fd000003e9000004fe
000004ff0000046b00000500000005000000014a0000050100000501000003eb00000502000005100000014b00000511000005110000046d0000051200000513000003ec00001e0000001e01000003ae
00001e3e00001e3f000003ac00001e8000001e850000039f00001ea000001ef1000003ee00001ef200001ef3000003a500001ef400001ef90000044000001f4d00001f4d000004a9000020000000200b
0000015b00002010000020110000016700002013000020140000016900002015000020150000046f000020170000201e0000016b00002020000020220000017300002025000020270000017600002030
00002030000001790000203200002033000003a7000020390000203a0000017a0000203c0000203c000003a900002044000020440000017c00002074000020740000017d0000207f0000207f0000017e
000020a3000020a30000046e000020a4000020a40000017f000020a6000020aa00000180000020ab000020ab00000447000020ac000020ac00000185000020b1000020b100000186000020b9000020ba
00000187000020bc000020bd0000018900002105000021050000018b00002113000021130000018c00002116000021160000018d00002122000021220000018e0000212600002126000000b90000212e
0000212e0000018f0000215b0000215e000001900000220200002202000001940000220600002206000000b10000220f0000220f000001950000221100002212000001960000221a0000221a00000198
0000221e0000221e000001990000222b0000222b0000019a00002248000022480000019b00002260000022600000019c00002264000022650000019d000025ca000025ca0000019f0000ee010000ee02
000001a00000f6c30000f6c3000001a20000fb010000fb04000001a40000feff0000feff000001aa0000fffc0000fffd000001abb0002c4bb0095058b101018e59b801ff85b0841db109035f5e2db001
2c2020456944b001602db0022cb0012a212db0032c2046b003254652582359208a208a49648a204620686164b004254620686164525823658a592f20b00053586920b000545821b040591b6920b00054
5821b0406559593a2db0042c2046b00425465258238a592046206a6164b0042546206a61645258238a592ffd2db0052c4b20b0032650585158b080441bb04044591b21212045b0c05058b0c0441b2159
592db0062c2020456944b001602020457d691844b001602db0072cb0062a2db0082c4b20b003265358b0401bb000598a8a20b0032653582321b0808a8a1b8a235920b0032653582321b0c08a8a1b8a23
5920b0032653582321b801008a8a1b8a235920b0032653582321b801408a8a1b8a235920b003265358b0032545b8018050582321b8018023211bb003254523212321591b2159442db0092c4b53584544
1b2121592db00a2cb029452db00b2cb02a452db00c2cb1270188208a5358b94000040063b80800885458b9002903e870591bb0235358b02088b810005458b9002903e8705959592db00d2cb04088b820
005a58b12a00441bb9002a03e844592db00c2bb0002b00b2010d022b01b20e01022b01b70e3a30251b1000082b00b701382e241a1100082bb7024e4032231500082bb703483b2e211400082bb7044e40
32231500082bb70530281f160e00082bb70663513f2d1b00082bb7074034241a1100082bb7085b4a3a291900082bb70983644e3a2300082bb70a77624c362100082bb70b91775c3a2300082bb70c7660
4b361d00082bb70d2c241c140c00082b00b20f0d072bb00020457d691844b2b0130173b250130174b280130174b270130175b20f1f0173b26f1f0175002a00cc0091009e009100ec007200b2007d0056
005f004e0060010400c400000014fe600014029b0010ff39000dfe9700120321000b043a0014048d001005b000140618001506c00010025b001207040005000000000000000000600060006000600060
009a00c4014001bf025802f4030e033a0369039c03c103e303f904200437048b04b9050a057d05c10627068f06bc073a07a407b007bc07db0802082108870933097309dd0a300a790ab90aef0b4e0b8b
0ba60bd90c200c440c9d0cd90d330d7e0dde0e370ea50ecf0f0d0f3e0f8d0fd8100910411065107c10a110c810e31104118311e3123712941308135113cb140b1445149014d714f2155d15a615f41658
16b816f5176317ae17f41824187218bb18fc19341977198e19cf1a131a501ab21b151b761bd91bf81c931cc41d651de31def1e0c1ebc1ed21f111f541fa720192039208a20b620d6210b21392183218f
21a921c321dd224622aa22e8236323b4242024de255625ab261d267c26da26f52741278a27c7281e287928fd299929c92a2c2a922aff2b632bb72c112c422ca52cdc2d042d0c2d3b2d5e2d962dc22e05
2e3a2e7e2e9e2ebe2ec72ef52f272f432f5c2fa12fa92fcf2ffc307530a330e33111314d31c2321c328532f83368339b340f348d34e7353035a335d03628369836e93742379f37f53839387838e43936
39963a0e3a5e3ad33b343ba33c183c8c3cdd3d193d713dcd3e393eb83ef13f3a3f803fec4022406340a040e9414241a641f2426842e7434143a944134439448e44fb457945b24603464a469446ea4718
474447ce48044846488348c7491b497d49c74a384ab04b094b814bef4c634cd34d374d734dd24e314e984f1d4f9e4feb503950a55112518451f5527e530653a4543754a5550f555355995604566b572b
57e3585c58db5930598359b859d45a075a1d5a335b045b725bda5c315ca05ccc5cf55d4a5d955deb5e3d5e8d5ee25f415f8f5fed604360d2615c61a261e56237628662c9633863b76417646c64ca6525
658c65ee66486657666766b6671e67a56817688068e6694a69b56a1f6a836af06b4b6b9d6bef6c406cb66ce16ce16ce16ce16ce16ce16ce16ce16ce16ce16ce16ce16ce16ce96cf16cfb6d056d206d43
6d656d856da46db06dbc6dee6e2c6e8d6eb16ebd6ecd6ee66fb46fd06fec6fff7013705a70dc717e720a721672e6734b73c9747e74e4755e75b6762476c1772277b878167878789278ac78c678e0794b
797179a979bf79f37a857ac77b467b857b947ba37bdc7bef7c187c317c3d7ca07cf57d8e7e187e8f7f487f4880f88161818e820b823c825282c1831b836883d9842f847584bc850a852d856b85ef8644
868c86cc8702876087ba87d588008843886788b988f28946898f89ea8a428aab8ad58b0e8b3f8b898bd28c038c3b8c838cac8cfe8d718db38e128e6e8e9b8f1f8f7f8f958fe8909690ff916291ab91f1
9233927492ea935393c993f39428949b94ce9518954a958d95fb964c96af970c978597f8988898d89917996c99c29a3d9abb9af79b4f9b989bdb9c149c559c8d9ccb9d219d2d9d799def9e7e9ed19f13
9f949ff9a05fa0c1a150a15ca1ada1f9a247a288a2f7a35ca3baa430a4c2a547a5dea653a6b2a705a765a76da7b9a81ea881a8f2a96da9c0aa22aa6daac9ab2aab54abababd7ac2eac76ac8aac9eacb0
acc4acd6acedad01ad5fad85ae02ae66aeb8aec0aec8aed0aedbaee3af49af49af51afc1b031b092b0d4b137b14eb165b17cb18eb1a6b1b9b1c5b1d1b1e8b1ffb216b22eb245b25cb273b28bb29db2b4
b2cbb2e2b2f9b311b328b33ab351b369b380b397b3a9b3bfb3d5b3ecb404b410b41cb433b445b45bb472b488b49eb4b5b4cdb4deb4f5b507b51db52eb546b55db56fb585b59cb5aeb5c5b5dcb5edb604
b61bb685b727b739b74bb762b778b78fb7a6b7b8b7c9b7dbb7ebb802b813b82ab840b857b86eb8dbb972b989b99ab9b1b9c7b9deb9f4ba0bba22ba2eba40ba57ba69ba80ba92baa9bac0bad7baeebaf9
bb04bb1bbb27bb33bb4abb61bb6dbb79bb90bba7bbb3bbbfbbd4bbe9bbf5bc01bc18bc2abc36bc42bc59bc6abc7fbc96bca7bcbebcd5bcedbd05bd17bd29bd35bd41bd53bd64bd76bd88bd9fbdb5bdc1
bdcdbdd9bde5bdf7be08be14be20be2cbe38be4fbe5bbe72be88be9abeb0bec7bedebef1bf04bf1cbf2fbf8dbfefc006c01dc034c04ac062c079c090c0a7c0bec0d0c0e1c0f8c10ac121c138c168c198
c1a8c1bfc1d6c1ecc1fdc215c22dc239c245c25cc273c289c2a0c2b7c2cdc2e4c2fcc30ec325c337c34dc35ec376c38dc3a4c3bac3d2c3e9c3ffc416c47dc48fc4a5c4bcc4cdc4dec4f4c50ac521c58e
c5a4c5bac5d1c5e8c5f4c60ac61cc633c64ac655c66bc682c68ec6a4c6b0c6c5c6d1c6e8c6f4c70bc71cc733c746c758c764c775c787c79dc7a9c7bac7c6c7dcc7e8c7fec80fc826c839c84cc8adc8c4
c8dac8f1c908c91fc935c940c94cc958c964c970c97cc988c9a3c9abc9b3c9bbc9c3c9cbc9d3c9dbc9e3c9ebc9f3c9fbca03ca0bca13ca2bca43ca55ca67ca79ca8acaa4caaccab4cabccac4cacccae4
cafbcb0dcb1fcb31cb49cb60cbcecbd6cbeecbf6cbfecc15cc2ccc34cc3ccc44cc4ccc63cc6bcc73cc7bcc83cc8bcc93cc9bcca3ccabccb3cccaccd2ccdacd2ecd36cd3ecd55cd6ccd74cd7ccd94cd9c
cdb3cdc9cde0cdf7ce0ece25ce38ce4bce62ce73ce87cea6ceb2cec4cecccee3cef5cf01cf0dcf24cf3bcf52cf69cf71cf79cf91cfa9cfb5cfc1cfcdcfd9cfe5cff1cff9d001d009d020d037d03fd056
d06dd085d09cd0a4d0acd0c3d0d9d0f1d0f9d110d128d140d158d16fd186d19cd1b4d1ccd1e4d1fcd204d20cd224d23bd253d26ad27cd28dd2a5d2bcd2d4d2ecd304d31bd337d353d35fd36bd373d37f
d38bd397d3a3d3b5d3c7d3e0d3f2d40bd41dd430d442d455d467d477d486d499d4abd4bed4d0d4e3d4f5d508d51ad52ad53ad546d552d564d576d588d599d5b2d5c4d5ddd5efd602d614d627d639d649
d658d66ad67cd688d694d6a0d6acd6bed6d0d6e3d6f5d708d71ad72dd73fd752d764d774d783d78fd7a1d7add7bfd7cbd7ddd7e9d7fad806d812d81ed82ad83cd84ed860d872d884d896d8a8d8bad8cc
d8ddd8e9d8f5d901d90dd91fd931d943d954d9ced9e8d9f4da00da0cda18da24da30da3cda48da54da60da6cda78da84da90da9cdaa8dab4dac0dac8db2ddb92dbd0dc0fdc6ddcccdce7dd02dd0edd1a
dd26dd32dd3edd4add95dde4de3ede96de9edeaadeb4debcdec4deccded4dedcdee4def6df08df1fdf36df4edf66df7edf96dfaedfc6dfdedff6e00ee026e03ee056e062e06ee07ae086e092e09ee0aa
e0b6e0c2e0d4e0e6e0f2e0fee10ae116e122e12ee13ae146e158e16ae176e182e18ee19ae1a6e1b2e1c4e1d5e1e1e1ede1f9e205e211e21de229e235e241e24de259e265e271e27de285e28de295e29d
e2a5e2ade2b5e2bde2c5e2cde2d5e2dde2e5e2fde314e32be33de345e34de365e36de37fe395e39de3a5e3ade3b5e3cce3d4e3dce3e4e3ece3f4e3fce404e40ce499e50ae56be573e57fe591e5a2e5aa
e5b6e5c2e5cee5dae5e60000000500640000032805b0000300060009000c000f006fb20c1011111239b00c10b000d0b00c10b006d0b00c10b009d0b00c10b00dd000b0004558b0022f1bb1021f3e59b0
004558b0002f1bb1000f3e59b2040200111239b2050200111239b2070200111239b2080200111239b00adcb20c0200111239b20d0200111239b00210b00edc3031212111210311010111010321013501
210328fd3c02c436feeefeba010ce40203fefe0102fdfd05b0faa40507fd7d0277fb110278fd5e025e88025e0002008ffff201a305b00003000d003bb2060e0f111239b00610b001d000b0004558b002
2f1bb1021f3e59b0004558b00c2f1bb10c0f3e59b2060d0a2b5821d81bf459b001d0b0012f30310123032101343632161514062226017ed1170100fef94a804a48844801ad0403fac3394b4b39374a4a
0002006503f40240060000040009002500b0004558b0032f1bb103213e59b002d0b0022fb007d0b0072fb00310b008d0b0082f3031010323113305032311330113238bae012d238bae0577fe7d020c89
fe7d020c00020060000004bc05b0001b001f008d00b0004558b00c2f1bb10c1f3e59b0004558b0102f1bb1101f3e59b0004558b0022f1bb1020f3e59b0004558b01a2f1bb11a0f3e59b21d0c02111239
b01d2fb200030a2b5821d81bf459b004d0b01d10b006d0b01d10b00bd0b00b2fb208030a2b5821d81bf459b00b10b00ed0b00b10b012d0b00810b014d0b01d10b016d0b00010b018d0b00810b01ed030
31012303231323352113233521133303331333033315230333152303230333132302cfe04ca84ce701053af301114ea74ee14ea74ed0ee3addfb4ca776e03ae0019afe66019a9e01399f01a0fe6001a0
fe609ffec79efe660238013900010064ff2d0426069b002c007db22a2d2e11123900b0004558b00c2f1bb10c1f3e59b0004558b0092f1bb1091f3e59b0004558b0232f1bb1230f3e59b0004558b0202f
1bb1200f3e59b2190c20111239b01910b202010a2b5821d81bf459b20f0923111239b00c10b213010a2b5821d81bf459b2272309111239b02310b22a010a2b5821d81bf4593031013426262726353436
37353315161615233426232206151416041e021514060715233526263533141633323603336cfc46e9caada0aebef27161606c6b0100926436cfb99fc6d5f37f747277017c556f59267df5a6d614dadc
19f5c47e91686157695e5067865aa9d213c3c216f0c67e8a6e0000050063ffec058905c5000d001a0027003500390089b2053a3b111239b00510b013d0b00510b01bd0b00510b028d0b00510b036d000
b0362fb0382fb0004558b0032f1bb1031f3e59b0004558b0252f1bb1250f3e59b00310b00ad0b00a2fb211020a2b5821d81bf459b00310b218020a2b5821d81bf459b02510b01ed0b01e2fb02510b22b
020a2b5821d81bf459b01e10b232020a2b5821d81bf459303113343633321615151406232226351714163332363535342622061501343633321615151406202635171416333236353534262322061505
27011763aa8a8ca9a98a87afaa4d3f3e4c4d7e4b0212ae8788ada7fee8abaa4f3e40494e3d3e4dfe027d02c77d049884a9a9894883a8a58c06455555494945565747fcd086a6a68d4782a9a789054457
534b4b4654544af44804724800030056ffec051105c4001c002500310098b22e3233111239b02e10b010d0b02e10b01ed000b0004558b0092f1bb1091f3e59b0004558b01b2f1bb11b0f3e59b0004558
b0182f1bb1180f3e59b2201b09111239b228091b111239b2032028111239b2102820111239b2131b09111239b2111318111239b2191813111239b2161119111239b01b10b21d010a2b5821d81bf459b2
1f1d11111239b00910b22f010a2b5821d81bf45930311334363726263534363332161514060707013635331007172127062024053237010706151416031417373736353426232206566ea25543d0b09f
cb5c696301193dd37ed6fee6529cfe50fefd01e27b6bfec21f788219676f1f3e56424754018965a9746b9646abc7bb8a5b994c48feb47893fef3acfd6175e523520177165b75657e03aa547f4c193756
395160000001005203fc010b06000004001600b0004558b0032f1bb103213e59b002d0b0022f30310103231133010b1a9fb90583fe79020400010080fe3102a2065f00100010b207111211123900b004
2fb00d2f30311334121237170602030710121707260202807cf086308daf0801ab9a3086f17b0250e7019f0147428e6bfe49fee556fed1fe257c87420149019d00010028fe310251065f00120010b207
131411123900b0042fb00e2f30310114020207273612113510022727371612121702517af8873096af988e1f3080f080080240defe63fead41877401dd013217011601c98a1c883efec4fe79d0000001
001b024d037405b0000e002000b0004558b0042f1bb1041f3e59b000d019b0002f18b009d019b0092f183031012537050333032517051307030327014cfecf37012e0fb30f012936fecac891b4b29203
cc58a9750158fea273ac58fef66a0120fee96600000100440092042a04b6000b001a00b0092fb000d0b00910b206010a2b5821d81bf459b003d0303101211521112311213521113302ae017cfe84ecfe
82017eec0321defe4f01b1de01950001001cfeb8015d00eb00090018b2090a0b11123900b00a2fb2050d0a2b5821d81bf4593031132736363735330706069f833a2b01db010169feb84e5b8746bdaf6a
d500000100470209025402cd0003001100b0022fb201010a2b5821d81bf4593031012135210254fdf3020d0209c400010087fff501a20100000a0022b2000b0c11123900b0004558b0062f1bb1060f3e
59b2000d0a2b5821d81bf459303101321615140623222634360114444a4a44414c4a01004d3a394b4a744d0000010002ff8302fe05b00003001300b0002fb0004558b0022f1bb1021f3e593031172301
33c1bf023dbf7d062d0000020069ffec042205c4000d001b0046b2031c1d111239b00310b011d000b0004558b00a2f1bb10a1f3e59b0004558b0032f1bb1030f3e59b00a10b211010a2b5821d81bf459
b00310b218010a2b5821d81bf4593031011002232202033510123332121327342623220607111416333236370422ebf0ecef03ebf1efeb03f3707a777003727a7570030265fec6fec101370131fc013a
013afecefecf14cdbfb5c0feb6ccc8b9c500000100a8000002ff05b50006003900b0004558b0052f1bb1051f3e59b0004558b0002f1bb1000f3e59b2040005111239b0042fb203010a2b5821d81bf459
b202030511123930312123110535253302fff2fe9b02381f04917acdd100000100510000044005c40019004eb2111a1b11123900b0004558b0112f1bb1111f3e59b0004558b0002f1bb1000f3e59b203
1100111239b01110b209010a2b5821d81bf459b2161100111239b00010b218010a2b5821d81bf459303121213501363635342623220615233436363332161514060701210440fc2d01e5695975637682
f379e193d4f57b8cfe9c02a4a70211759d4f6880907d85d576d5bc6def98fe830001004fffec041505c40029006eb2072a2b11123900b0004558b00f2f1bb10f1f3e59b0004558b01b2f1bb11b0f3e59
b2010f1b111239b0012fb21f010171b29f01015db23f010171b00f10b207010a2b5821d81bf459b00110b228010a2b5821d81bf459b2152801111239b01b10b222010a2b5821d81bf459303101333636
353426232206152334363633321615140607161615140423222435331416333236353426232301869470836d70627ef377d584daf97d63787dfef3dbd2fef4f3816d718288868f034701726c6873715b
70b867dbc362ad2c29b07ac4e8e0ba60787872737c00000200340000045805b0000a000e004900b0004558b0092f1bb1091f3e59b0004558b0042f1bb1040f3e59b2010904111239b0012fb202010a2b
5821d81bf459b006d0b00110b00bd0b208060b111239b20d0904111239303101331523112311212701330121110703a3b5b5f3fd8b070274fbfd90017d120207c3febc01449403d8fc57026020000001
0081ffec043a05b0001d006ab21a1e1f11123900b0004558b0012f1bb1011f3e59b0004558b00d2f1bb10d0f3e59b00110b203010a2b5821d81bf459b207010d111239b0072fb21a010a2b5821d81bf4
59b205071a111239b00d10b214010a2b5821d81bf459b211141a111239b21d1a141112393031131321152103363332121514002322242733161633323635342623220607ae4f030efdbc28657fd0e7ff
00dfc8fef90beb0e7c64707d8a79425c3602d202ded2fea43afef6e1defef9e3ba6a71a08a859b23330000020075ffec043705b70014001f0062b2152021111239b01510b00dd000b0004558b0002f1b
b1001f3e59b0004558b00d2f1bb10d0f3e59b00010b201010a2b5821d81bf459b207000d111239b0072fb205070d111239b215010a2b5821d81bf459b00d10b21b010a2b5821d81bf459303101152306
0607363332121514002322001135100021032206071514163236102603611eccf41775b6c1dffefbd4dafef10175015eec50851f88d87e8005b7c903dac87bfef0d7defeed0142010553017f01b2fd49
5a4b4aa2bfa20108a600000100450000043605b00006003200b0004558b0052f1bb1051f3e59b0004558b0012f1bb1010f3e59b00510b203010a2b5821d81bf459b20003051112393031010123012135
210436fdbaff0245fd0f03f10529fad704edc30000030068ffec042205c400170021002b0074b2092c2d111239b00910b01ad0b00910b024d000b0004558b0152f1bb1151f3e59b0004558b0092f1bb1
090f3e59b2290915111239b0292fb21f290171b21a010a2b5821d81bf459b2031a29111239b20f291a111239b00910b21f010a2b5821d81bf459b01510b225010a2b5821d81bf4593031011406071616
151404232224353436372626353436333216033426220615141632360334262206151416323604026e5f727bfefcd8d9fefb7c705e6df0cccdf0d381d47f7ddc7b1f6eba6c6dba6d04306ba73035b874
c0e1e2bf75ba3230a76bbadadafcaf6c85846d6b807c02fd5f7b7565647676000002005dfffa041205c4001500210064b2092223111239b00910b016d000b0004558b0092f1bb1091f3e59b0004558b0
112f1bb1110f3e59b21611091112397cb0162f18b202010a2b5821d81bf459b2000209111239b01110b212010a2b5821d81bf459b00910b21d010a2b5821d81bf4593031010623220235343636333200
11151000052335333636033236373534262206151416031e7aa3c0e474d68ddc0102fe9cfe9f1d23d7e6dc49802384d27d7e026181010ddb90ea82feb8feed44fe76fe6203c903c9010f544a5fa1c4ad
8489a800ffff0082fff5019d045100260012fb0000070012fffb0351ffff002efeb80188045100270012ffe603510006001012000001003f00a40384044e00060017b200070811123900b0004558b005
2f1bb1051b3e593031010515013501150136024efcbb03450277e0f30175c10174f300020091016403ef03d600030007002500b0072fb003d0b0032fb200010a2b5821d81bf459b00710b204010a2b58
21d81bf4593031012135211121352103effca2035efca2035e030ccafd8ec9000001008000a503e0044e00060017b200070811123900b0004558b0022f1bb1021b3e5930310125350115013502eafd96
0360fca0027ce3effe8cc1fe8cef0002003cfff4039805c400180023005eb2092425111239b00910b01cd000b0004558b0102f1bb1101f3e59b0004558b0222f1bb1220f3e59b21c0d0a2b5821d81bf4
59b000d0b0002fb2040010111239b01010b209010a2b5821d81bf459b20c1000111239b21500101112393031013436363736353426232206152336363332161514070706070334363332161514062226
015e42c31a285d5a5669f302edc3c9e1987b4202f44a3f404a48844701ac859ebd283d475e636153b1ceccb7a39e794b90fec93b494b39374a4a0002005bfe3b06d9059000360042007cb23b43441112
39b03b10b023d000b02a2fb0332fb0004558b0032f1bb1030f3e59b0004558b0082f1bb1080f3e59b2053308111239b20f3308111239b00f2fb00810b23a020a2b5821d81bf459b015d0b03310b21b02
0a2b5821d81bf459b02a10b223020a2b5821d81bf459b00f10b240020a2b5821d81bf4593031010602232227060623222637361236333216170306333236371200212204020706120433323637170606
2322242726131212243332041201061633323637132623220606cd0cdebeb53d33874a929712107fc36e54815734138566830611fec1fec0c4fed1b2090c8b011fcf54b740263dcf69fefe945b5e0b0c
de0181f6f90167b2fc030d4a5136601e2d322f6f8c0206fafedf9a4c4cf0c9a301068f2a42fdcdc6dbae01710188c4fe8dedf1fea3b62822892831d7ccd30126011201b5f2dbfe65fe8c888d5f5301ed
13d1000200120000054205b00007000a004600b0004558b0042f1bb1041f3e59b0004558b0022f1bb1020f3e59b0004558b0062f1bb1060f3e59b2090402111239b0092fb200010a2b5821d81bf459b2
0a04021112393031012103210133012101210303c3fdcc76fef90226e30227fef8fd9c01a6d30153fead05b0fa50021f025c00030094000004a305b0000e0016001f006db2022021111239b00210b011
d0b00210b01ed000b0004558b0012f1bb1011f3e59b0004558b0002f1bb1000f3e59b2170001111239b0172fb21f170171b20f010a2b5821d81bf459b2080f17111239b00010b210010a2b5821d81bf4
59b00110b21e010a2b5821d81bf459303133112132041514060716161514042301112132363534272533323635342623239401f3f701026c687681fef9f5feea01197786e8fed2f876857b82f605b0c6
c464a02c20b17ccddc0291fe397669e305ba6b626c6000010066ffec04eb05c4001d0040b2031e1f11123900b0004558b00c2f1bb10c1f3e59b0004558b0032f1bb1030f3e59b00c10b213010a2b5821
d81bf459b00310b21a010a2b5821d81bf459303101060023222402273534122433320017232626232206071514163332363704eb16fed4f9aefef79003920111b3f1012618fc12938ea5b102a9a39596
1401dae9fefba50130c988ce013aaafefaef9d8bf1e981ecf8869c0000020094000004d205b0000b00150046b2021617111239b00210b015d000b0004558b0012f1bb1011f3e59b0004558b0002f1bb1
000f3e59b00110b20c010a2b5821d81bf459b00010b20d010a2b5821d81bf4593031331121320412151514020423031133323637353426239401aec1012ba4a5fecfc5a6a5c7d502cec405b0acfec4cc
49cffec6aa04e4fbe6f9e951edfa000100940000044c05b0000b004e00b0004558b0062f1bb1061f3e59b0004558b0042f1bb1040f3e59b20b0604111239b00b2fb200010a2b5821d81bf459b00410b2
02010a2b5821d81bf459b00610b208010a2b5821d81bf459303101211121152111211521112103e7fdaa02bbfc4803b1fd4c0256028afe40ca05b0ccfe6e000100940000043105b00009004000b00045
58b0042f1bb1041f3e59b0004558b0022f1bb1020f3e59b2090402111239b0092fb200010a2b5821d81bf459b00410b206010a2b5821d81bf45930310121112311211521112103dbfdb6fd039dfd6002
4a0269fd9705b0ccfe4f0001006affec04f005c4001e0055b20b1f2011123900b0004558b00b2f1bb10b1f3e59b0004558b0032f1bb1030f3e59b00b10b211010a2b5821d81bf459b00310b218010a2b
5821d81bf459b21e0b03111239b01e2fb21b010a2b5821d81bf45930312506042322240227351000213204172302212206071514123332371121352104f04ffee8b2b7fee69903013c011bf3011e1df8
2afef9aab103c7b1c252fed40228bd676aa60135ce72014a0173f0e20107f5ed70ecfefb58011dc0000100940000051805b0000b004c00b0004558b0062f1bb1061f3e59b0004558b00a2f1bb10a1f3e
59b0004558b0002f1bb1000f3e59b0004558b0042f1bb1040f3e59b2090600111239b0092fb202010a2b5821d81bf45930312123112111231133112111330518fcfd75fdfd028bfc0287fd7905b0fda2
025e000100a30000019f05b00003001d00b0004558b0022f1bb1021f3e59b0004558b0002f1bb1000f3e59303121231133019ffcfc05b0000001002dffec03e405b0000f002fb205101111123900b000
4558b0002f1bb1001f3e59b0004558b0052f1bb1050f3e59b20c010a2b5821d81bf45930310133111404232226353314163332363502e8fcfefbd6e4f8fc736d667905b0fc03d1f6e6cd747587770001
00940000051805b0000c005300b0004558b0042f1bb1041f3e59b0004558b0082f1bb1081f3e59b0004558b0022f1bb1020f3e59b0004558b00b2f1bb10b0f3e59b2000402111239b46a007a00025db2
060402111239b465067506025d3031010711231133113701210101210236a5fdfd8c01aa0132fde3023cfed40275affe3a05b0fd55ad01fefd7bfcd5000100940000042605b00005002800b0004558b0
042f1bb1041f3e59b0004558b0022f1bb1020f3e59b200010a2b5821d81bf459303125211521113301910295fc6efdcaca05b000000100940000066a05b0000e006e00b0004558b0002f1bb1001f3e59
b0004558b0022f1bb1021f3e59b0004558b0042f1bb1040f3e59b0004558b0082f1bb1080f3e59b0004558b00c2f1bb10c0f3e59b2010004111239b465017501025db2070004111239b46a077a07025d
b20a0004111239b46a0a7a0a025d3031090221112311130123011311231101dc01a401a30147fc19fe52b5fe5319fc05b0fba4045cfa5001e00282fb9e0461fd7ffe2005b000000100940000051705b0
0009004cb2010a0b11123900b0004558b0052f1bb1051f3e59b0004558b0082f1bb1081f3e59b0004558b0002f1bb1000f3e59b0004558b0032f1bb1030f3e59b2020500111239b20705001112393031
212301112311330111330517fdfd77fdfd028bfb0409fbf705b0fbf3040d00020066ffec051e05c40010001e0046b2041f20111239b00410b014d000b0004558b00c2f1bb10c1f3e59b0004558b0042f
1bb1040f3e59b00c10b214010a2b5821d81bf459b00410b21b010a2b5821d81bf459303101140204232224022735341224200412170734022322020715141233321235051e94feedb3b1feeb97019701
13016401139601fdb7a8a4b902bba6a8b502b2d6febdadad0140d152d50146adabfebfd505f20102feffeb54f0fefa0100f600020094000004d405b0000a0013004db20a1415111239b00a10b00cd000
b0004558b0032f1bb1031f3e59b0004558b0012f1bb1010f3e59b20b0103111239b00b2fb200010a2b5821d81bf459b00310b213010a2b5821d81bf45930310111231121320415140423252132363534
2627210191fd022df4011ffee7fdfed30130878e907efec9021dfde305b0fed1d6eecb7f78768d0200020060ff04051a05c4001500230046b2082425111239b00810b020d000b0004558b0112f1bb111
1f3e59b0004558b0082f1bb1080f3e59b01110b219010a2b5821d81bf459b00810b220010a2b5821d81bf459303101140207170725062322240227353412243332041217073426232202071514123332
123505198376faa4feca3d46b0feeb9701970113b1b401139601feb8a8a3b902b9a7a9b502b2cffed159c394f50dad0140d152d50146adabfebfd505f6fefeffea55ecfef60100f600020094000004de
05b0000e0017005ab2051819111239b00510b010d000b0004558b0042f1bb1041f3e59b0004558b0022f1bb1020f3e59b20f0204111239b00f2fb201010a2b5821d81bf459b20b010f111239b00210b0
0ed0b00410b217010a2b5821d81bf459303101211123112132041514060701152101213236353426272102abfee6fd0200fc01128d7e0147fef1fdc2010480908584fef50231fdcf05b0e2d692c535fd
a10d02fc8170758002000001004affec048a05c400270063b211282911123900b0004558b0092f1bb1091f3e59b0004558b01d2f1bb11d0f3e59b2021d09111239b20e091d111239b00910b211010a2b
5821d81bf459b00210b217010a2b5821d81bf459b2221d09111239b01d10b225010a2b5821d81bf459303101342624272635342433321616152334262322061514160416161514042322242635331416
333236038d87fea068c7011fe598ee88fc8f857c89940154ce60fee9ef9efef793fda4998485017760686a417dc9b0e470cf7e72816a5f506b6581a770b6d775ce897c886b000001002d000004b005b0
0007002e00b0004558b0062f1bb1061f3e59b0004558b0022f1bb1020f3e59b00610b200010a2b5821d81bf459b004d03031012111231121352104b0fe3afbfe3e048304e4fb1c04e4cc0001007dffec
04bd05b00010003cb204111211123900b0004558b0092f1bb1091f3e59b0004558b0102f1bb1101f3e59b0004558b0042f1bb1040f3e59b20d010a2b5821d81bf4593031011114002322003511331114
163320111104bdfed7f7fafedafc9490012405b0fc33e8fef1010bed03ccfc32929a013403c6000100120000051d05b000060038b200070811123900b0004558b0012f1bb1011f3e59b0004558b0052f
1bb1051f3e59b0004558b0032f1bb1030f3e59b2000103111239303101012101230121029501720116fdf4f5fdf60115013d0473fa5005b000010030000006e505b0000c0060b2050d0e11123900b000
4558b0012f1bb1011f3e59b0004558b0082f1bb1081f3e59b0004558b00b2f1bb10b1f3e59b0004558b0032f1bb1030f3e59b0004558b0062f1bb1060f3e59b2000103111239b2050103111239b20a01
03111239303101133301230101230133130133050ae0fbfeb0f2feebfee5f3feb0fbe20116d401680448fa500427fbd905b0fbba044600010029000004e905b0000b005300b0004558b0012f1bb1011f
3e59b0004558b00a2f1bb10a1f3e59b0004558b0042f1bb1040f3e59b0004558b0072f1bb1070f3e59b2000104111239b2060104111239b2030006111239b20906001112393031010121010121010121
010121028901320124fe4801c2fed9fec7fec6feda01c3fe47012403a2020efd2efd220216fdea02de02d20000010007000004d605b00008003100b0004558b0012f1bb1011f3e59b0004558b0072f1b
b1071f3e59b0004558b0042f1bb1040f3e59b20001041112393031010121011123110121026f014f0118fe18fefe17011902fe02b2fc68fde80218039800000100500000048c05b00009004400b00045
58b0072f1bb1071f3e59b0004558b0022f1bb1020f3e59b200010a2b5821d81bf459b2040002111239b00710b205010a2b5821d81bf459b20905071112393031252115213501213521150182030afbc4
02f1fd14041fcacaa40440cca00000010084febc021c068e0007002200b0042fb0072fb200010a2b5821d81bf459b00410b203010a2b5821d81bf45930310123113315211121021ca5a5fe68019805d0
f9a9bd07d20000010014ff83036405b00003001300b0022fb0004558b0002f1bb1001f3e5930311333012314f00260f005b0f9d30001000cfebc01a6068e0007002500b0022fb0012fb00210b205010a
2b5821d81bf459b00110b206010a2b5821d81bf459303113211121353311230c019afe66a7a7068ef82ebd0657000001003502d9033505b000060027b200070811123900b0004558b0032f1bb1031f3e
59b000d0b2010703111239b0012fb005d030310103230133012301b5b2ce012bab012acd04a6fe3302d7fd2900010003ff41039800000003001b00b0004558b0032f1bb1030f3e59b200010a2b5821d8
1bf4593031052135210398fc6b0395bfbf000001003104d1020906000003002400b0012fb20f01015db003d0b0032fb40f031f03025db200010311123919b0002f183031012301210209cafef2011504
d1012f000002005affec03fb044e001e00290085b2172a2b111239b01710b020d000b0004558b0172f1bb1171b3e59b0004558b0042f1bb1040f3e59b0004558b0002f1bb1000f3e59b2021704111239
b20b1704111239b00b2fb01710b20f010a2b5821d81bf459b2120b0f11123940090c121c122c123c12045db00410b21f010a2b5821d81bf459b00b10b223070a2b5821d81bf459303121262706232226
35342433333534262322061523343636333216171114171525323637352322061514160303100c74a8a3ce0101ef955e60536af376cb7dbee20329fdfd487f208387885d1f4679ba89adb94754655340
599b58bfadfe18925711af463bcc5e5646530002007cffec04320600000f001b0064b2131c1d111239b01310b00cd000b0092fb0004558b00c2f1bb10c1b3e59b0004558b0032f1bb1030f3e59b00045
58b0062f1bb1060f3e59b2050c03111239b20a0c03111239b00c10b213010a2b5821d81bf459b00310b218010a2b5821d81bf45930310114022322270723113311363332121127342623220711163332
36370432e1c5be6a0cdcf369b2c6e2f37c769e40419f727c020212fcfed689750600fdd27cfedafef807b0b08afe428daaac0001004fffec03f5044e001c004bb2001d1e11123900b0004558b00f2f1b
b10f1b3e59b0004558b0082f1bb1080f3e59b200010a2b5821d81bf459b203080f111239b2130f08111239b00f10b216010a2b5821d81bf459303125323637330e022322001135340033321617232626
2322060715141602395b7804e50476ca75e3fef60108e4c1f306e504775c7680017fae6a4e65af660126010319f70129e1b75d78abae27b0ad000002004fffec04030600000e00190064b2171a1b1112
39b01710b003d000b0062fb0004558b0032f1bb1031b3e59b0004558b00c2f1bb10c0f3e59b0004558b0082f1bb1080f3e59b205030c111239b20a030c111239b00c10b212010a2b5821d81bf459b003
10b217010a2b5821d81bf459303113341233321711331123270623220237141633323711262322064fe8c3ac6af3dc0c6db6beebf37f759545439576800225fa012f78022afa0070840132f2a5b98501
ce82bb0000020053ffec040b044e0015001d0083b2161e1f111239b01610b008d000b0004558b0082f1bb1081b3e59b0004558b0002f1bb1000f3e59b21a0008111239b01a2fb4bf1acf1a025db45f1a
6f1a0271b41f1a2f1a0271b4ef1aff1a0271b28c1a015db20c070a2b5821d81bf459b00010b210010a2b5821d81bf459b2120800111239b00810b216010a2b5821d81bf4593031052200353534363633
3212111521161633323717060603220607213526260259e7fee17de28bddf1fd3d0b9d77a7698341d9a4647b1101cf0872140123f21ea2ff8efee6fefe62869c877d616b039f8c7d127a7d000001002d
000002d6061500140053b207151611123900b0004558b0082f1bb108213e59b0004558b0042f1bb1041b3e59b0004558b0002f1bb1000f3e59b00410b010d0b213010a2b5821d81bf459b001d0b00810
b20d010a2b5821d81bf4593031331123353335343633321707262322151533152311d2a5a5c8b44048062835aedcdc0386b463b4c412be08b360b4fc7a0000020052fe56040c044e001900240083b222
2526111239b02210b00bd000b0004558b0032f1bb1031b3e59b0004558b0062f1bb1061b3e59b0004558b00b2f1bb10b113e59b0004558b0172f1bb1170f3e59b2050317111239b00b10b211010a2b58
21d81bf459b20f1117111239b2150317111239b01710b21d010a2b5821d81bf459b00310b222010a2b5821d81bf459303113341233321737331114042322262737163332363535062322023714163332
37112623220652edc4b96a0bdbfef7e177e33b7370a4798c69afbef1f285769347459378850225fc012d816dfbe7d5f663509285837f4975012ef6a3bb7e01dc7bbe00010079000003f8060000100042
b20a111211123900b0102fb0004558b0022f1bb1021b3e59b0004558b00d2f1bb10d0f3e59b0004558b0062f1bb1060f3e59b00210b20a010a2b5821d81bf45930310136332013112311342623220711
231133016c77b6015a05f3615e9248f3f303c48afe75fd3d02ba705d82fcfb0600000002007d0000019005d50003000d003eb2060e0f111239b00610b001d000b0004558b0022f1bb1021b3e59b00045
58b0012f1bb1010f3e59b00210b00cd0b00c2fb2060d0a2b5821d81bf45930312123113301343632161514062226017ff3f3fefe478448488447043a0119384a4a38374949000002ffb5fe4b018505d5
000c00160049b2031718111239b00310b010d000b0004558b00c2f1bb10c1b3e59b0004558b0042f1bb104113e59b209010a2b5821d81bf459b00c10b015d0b0152fb20f0d0a2b5821d81bf459303101
11140623222735163332371103343632161514062226017aa59f433e2630790315478448488447043afb66a6af11c0098404a30119384a4a383749490001007d000004360600000c005300b0004558b0
042f1bb104213e59b0004558b0082f1bb1081b3e59b0004558b0022f1bb1020f3e59b0004558b00b2f1bb10b0f3e59b2000802111239b46a007a00025db2060802111239b465067506025d3031010711
2311331137012101012101dc6cf3f34c012b0124fe6e01bdfee701d06ffe9f0600fc8a5f0151fe3dfd890001008c0000017f06000003001d00b0004558b0022f1bb102213e59b0004558b0002f1bb100
0f3e59303121231133017ff3f30600000001007c00000679044e001d0077b2041e1f11123900b0004558b0032f1bb1031b3e59b0004558b0072f1bb1071b3e59b0004558b0002f1bb1001b3e59b00045
58b01b2f1bb11b0f3e59b0004558b0152f1bb1150f3e59b0004558b00c2f1bb10c0f3e59b201031b111239b2050715111239b00710b210010a2b5821d81bf459b018d030310117363332173633321617
1123113426232206071323112623220711231101610772c6d95076d6b3af02f35a6853691501f305be923df3043a7185a6a6c6c1fd3902c067605948fd1a02c8bf77fcf0043a00010079000003f8044e
00100053b20b111211123900b0004558b0032f1bb1031b3e59b0004558b0002f1bb1001b3e59b0004558b00e2f1bb10e0f3e59b0004558b0072f1bb1070f3e59b2010e03111239b00310b20b010a2b58
21d81bf45930310117363320131123113426232207112311015e0778c3015206f359659348f3043a7d91fe7dfd3502bd676385fcfe043a000002004fffec043d044e000f001a0043b20c1b1c111239b0
0c10b018d000b0004558b0042f1bb1041b3e59b0004558b00c2f1bb10c0f3e59b212010a2b5821d81bf459b00410b218010a2b5821d81bf4593031133436363332001717140606232200351714163236
3534262322064f7ee494db01110b017be596e5feedf38af6898d79778c02279fff89fee6e939a0fc8a0131fe09a7bdc0b9a4c0bd0002007cfe600430044e000f001a006eb2131b1c111239b01310b00c
d000b0004558b00c2f1bb10c1b3e59b0004558b0092f1bb1091b3e59b0004558b0062f1bb106113e59b0004558b0032f1bb1030f3e59b2050c03111239b20a0c03111239b00c10b213010a2b5821d81b
f459b00310b218010a2b5821d81bf45930310114022322271123113317363332121127342623220711163332360430e4c0b26bf3e00a6bb8c6e1f281789541429674830212fbfed575fdff05da6e82fe
d9fefa06a2be7bfe207ebb000002004ffe600402044e000e0019006bb2171a1b111239b01710b003d000b0004558b0032f1bb1031b3e59b0004558b0062f1bb1061b3e59b0004558b0082f1bb108113e
59b0004558b00c2f1bb10c0f3e59b205030c111239b20a030c111239b212010a2b5821d81bf459b00310b217010a2b5821d81bf459303113341233321737331123110623220237141633323711262322
064fe8c6b56a0ed8f36aaac2eaf383749046468e74850226fe012a7f6bfa2601fc70012ff6a6bd7b01ec76ba0001007c000002b4044e000d0046b2090e0f11123900b0004558b0082f1bb1081b3e59b0
004558b00b2f1bb10b1b3e59b0004558b0052f1bb1050f3e59b00b10b202010a2b5821d81bf459b2090b051112393031012623220711231133173633321702b33033a73af3e806589c3422035c0880fd
1c043a798d0e0001004bffec03ca044e00260069b209272811123900b0004558b0092f1bb1091b3e59b0004558b01c2f1bb11c0f3e59b2021c09111239b00210b016d0b00910b210010a2b5821d81bf4
59b20d1610111239b40c0d1c0d025db01c10b224010a2b5821d81bf459b2212402111239b403211321025d30310134262627263534363332161523342623220615141604161615140623222626353316
1633323602db6bf853b6ecb6c2eff3685650655e011ea34ff2c485d074ec057863606401264144342858a78cbcc099465d4a3e383e3f577a5792b560a861565d490000010008ffec0272054100140052
b200151611123900b0004558b0132f1bb1131b3e59b0004558b00d2f1bb10d0f3e59b01310b001d0b000d0b0002fb00110b204010a2b5821d81bf459b00d10b208010a2b5821d81bf459b00410b010d0
303101113315231114163332371506232011112335331101adbfbf313f2a2b534dfee8b2b20541fef9b4fda43e370abc1701350265b4010700010077ffec03f7043a00100053b20a111211123900b000
4558b0072f1bb1071b3e59b0004558b00d2f1bb10d1b3e59b0004558b0022f1bb1020f3e59b0004558b00f2f1bb10f0f3e59b200020d111239b00210b20a010a2b5821d81bf459303125062322263511
33111433323711331123030c6bc5b0b5f3abb13ef3e56a7ecec302bdfd46ce7f0309fbc600010016000003da043a00060038b200070811123900b0004558b0012f1bb1011b3e59b0004558b0052f1bb1
051b3e59b0004558b0032f1bb1030f3e59b200050311123930310113330123013301fae5fbfe89d3fe86fc01340306fbc6043a0000010021000005cc043a000c0060b2050d0e11123900b0004558b001
2f1bb1011b3e59b0004558b0082f1bb1081b3e59b0004558b00b2f1bb10b1b3e59b0004558b0032f1bb1030f3e59b0004558b0062f1bb1060f3e59b2000b03111239b2050b03111239b20a0b03111239
3031011333012303032301331313330433acedfed9c8e8e4c8fed8edafdeb7014f02ebfbc602e7fd19043afd1d02e3000001001f000003e8043a000b005300b0004558b0012f1bb1011b3e59b0004558
b00a2f1bb10a1b3e59b0004558b0042f1bb1040f3e59b0004558b0072f1bb1070f3e59b2000a04111239b2060a04111239b2030006111239b209060011123930310113210101210303210101210201ce
010efeb50156fef4d8d7fef20156feb6010c02d60164fdebfddb0172fe8e022502150001000cfe4b03d6043a000f003fb200101111123900b0004558b00f2f1bb10f1b3e59b0004558b0052f1bb10511
3e59b200050f111239b00f10b001d0b00510b209010a2b5821d81bf45930310113210102232227351732363737012101f7dc0103fe5263ed35402e5c5d1b23fe840106015c02defb22feef12bc03434f
5d04350000010052000003c0043a0009004400b0004558b0072f1bb1071b3e59b0004558b0022f1bb1020f3e59b200010a2b5821d81bf459b2040002111239b00710b205010a2b5821d81bf459b20905
0711123930312521152135012135211501800240fc920225fde5034fc2c29f02d7c49a0000010038fe980291063d00170036b212181911123900b00c2fb0004558b0002f1bb100173e59b206000c1112
39b0062fb205070a2b5821d81bf459b212050611123930310124033534233532353536363717060715140716151516170261fe9f07c1c103b5b030ad06adad06adfe98630160d5e1b2e2d4b4de328c38
fad8e15b5ce3d5fa3800000100aefef2015505b00003001300b0002fb0004558b0022f1bb1021f3e593031012311330155a7a7fef206be000001001bfe980275063d00180036b205191a11123900b00b
2fb0004558b0182f1bb118173e59b211180b111239b0112fb212070a2b5821d81bf459b20512111112393031173637353437263535262737161615151433152215151406071bb004b6b604b030b6b2c2
c2b3b5db39ffd0e75656eacfff398c33e5b9c8e1b2e1c5bbe53300010075018304dc032f0017003fb211181911123900b00f2fb203180f111239b0032fb00f10b208010a2b5821d81bf459b00310b00b
d0b00310b214010a2b5821d81bf459b00f10b017d0303101140623222e022322061523343633321e023332363504dcbe8e4a7d9a4326434dc1b6944a8591432743540312b0df3889216854abdb3b8422
705400020086fe940199044d0003000f003eb2071011111239b00710b000d000b0004558b00d2f1bb10d1b3e59b0004558b0032f1bb103173e59b00d10b2070d0a2b5821d81bf459b000d0b0002f3031
13331321011406232226353436333216aad118feff010748414248484241480296fbfe0537384b4b38374b4b00010064ff0b040a05260020005db21b212211123900b0004558b0112f1bb1111b3e59b0
004558b00a2f1bb10a0f3e59b200010a2b5821d81bf459b2030a11111239b00a10b007d0b0072fb01110b014d0b0142fb218110a111239b01110b21b010a2b5821d81bf4593031253236373306060715
233526023535341237353315161617232626232203071416024f597806e404c592c8b7ccccb7c89eb904e407765be610017fae685088cd1ceaea22011fdc1cd5012022e1e01cd89c6075fec848b0ad00
0001005e0000047c05c3001f0065b21a202111123900b0004558b0122f1bb1121f3e59b0004558b0052f1bb1050f3e59b204010a2b5821d81bf459b008d0b21e0512111239b01e2fb21f010a2b5821d8
1bf459b00cd0b01e10b00fd0b2160512111239b01210b219010a2b5821d81bf4593031011714072107213533363635272335332734362016152334262322061517211501fd074002b801fbe752272b07
a19b08fa0196e8f5695e59670901370256b08755caca096f5bb9c7f2caeadab85f698268f2c70002005dffe5054f04f1001b0028003fb202292a111239b00210b01fd000b0004558b0022f1bb1020f3e
59b010d0b0102fb00210b220070a2b5821d81bf459b01010b226070a2b5821d81bf45930312506232227072737263534372737173633321737170716151407170701141616323636342626220606043d
9fcbca9e818d87646d908d8e9bc0c29b918e946b628b8efc786ebedcbe6d6dbddebe6d6b7f7e8490899cc5c8a593909173759491979fcac19c8d91027b78ce7576ceeecc7575cc0000010019000004c0
05b00016007200b0004558b0162f1bb1161f3e59b0004558b00c2f1bb10c0f3e59b2000c16111239b01610b001d0b20f0c16111239b00f2fb013d0b0132fb40f131f13025db004d0b0042fb01310b212
040a2b5821d81bf459b006d0b00f10b007d0b0072fb00f10b20e040a2b5821d81bf459b00ad030310101210121152115211521112311213521352135210121026d013b0118fe77010dfea3015dfea3fc
fe9e0162fe9e0119fe7701190334027cfd36988a97fed3012d978a9802ca00020088fef2016d05b000030007001800b0002fb0004558b0062f1bb1061f3e59b20501032b3031131133111123113388e5
e5e5fef2031bfce503c802f60002005afe26048c05c4002f003d0082b2203e3f111239b02010b030d000b0072fb0004558b0202f1bb1201f3e59b2392007111239b03910b213010a2b5821d81bf459b2
023913111239b00710b20e010a2b5821d81bf459b20b0e13111239b2322007111239b03210b22c010a2b5821d81bf459b21a322c111239b02010b227010a2b5821d81bf459b2242c2711123930310114
071615140423222435371416333236353426272e02353437262635342433320415233426232206151416041616252627061514161f0236353426048cab87fef2eaf6fee0f29c88798d86bbbcbe5da941
440113e6f0010cf391787b8b780183c25afdcd514c6c6395b32e738801c7b85964b9adc6d9cf016e785f4f4d5b37336e9a6db85a328864aacce1cc6a805f5254576871996e151c287c51562f35102f75
51610002005d04df032305cc00080011002200b0072fb20f07015db202050a2b5821d81bf459b00bd0b00710b010d0b0102f30311334363216140622262534363216140622265d43764444764301c844
764444764405563244446444443132444464444400030057ffec05e205c4001a00280036008eb21f3738111239b01f10b009d0b01f10b033d000b0004558b0332f1bb1330f3e59b02dd0b02d2fb20233
2d111239b0022fb40f021f02025db2092d33111239b0092fb400091009025db20d0902111239b210020a2b5821d81bf459b00210b217020a2b5821d81bf459b21a0209111239b02d10b21f080a2b5821
d81bf459b03310b225080a2b5821d81bf459303101140620263535343633321615233426232206151514163332363525340224232204021012042024122534122420041210020423222402045eaffec0
bdbf9ea3ad9c5c585c67685b595a01a696feeea39ffeef9c9b01110140011398faefbb014b0180014abbbbfeb8c2c1feb7bc025498a2d5b471aed5a59560538876757686516285a6011daba4fee0feac
fee0a7aa0120a7ca015ac7c7fea6fe6cfea6c9c8015a0002008d02b3031105c4001a0024008fb20d2526111239b00d10b01cd000b0004558b0142f1bb1141f3e59b2032514111239b0032fb000d0b000
2fb2010314111239b20a0314111239b00a2fb01410b20d020a2b5821d81bf459b2100a0d111239b2cc10015d40130c101c102c103c104c105c106c107c108c10095db2ba100171b00310b21b020a2b58
21d81bf459b00a10b21f020a2b5821d81bf4593031012706232226353436333335342322061527343633321615111417253236373523060615140260114d7c7683a8ad66744149adaf88899a1afea028
541b6a4c5602c144527b696e79337f33300e68819184fec4615182241989013c3158ffff0057008a038503a90026017aeb000007017a015200000001007f017603c203250005001a00b0042fb001d0b0
012fb00410b202010a2b5821d81bf459303101231121352103c2c8fd85034301760104ab00040057ffec05e205c4000d001b0031003a009db20a3b3c111239b00a10b012d0b00a10b031d0b00a10b033
d000b0004558b0032f1bb1031f3e59b0004558b00a2f1bb10a0f3e59b00310b212080a2b5821d81bf459b00a10b218080a2b5821d81bf459b21d0a03111239b01d2fb21f030a111239b01f2fb4001f10
1f025db2321d1f111239b0322fb21c080a2b5821d81bf459b2251c32111239b01d10b02cd0b01f10b23a080a2b5821d81bf4593031133412242004121002042322240225340224232204021012042024
122511231121321615140716161416171523263534262327333236353426272357bb014b0180014abbbbfeb8c2c1feb7bc051196feeea39ffeef9c9b01110140011398fd2597011999ac784134070a9b
0d424d9e8f455d475d8d02d9ca015ac7c7fea6fe6cfea6c9c8015acba6011daba4fee0feacfee0a7aa01205bfeaf0352877d753f1d6fa344171022a04c43863e36463b01000100870512035e05b00003
001100b0012fb202030a2b5821d81bf459303101213521035efd2902d705129e0002007f03af028b05c4000900130039b2001415111239b00ad000b0004558b0002f1bb1001f3e59b00ad0b00a2fb205
020a2b5821d81bf459b00010b210020a2b5821d81bf4593031013216140623222634361332363534262206141601876a9a986c6d9b9d6b3545456a484905c49edc9b9bdc9efe784735344c4c68480002
005f000103f304fc000b000f004600b0092fb0004558b00d2f1bb10d0f3e59b00910b000d0b00910b206010a2b5821d81bf459b003d0b00d10b20e010a2b5821d81bf459b2050e06111239b40b051b05
025d303101211521112311213521113301213521029c0157fea9d8fe9b0165d80132fcaf03510383c7fe7c0184c70179fb05c4000001003c029b02b205bb00170059b208181911123900b0004558b00f
2f1bb10f1f3e59b0004558b0002f1bb100133e59b216020a2b5821d81bf459b2020016111239b2030f00111239b00f10b208020a2b5821d81bf459b20c0f00111239b2130f0011123930310121350136
3534262322061523343633321615140f022102b2fd9c011d7136343a42baa9878f9c6a628c0173029b7d010567432a354236749980736b66577100010037028f02a905ba0024007db21e252611123900
b0004558b00d2f1bb10d1f3e59b0004558b0172f1bb117133e59b201170d1112397cb0012f18b64001500160010371b29001015db00d10b206020a2b5821d81bf459b209010d111239b00110b223020a
2b5821d81bf459b2122301111239b21b170d111239b01710b21e020a2b5821d81bf459303101333235342623220615233436333216151407161514062322263533141633323635342723010c5184363e
3041baa5828fa38795b18f87abba453c3f3d865c046c6123352723637c79697733298e6a7e7f712635372a6501000001007004d1024806000003002300b0022fb20f02015db000d0b0002fb40f001f00
025db00210b003d019b0032f1830310121012301330115feebc30600fed100010092fe60041f043a00120060b20d131411123900b0004558b0002f1bb1001b3e59b0004558b0072f1bb1071b3e59b000
4558b0102f1bb110113e59b0004558b00d2f1bb10d0f3e59b0004558b00a2f1bb10a0f3e59b00d10b204010a2b5821d81bf459b20b0d0711123930310111161633323711331123270623222711231101
8402596aa83bf3df075c93794df2043afd848d82790312fbc6566b37fe3e05da000100450000035605b0000a002bb2020b0c11123900b0004558b0082f1bb1081f3e59b0004558b0002f1bb1000f3e59
b201000811123930312111232224353424332111028450e6fef7010ae601210208fed6d5fffa50000001008e024501a90352000a0016b2080b0c11123900b0022fb1080a2b58d81bdc59303113343632
161514062322268e4a864b4e40414c02ca3a4e4e3a3b4a4a0001006dfe4101c90003000e0034b2090f1011123900b0062fb0004558b00e2f1bb10e0f3e59b00610b1070a2b58d81bdc59b20d070e1112
39b2010d0e1112393031250716151406232732363534262737013e0b96ac9b07424747502003361b926976892f2a2d23058b0001008002a0020205b300060039b201070811123900b0004558b0052f1b
b1051f3e59b0004558b0002f1bb100133e59b2040500111239b00410b203020a2b5821d81bf4593031012311073525330202b9c9016f1302a0023a3092770002007702b2032c05c4000c001a0040b209
1b1c111239b00910b010d000b0004558b0022f1bb1021f3e59b2091b02111239b0092fb210020a2b5821d81bf459b00210b217020a2b5821d81bf4593031133436201615151406232226351714163332
36373534262322061577bf0136c0bc9d9ebeaf5d504e5b015d4f4e5d0461a0c3c2a6489fc3c4a305626e6c6150616e6d6600ffff005d008a039903a90026017b09000007017b017e0000ffff00590000
058305ab002701d5ffd902980027017c011b0008010701d802c50000001000b0004558b0052f1bb1051f3e593031ffff0050000005cc05ae0027017c00f00008002701d5ffd0029b010701d6031a0000
001000b0004558b0092f1bb1091f3e593031ffff0067000005fc05bb0027017c01a80008002701d8033e0000010701d70030029b001000b0004558b0202f1bb1201f3e59303100020042fe7f03a5044e
001900230061b2102425111239b01010b01dd000b0004558b0212f1bb1211b3e59b0004558b0102f1bb110173e59b02110b21d0d0a2b5821d81bf459b000d0b0002fb2030010111239b01010b209010a
2b5821d81bf459b20c1000111239b216100011123930310106060707061514163332363533060623222635343737363737131406222635343632160276023549675a6259586af302efc2cee29b5c4e0a
02f747844848844702957c914f6a616a5e5d6453b1d0c9b8a5a35d4873350137384b4b38374b4b000002fff60000075705b0000f0012007700b0004558b0062f1bb1061f3e59b0004558b0002f1bb100
0f3e59b0004558b0042f1bb1040f3e59b2110600111239b0112fb202010a2b5821d81bf459b00610b208010a2b5821d81bf459b20b0600111239b00b2fb20c010a2b5821d81bf459b00010b20e010a2b
5821d81bf459b21206001112393031212103210321012115211321152113210121030757fc7e0ffe0ab8fede034303e0fd7a110224fde4140297faed01791b0154feac05b0c5fe68c5fe360167028800
0001004d00d603ec0486000b003800b0032fb2090c03111239b0092fb20a0903111239b2040309111239b2010a04111239b00310b005d0b207040a111239b00910b00bd0303113010137010117010107
01014d013cfec494013b013c94fec4013c94fec4fec5016c0142014296febe014296febefebe960141febf0000030069ffa1052205ee0017002000290066b2102a2b111239b01010b01dd0b01010b026
d000b0004558b0102f1bb1101f3e59b0004558b0042f1bb1040f3e59b21a1004111239b2231004111239b02310b01bd0b01010b21d010a2b5821d81bf459b01a10b024d0b00410b226010a2b5821d81b
f4593031011402042322270723372611353412243332173733071613051417012623220207053427011633321235052294feedb4a4845ba991c3960114b2c58f57a7939d01fc444701f65787a4b90202
bf2cfe174e69a9b502b2d6febdad4b96eec3016743d50144af658ff3c1fec34bcf80033a55feffeb08a672fcdc360100f600000200940000047e05b0000c00140057b2021516111239b00210b00fd000
b0004558b0002f1bb1001f3e59b0004558b00a2f1bb10a0f3e59b2010a00111239b0012fb20e0a00111239b00e2fb209010a2b5821d81bf459b00110b20d010a2b5821d81bf459303101113332041514
04232311231113113332363426270187f1f40112feeef3f2f3f3f67d918c7a05b0fee8eec8c7effed405b0fe25fe1a82de84020000010088ffec049b0615002c005bb2232d2e11123900b0004558b005
2f1bb105213e59b0004558b0152f1bb1150f3e59b0004558b0002f1bb1000f3e59b20e0515111239b01510b21c010a2b5821d81bf459b2221505111239b00510b22a010a2b5821d81bf4593031212311
343633321615140e0215141e021514062322262737161633323635342e02353436353426232207017af2e5cebbd71b451641b251d9c650ab26312d7f36615a46ae517e5c50b8040451d6eebba93e6271
41272c5494894babb92719c31c255643315b88885058c94d5161f70000030048ffec0684045000290034003c00cab2023d3e111239b00210b02dd0b00210b038d000b0004558b0172f1bb1171b3e59b0
004558b0052f1bb1050f3e59b000d0b0002fb20c0517111239b00c2fb28f0c015db01710b210010a2b5821d81bf459b01710b01bd0b01b2fb238001b111239b0382fb41f382f380271b4ef38ff380271
b45f386f380271b4bf38cf38025db28c38015db220070a2b5821d81bf459b00010b223010a2b5821d81bf459b00510b22a010a2b5821d81bf459b00c10b22f070a2b5821d81bf459b01b10b235010a2b
5821d81bf45930310522270606232226353436333335342623220615273436333217361732121515211616333237371706062532363735230606151416012206072135342604e6fd8c41d686b0c8eee9
bf5f585b73f2fdc5df6f83c8d4eefd49099886896b3d4946d1fc983a882dc468785d032b637f1001c46d14a14d54b09c9eac475b6759421392b9858702fefdeb898b9e3a22a63840b83b2bd1025f4641
4f02e78a7f1e717a00020067ffec0440062c001d002b0065b2072c2d111239b00710b028d000b0004558b0192f1bb119213e59b0004558b0072f1bb1070f3e59b20f0719111239b00f2fb2110f071112
39b01910b218010a2b5821d81bf459b00f10b222010a2b5821d81bf459b00710b228010a2b5821d81bf45930310112111514020623222626353436363332172627072737262737161737170327262623
2206151416333236350342fe7ee58c8ae27e71ce849271317ecc4eac7ea24beeb1b44e8f01207b4e7e8b8d6e6f890517fef7fe6f52a6fef9927ee28895e77d5ba97a876d72522ac33287786dfd191230
38a8957ea8c8ad00000300430093043704cc0003000d00190052b2041a1b111239b00410b000d0b00410b011d000b0032fb200010a2b5821d81bf459b00310b1090a2b58d81bdc59b2040d0a2b5821d8
1bf459b00010b1110a2b58d81bdc59b2170d0a2b5821d81bf459303101213521013216140623222634360334363332161514062322260437fc0c03f4fe09444a4a44434a4a4a4a43444a4a44434a0246
d401b24c724b4b724cfc4a3a4c4c3a394a4a0003004fff77043d04bb0015001d00250066b2042627111239b00410b01bd0b00410b023d000b0004558b0042f1bb1041b3e59b0004558b00f2f1bb10f0f
3e59b218040f111239b220040f111239b02010b019d0b00410b21b010a2b5821d81bf459b01810b021d0b00f10b223010a2b5821d81bf459303113343636333217373307161114060623222707233726
131417012623220605342701163332364f7ee4946a58479166c47be5965d5a489166cef340012b2f39778c02093afed82b337b8902279fff89228fd099fec0a0fc8a1e93cf9601369c62026116bda794
5dfda711c00000020082fe6004370600000f001a0064b2131b1c111239b01310b00cd000b0092fb0004558b00c2f1bb10c1b3e59b0004558b0062f1bb106113e59b0004558b0032f1bb1030f3e59b205
0c03111239b20a0c03111239b00c10b213010a2b5821d81bf459b00310b218010a2b5821d81bf45930310114022322271123113311363332121127342623220711163332360437e3c2b26bf3f36ab0c5
e3f383769541429674830212f7fed175fdff07a0fdd777fedafefa05a6ba7bfe207ebb000002001f0000059d05b000130017006b00b0004558b00f2f1bb10f1f3e59b0004558b0082f1bb1080f3e59b2
14080f111239b0142fb210140f111239b0102fb000d0b01010b217070a2b5821d81bf459b003d0b00810b005d0b01410b207010a2b5821d81bf459b01710b00ad0b01010b00dd0b00f10b012d0303101
3315231123112111231123353311331121113301213521051e7f7ffcfd75fc7c7cfc028bfcfc79028bfd7504aea2fbf40287fd79040ca20102fefe0102fda2ba0001008f00000182043a0003001d00b0
004558b0022f1bb1021b3e59b0004558b0002f1bb1000f3e593031212311330182f3f3043a000001008e0000046b043a000c005f00b0004558b0042f1bb1041b3e59b0004558b0082f1bb1081b3e59b0
004558b0022f1bb1020f3e59b0004558b00b2f1bb10b0f3e59b2060204111239b0062fb41f062f060271b28f06015db201010a2b5821d81bf459b20a0106111239303101231123113311330121010121
01ef6ff2f2550150012cfe6101b9fecb01acfe54043afe5001b0fdf3fdd3000100220000043605b0000d005b00b0004558b00c2f1bb10c1f3e59b0004558b0062f1bb1060f3e59b2010c06111239b001
2fb000d0b00110b202070a2b5821d81bf459b003d0b00610b204010a2b5821d81bf459b00310b008d0b009d0b00010b00bd0b00ad03031013715071121152111073537113301a1eaea0295fc6e8282fd
0367479347fdf6ca0287279327029600000100210000022e0600000b004a00b0004558b00a2f1bb10a213e59b0004558b0042f1bb1040f3e59b201040a111239b0012fb000d0b00110b202070a2b5821
d81bf459b003d0b006d0b007d0b00010b009d0b008d03031013715071123110735371133019a9494f38686f30379359235fd1902902f922f02de00010090fe4b050905b000130067b206141511123900
b0004558b0002f1bb1001f3e59b0004558b0102f1bb1101f3e59b0004558b0042f1bb104113e59b0004558b00c2f1bb10c0f3e59b0004558b00e2f1bb10e0f3e59b00410b209010a2b5821d81bf459b2
0d000c111239b2120e00111239303101111406232227371633323535011123113301110509bea9463c0e283a7bfd81fcfc027f05b0fa18b7c611c70cb8310415fbeb05b0fbec04140001007efe4b0406
044e001a0061b2151b1c11123900b0004558b0032f1bb1031b3e59b0004558b0002f1bb1001b3e59b0004558b00a2f1bb10a113e59b0004558b0182f1bb1180f3e59b2011803111239b00a10b20f010a
2b5821d81bf459b00310b215010a2b5821d81bf4593031011736333216171114062322273716333235113426232207112311015c0d73c4b0b501bba6453a0e283b7c5d69914bf3043a96aad6d2fd1bb4
c211c60cb002d9787067fce0043a00020064ffec072d05c4001700230091b2012425111239b00110b01ad000b0004558b00c2f1bb10c1f3e59b0004558b00e2f1bb10e1f3e59b0004558b0032f1bb103
0f3e59b0004558b0002f1bb1000f3e59b00e10b210010a2b5821d81bf459b212000e111239b0122fb215010a2b5821d81bf459b00010b217010a2b5821d81bf459b00310b218010a2b5821d81bf459b0
0c10b21d010a2b5821d81bf4593031212106232224022711341224333217211521112115211121053237112623220607111416072dfc9da779a7fef7940291010ba87ba7035cfd4c0256fdaa02bbfb7d
6368725ba1af01b21493010daa013aac01129614ccfe6ec8fe401c0d04380ecfbcfecac1d1000003005bffec06f2044f001e002a0032009bb2193334111239b01910b024d0b01910b02ed000b0004558
b0032f1bb1031b3e59b0004558b0082f1bb1081b3e59b0004558b0172f1bb1170f3e59b0004558b01b2f1bb11b0f3e59b2050817111239b22f1708111239b02f2fb41f2f2f2f0271b28c2f015db20c07
0a2b5821d81bf459b01710b210010a2b5821d81bf459b2190817111239b022d0b00310b228010a2b5821d81bf459b02bd030311334003332173636173212151521161633323637170606232227062322
001117141633323635342623220625220607213534265b010fe0f98641b76dd6eefd560b9175598f474f47cd78f78c86f6e3fef2f2867977868778758803e155781401b5710227f8012fb1545e01fefd
ec888b9e2a329e3f41aeae012d010209aabab9c0a6bebaba8979196f7a000001008b000002950615000c0032b2030d0e11123900b0004558b0042f1bb104213e59b0004558b0002f1bb1000f3e59b004
10b209010a2b5821d81bf4593031331134363332170726232215118bc2b03f59192a32a3049cb6c315b90bbafb6800020051ffec051e05c40016001e005bb2001f20111239b017d000b0004558b00f2f
1bb10f1f3e59b0004558b0002f1bb1000f3e59b2050f00111239b0052fb00f10b208010a2b5821d81bf459b00010b217010a2b5821d81bf459b00510b21a010a2b5821d81bf459303105200011352126
26232207072737363320001115140204273236372115141602b8fedcfebd03d005dfcca797343121b0da013a016ba2fee5a996be12fd2fba140160014989e0f03413c60f48fe8bfeb76bc3fec3afd4da
bd1fb9bf0001ffe4fe4b02d30615001e0071b2141f2011123900b0004558b0152f1bb115213e59b0004558b0102f1bb1101b3e59b0004558b01d2f1bb11d1b3e59b0004558b0052f1bb105113e59b01d
10b200010a2b5821d81bf459b00510b20b010a2b5821d81bf459b00010b00ed0b00fd0b01510b21a010a2b5821d81bf45930310123111406232227371616333235112335333534363332170726232207
15330284c9b5a448360f07441278a5a5c2b13d5b19263b9d01c90386fc35b0c011bf030aae03cab462b6c315bc0aad6700020058ffec05aa062e00180026005bb2042728111239b00410b023d000b000
4558b00d2f1bb10d1f3e59b0004558b0042f1bb1040f3e59b20f0d04111239b00f2fb216080a2b5821d81bf459b00d10b21c010a2b5821d81bf459b00410b223010a2b5821d81bf45930310114020423
22240227353412243332173636353314060716170734262322020715141233321235051094feedb4b0feeb9701970113b1ffa24f4cbb797c5704fdb8a8a4b902b9a8a9b502b2d6febdadad0140d152d5
0146ada80d8382a4d123a7df12f6fefeffeb54ecfef60100f6000002004fffec04bb04a800170022005bb2142324111239b01410b020d000b0004558b0042f1bb1041b3e59b0004558b0142f1bb1140f
3e59b2060414111239b0062fb20d080a2b5821d81bf459b01410b21a010a2b5821d81bf459b00410b220010a2b5821d81bf4593031133436363332173636353314060716171514060623220011171416
32363534262322064f7de494e18a3530a758673f027be795e3feecf28af6898d79778c0227a1fd8995136a7286b3257d9e1da0fc8a012e010109a7bdc0b9a7bdbd000001007dffec063d060100180054
b20c191a11123900b0004558b0182f1bb1181f3e59b0004558b0112f1bb1111f3e59b0004558b00c2f1bb10c0f3e59b2010c18111239b0012fb208080a2b5821d81bf459b00c10b215010a2b5821d81b
f45930310115363635331406071114002322003511331114163320111104bd6d5eb5bbc5fed7f7fafedafc9490012405b0dc0a82a1e4d609fda5e8fef1010bed03ccfc32929a013403c600010077ffec
0528049300190061b2071a1b11123900b0004558b00d2f1bb10d1b3e59b0004558b0082f1bb1080f3e59b0004558b0042f1bb1040f3e59b00d10b013d0b2151308111239b0152fb203080a2b5821d81b
f459b2061508111239b00810b210010a2b5821d81bf4593031011406071123270623222635113311143332371133153636373705288fa2e5066bc5b0b5f3abb13ef3484105020493b2a50bfccf6a7ece
c302bdfd46ce7f03098807424c4c0001ffb5fe4b0193043a000c002fb2030d0e11123900b0004558b00c2f1bb10c1b3e59b0004558b0042f1bb104113e59b209010a2b5821d81bf45930310111060623
2227371633323511019301b8a746380f273a7c043afb85b2c211bf0dc0046c0000020059ffec03f8044f0016001e005eb2081f20111239b00810b017d000b0004558b0002f1bb1001b3e59b0004558b0
082f1bb1080f3e59b20c0008111239b00c2fb00010b210010a2b5821d81bf459b00810b217010a2b5821d81bf459b00c10b21a070a2b5821d81bf4593031013200151514060627220235352126262322
060727363613323637211514160200e401147bda86d5ef02aa0b8f77568b4e4f46d291567813fe4b71044ffed4f61f9afb8d010101ed8888a127359e3e43fc608e74196f7a000001009404e003430601
0008004500b0042fb20f04015db25004015db27004015db002d0b0022fb001d019b0012f18b00410b007d0b0072fb40f071f07025db2030704111239b00110b005d019b0052f18303101152327072335
01330343c39695c1010f8f04eb0b9c9c0d0114000001007204e0033406010008002500b0042fb20f04015db001d0b0012fb40f011f01025db2000401111239b008d0b0082f3031013733150123013533
01d292d0fee996feebce05669b0afee901180900ffff00870512035e05b00006007000000001007504cc02fb05e6000b002f00b0032fb20f03015db006d0b0062fb40f061f06025db00310b208020a2b
5821d81bf459b00610b00bd0b00b2f303101140620263533141632363502fbb0fedab0b64b844a05e67e9c9c7e42494942000001008104df018705d50009001db2030a0b11123900b0082fb20f08015d
b202050a2b5821d81bf45930311334363216151406222681447e44447e440559354747353446460000020078048d0233062a00090014002a00b0052fb20f05015db013d0b0132fb2000a0a2b5821d81b
f459b00510b20d0a0a2b5821d81bf459303101321614062322263436071416333236353426220601565d807d60617d7f11422e2f413f623f062a7baa7878aa7bd02f4140302e434300010029fe5201a1
003c000f0022b20f101111123900b0004558b00a2f1bb10a113e59b205030a2b5821d81bf459303121060615143332371706232226353437018c574a472c2e15495c5f74f4385e3144178e2c6e5bb56c
0001007a04db035705f50015004000b0032fb008d0b0082fb60f081f082f08035db00310b00bd0b00b2fb00810b20f030a2b5821d81bf459b00310b212030a2b5821d81bf459b00f10b015d030310114
0623222e02232206152734363332163332363503577f602739692b1a2635957f5f39a134263605e96e92113c0c392e086e965a392f000002004904d1035605ff00030007004000b0022fb20f02015db0
00d0b0002fb40f001f00025db00210b003d019b0032f18b00010b005d0b0052fb00210b006d0b0062fb00310b007d019b0072f18303101330123033303230268eefef6c590e9deb905fffed2012efed2
00020082fe6a01ecffbe000b0017003d00b0182fb003d0b0032f400f0003100320033003400350036003075db00fd0b00f2fb209090a2b5821d81bf459b00310b215090a2b5821d81bf4593031173436
33321615140623222637141633323635342623220682694e496a6a494e69653022212d2d212230ee4963614b4a5e6048212e2d22243030000001fc8e04d1fe6606000003002300b0012fb20f01015db0
00d019b0002f18b00110b002d0b0022fb40f021f02025d303101230121fe66cafef2011504d1012f0001fd5e04d1ff3606000003002300b0022fb20f02015db001d0b0012fb40f011f01025db00210b0
03d019b0032f18303101210123fe210115feebc30600fed1fffffc7304dbff5005f5000700a4fbf900000001fd3e04e6fe99067f000e002500b0002fb006d0b0062fb2010006111239b207080a2b5821
d81bf459b20d01001112393031012736363534233732161514060715fd510749419607a9ab4e4804e692051c23487b68583c4e0a45000002fc0c04e4ff3405ee00030007003700b0012fb000d019b000
2f18b00110b005d0b0052fb006d0b0062fb60f061f062f06035db003d0b0032fb00010b004d019b0042f1830310123012101230333fe07d0fed501060222c3f5fa04e4010afef6010a000001fd1cfe94
fe2fff8b0008001100b0022fb206050a2b5821d81bf4593031053436321614062226fd1c478448488447f13547476a464600000100c604e901e206410003001700b0022fb000d0b0002fb00210b003d0
19b0032f183031013303230103df8c900641fea80003006704df03ba06af0003000c0015003b00b0142fb002d0b0022fb001d0b0012fb40f011f01025db00210b003d019b0032f18b01410b00bd0b00b
2fb206050a2b5821d81bf459b00fd030310133032305343632161406222625343632161406222601eee58292fea8447643437644025643764444764306affed62f32444464444431324444644444ffff
008e024501a903520206007800000001009b0000043705b00005002b00b0004558b0042f1bb1041f3e59b0004558b0022f1bb1020f3e59b00410b200010a2b5821d81bf45930310121112311210437fd
60fc039c04e4fb1c05b000020019000005a005b000030006002f00b0004558b0002f1bb1001f3e59b0004558b0022f1bb1020f3e59b204010a2b5821d81bf459b2060200111239303101330121252101
026ff3023efa79015502e0fe9805b0fa50ca03bb0003005bffec051305c40003001400220076b2082324111239b00810b001d0b00810b01fd000b0004558b0102f1bb1101f3e59b0004558b0082f1bb1
080f3e59b20208101112397cb0022f18b460027002025db430024002025db200020171b201010a2b5821d81bf459b01010b218010a2b5821d81bf459b00810b21f010a2b5821d81bf459303101213521
0514020423222402273534122420041217073402232202071514123332123503a3fe4001c0017094feedb3b0feee9903960114016401139601fcb7a9a4b902bba6a9b50279c289d6febdadaa013ccd5d
d50144afabfebfd505ef0105feffeb54f0fefa0100f6000100200000051205b00006003100b0004558b0032f1bb1031f3e59b0004558b0012f1bb1010f3e59b0004558b0052f1bb1050f3e59b2000301
1112393031010121013301210298fe97fef101fef501fffef00444fbbc05b0fa50000003006c0000042e05b000030007000b004b00b0004558b0082f1bb1081f3e59b0004558b0022f1bb1020f3e59b2
00010a2b5821d81bf459b2050802111239b0052fb206010a2b5821d81bf459b00810b20a010a2b5821d81bf45930313721152113211521032115216c03c2fc3e6402f6fd0a570399fc67caca034dc603
29cc0001009b0000051405b00007003800b0004558b0062f1bb1061f3e59b0004558b0002f1bb1000f3e59b0004558b0042f1bb1040f3e59b00610b202010a2b5821d81bf45930312123112111231121
0514fcfd7ffc047904e4fb1c05b0000100470000044d05b0000c003c00b0004558b0082f1bb1081f3e59b0004558b0032f1bb1030f3e59b201010a2b5821d81bf459b005d0b00810b20a010a2b5821d8
1bf459b007d0303101012115213501013521152101031cfe7502bcfbfa01c9fe3703e2fd6b018802d0fdfaca970242023f98ccfdff000003004a000005ae05b00015001c0023006cb20b2425111239b0
0b10b019d0b00b10b020d000b0004558b0142f1bb1141f3e59b0004558b00a2f1bb10a0f3e59b213140a111239b0132fb000d0b2090a14111239b0092fb00cd0b00910b221010a2b5821d81bf459b019
d0b01310b21a010a2b5821d81bf459b020d03031011604161514060706071523352624261036243735330114161711060605342627113636037ca101038e887c85a9fda2fefc8f8e0103a4fdfdc6aa93
96a70374a69491a904ff038ffe9e9af6484d03a9a9018cfa013eff8f03b1fd1fa0b00202ae04b79fa0b604fd5202b300000100440000055c05b00017005cb200181911123900b0004558b0112f1bb111
1f3e59b0004558b0162f1bb1161f3e59b0004558b0042f1bb1041f3e59b0004558b00b2f1bb10b0f3e59b2150b16111239b0152fb000d0b01510b20c010a2b5821d81bf459b009d03031013636351133
110600071123112600271133111616171133034c8390fd03fee9f6fcf0fee804fc018f80fc024317bea701f1fe06f6fecf19fe8a0175170130f501fffe0b9dc218036c000001006b000004dd05c30025
005cb207262711123900b0004558b01a2f1bb11a1f3e59b0004558b00f2f1bb10f0f3e59b0004558b0242f1bb1240f3e59b00f10b211010a2b5821d81bf459b00ed0b000d0b01a10b207010a2b5821d8
1bf459b01110b022d0b023d03031253612373534262322061515141217152135332602353534122433320412151514020733152102df747b019d908e9b7f77fe07d86b788e0105a4a5010690776bd4fe
10cf200110e76dcadad9cd64ebfeeb1ecfcb67011f9e62b6011d9f9efee2b56597fedc67cb0000020056ffeb0479044e001600210079b21f2223111239b01f10b013d000b0004558b0132f1bb1131b3e
59b0004558b0002f1bb1001b3e59b0004558b00c2f1bb10c0f3e59b0004558b0082f1bb1080f3e59b203010a2b5821d81bf459b20a130c111239b215130c111239b00c10b21a010a2b5821d81bf459b0
1310b21f010a2b5821d81bf45930310111163332371706232227062322023535101233321737011416333237112623220603fd0346110a18334ca23566c1c3e3e4c4b56713fe1c7a768c46468a737f04
3afcfa7b04b41ea3a2011df80d010a01369783fdbf9ead8801c78ec500020096fe77046a05c4001400280065b227292a111239b02710b000d000b00f2fb0004558b0002f1bb1001f3e59b0004558b00c
2f1bb10c0f3e59b227000c111239b0272fb224010a2b5821d81bf459b2062427111239b00010b218010a2b5821d81bf459b00c10b21e010a2b5821d81bf4593031013216151406071616151406232227
11231134363601342623220615111633323635342627233533320269cff263587982f2d1a57af27cd9014c715d6081589d71897a677b48d405c4d8b25f9b302cbd82cdec53fe3805a973c170fe6d5a76
7e68fce552896e6d9101b90000010020fe5f03f5043a00080038b200090a11123900b0004558b0012f1bb1011b3e59b0004558b0072f1bb1071b3e59b0004558b0042f1bb104113e59b2000704111239
3031011333011123110133020eecfbfe8ff3fe8ffb013b02fffbf0fe3501d0040b0000020054ffec04380620001f002b0062b2162c2d111239b01610b023d000b0004558b0032f1bb103213e59b00045
58b0162f1bb1160f3e59b00310b209010a2b5821d81bf459b20e1603111239b00e2fb229010a2b5821d81bf459b21d290e111239b01610b223010a2b5821d81bf4593031133436333216171526232206
1514171612171514060623220011343637272626131416333236353426272206d0d4b749714f97694e5abce0de027ae195e2feeeb889025b687689797787916d798904ea91a5161bc3353d345d424ffe
eacc1c9bf68701230103a5ff22052889fd7da2bcbcb678cb17be00010060ffec040c044d0027008bb216282911123900b0004558b0092f1bb1091b3e59b0004558b0252f1bb1250f3e59b21709251112
397cb0172f18b440175017025db4d017e017025db218070a2b5821d81bf459b2031817111239b00910b210010a2b5821d81bf459b20d1710111239b21c0d015db20b0d015db02510b21e010a2b5821d8
1bf459b2211e18111239b404211421025d3031133436372626353436333216152334262322061514163333152306151416333236353314042322246069625761f8d2bffff27a595e726069c7d1d27d66
6282f2fefccbd5fef801325c7f2024794896a5b5913c4f4d3f3c4bad03933f5759429bbab20000010061fe7e03ca05b0001e004ab2081f2011123900b00f2fb0004558b0002f1bb1001f3e59b0004558
b0152f1bb1150f3e59b00010b21c010a2b5821d81bf459b2011c00111239b01510b208010a2b5821d81bf45930310115010606151416171716161514060727363536272726272635100137213503cafe
6056463d4bdd614f7a527d5d026e68c44a390125dcfdc405b091fe0a6dba6b545a18421f625147ba3e6567463d211b3269508b01200151fdc3000001007efe610406044e00110053b20c121311123900
b0004558b0032f1bb1031b3e59b0004558b0002f1bb1001b3e59b0004558b0072f1bb107113e59b0004558b00f2f1bb10f0f3e59b201030f111239b00310b20c010a2b5821d81bf45930310117363332
16171123113426232207112311015c0c77c1b6ad03f35e689646f3043a8397c4c5fb9c04536e697afcef043a00030073ffec042c05c4000d0016001e0079b2031f20111239b00310b013d0b00310b01b
d000b0004558b00a2f1bb10a1f3e59b0004558b0032f1bb1030f3e59b20e030a1112397cb00e2f18b4600e700e025db4300e400e025db2000e0171b00a10b213010a2b5821d81bf459b00e10b218010a
2b5821d81bf459b00310b21b010a2b5821d81bf459303101100223220203351012333212130521353426232206150521151416323637042cf8e3dffa05f6e6e2f605fd3a01d47a716f7a01d4fe2c7be0
77020272fec4feb60141012de90135014cfec4fed32330cecbcbceef2ad0d1caca00000100a9fff40261043a000c002800b0004558b0002f1bb1001b3e59b0004558b0092f1bb1090f3e59b204010a2b
5821d81bf459303101111416333237150623201111019c323e2a2b4a56fee8043afcf63d360abc170135031100010016ffee044a05fb00190050b2031a1b11123900b0002fb0004558b00b2f1bb10b0f
3e59b0004558b0102f1bb1100f3e59b00b10b207010a2b5821d81bf459b20f000b111239b00f10b012d0b00010b215010a2b5821d81bf459303101321617011617173717062322262703032101272627
2307273601126c781f01ab24312011042a346d752bcaf6fef701815b2249221b033b05fb5550fbbf56070101c00a586f0214fd37040fda4b0302b61000010064fe7603d405c4002c0056b2032d2e1112
3900b0162fb0004558b02a2f1bb12a1f3e59b202010a2b5821d81bf459b2082d2a111239b0082fb209010a2b5821d81bf459b21d2d2a111239b01d10b20e010a2b5821d81bf459b22409081112393031
01262322061514213315232011141604161716150606072736363534262427262635343637262635342433321703838a577a88011c898cfe9e8101196f2351027b5083352e3ffefd4c7f76a3906e7c01
02e3997d04da24564bb8c6fee36288422518386d48bb3b64395029232d442035b79491c42d288e61a6c52c000001002dfff404cf043a0014005cb20b151611123900b0004558b0132f1bb1131b3e59b0
004558b00a2f1bb10a0f3e59b0004558b00f2f1bb10f0f3e59b01310b200070a2b5821d81bf459b00a10b205010a2b5821d81bf459b00010b00dd0b00ed0b011d0b012d0303101231114163332371506
232011112111231123352104a99f313f262f4a56fee8feb4f3ab047c037cfdb63e370abc1701350253fc84037cbe00020080fe600431044e000e001a0057b2111b1c111239b01110b000d000b0004558
b0002f1bb1001b3e59b0004558b00a2f1bb10a113e59b0004558b0072f1bb1070f3e59b2090007111239b211010a2b5821d81bf459b00010b217010a2b5821d81bf45930310132121115140223222711
231134000316333236353426232206150256e0fbe0c1b36af30103104395767d7c726677044efecbfeef0ff2fee577fdfd03dbf20121fcd575adb3b8c5c1a00000010052fe8a03e9044e0022004db21b
232411123900b0004558b0002f1bb1001b3e59b0004558b0142f1bb114173e59b00010b004d0b00010b207010a2b5821d81bf459b21c2300111239b01c10b20d010a2b5821d81bf45930310132161523
3426232206151514160416161714060727363635342627262627353436360238c4ede46d60718394012e6031017f4c7f332a3c41eeed0178dc044eddbb6174bcaa1a839b5639534248bf3865374e2c28
2a0f37fed1279dfa890000020052ffec047e043a000f001b004cb2071c1d111239b00710b013d000b0004558b00e2f1bb10e1b3e59b0004558b0072f1bb1070f3e59b00e10b200010a2b5821d81bf459
b00710b213010a2b5821d81bf459b00010b019d0303101211615140606232200113534003721011416333236353426232206047efef5ba7ade91e2fef0010cdf0241fcc7857a758183757687037692fb
8eec83012c01030cee012302fdd8a9bbbcbd9cb3b0000001003fffec03ec043a00100049b201111211123900b0004558b00f2f1bb10f1b3e59b0004558b00a2f1bb10a0f3e59b00f10b200010a2b5821
d81bf459b00a10b205010a2b5821d81bf459b00010b00dd0b00ed03031012111141633323717062320031121352103ecfe982b33273726506cfeec05feae03ad0379fdb03b3b16b12c01390254c10001
0080ffeb0408043a00120038b20e131411123900b0004558b0002f1bb1001b3e59b0004558b00e2f1bb10e0f3e59b203010a2b5821d81bf459b00010b008d0b0082f3031011110333236352603331611
100023222627110172a17191036ef173fefce7cbd101043afd76fefde9a0e7011de6fee2fef4fec1e2d8029500020044fe2205850441001a0023005fb2102425111239b01010b01bd000b0192fb00045
58b0112f1bb1111b3e59b0004558b0062f1bb1061b3e59b0004558b0002f1bb1000f3e59b20d010a2b5821d81bf459b00010b018d0b00d10b01bd0b01110b221010a2b5821d81bf45930310524003534
123717060607141617113436333216161514000511231336363526262322150265fefcfee37e7398484c029a949e7c93ec87fedefef5f3f395a5028d74370e1c0137ffa40105539246bc68a1cd1e0280
77928dfb92f3fed71afe31029419c19797bf3e000001004ffe22057e043a00180044b200191a11123900b00d2fb0004558b0142f1bb1141b3e59b0004558b00f2f1bb10f0f3e59b217010a2b5821d81b
f459b001d0b01410b018d0b006d0b00f10b00cd0303101113636352603331611100005112311240003113311100511035293a70570ee79fee1fef3f3fefcfef501f3011d043afc7d1bcea4e20114e3fe
edfefcfeca1afe3201d01e0133010a01edfe18fea23c038200010066ffec062d043a00200056b21a212211123900b0004558b0002f1bb1001b3e59b0004558b0182f1bb1180f3e59b0004558b01c2f1b
b11c0f3e59b205010a2b5821d81bf459b209001c111239b00ed0b00010b013d0b0132fb21a0518111239303101020714163332363511331116163332363526033316100223222706232202103701e586
0761585b60fb025f5a58610785f18dd5cbe85c5ce6cbd68d043afee9edbdcb9d940146feaf8e98cbbdef0115e8fdc8fed2dede012e0238e800020076ffec049805c400200029006bb20f2a2b111239b0
0f10b021d000b0004558b01a2f1bb11a1f3e59b0004558b0062f1bb1060f3e59b2241a06111239b0242fb213010a2b5821d81bf459b002d0b20b1a06111239b00610b20f010a2b5821d81bf459b02410
b01ed0b01a10b227010a2b5821d81bf459303101060715140623220035113711141633323635352600273534363332161511363701141617112623220604983a44fad5d3fefeec826e626dd1ff0003c5
a5a7bc4b2afdaa7d6b046d34430257140b75dafd0105d4011d02fede7d8f86837c260113c01ba9ccd0bbfece0c0b01236ca22001459a49000001ffe10000049e05c3001a0042b2001b1c11123900b000
4558b0042f1bb1041f3e59b0004558b00d2f1bb10d0f3e59b200040d111239b00410b209010a2b5821d81bf459b012d0b00410b017d03031011336363332170726232207011123110126232207273633
321617023fd22b7a604642260d28411ffed9fcfedb21402b0a243c4a677d2c030701f864601ac20545fd6bfdee021002974505c11b646c0000020033ffec0654043a001200260070b2082728111239b0
0810b01ed000b0004558b0112f1bb1111b3e59b0004558b0062f1bb1060f3e59b0004558b00a2f1bb10a0f3e59b01110b200010a2b5821d81bf459b2081106111239b00fd0b010d0b015d0b016d0b00a
10b21b010a2b5821d81bf459b21f100a111239b024d0303101231615100223222706232202113437233521012627210607141633323637353315161633323606548037cabcee5c5ceebdc8366f0621fe
c5043dfcc63c04534b5c6601fa02635d4b5303839eaffee2fed4e2e2012e011cb19cb7fdfca0adb19cbeca9795e8ee8f97ca00010022fff205bc05b00018006eb211191a11123900b0004558b0172f1b
b1171f3e59b0004558b0092f1bb1090f3e59b0004558b0132f1bb1130f3e59b01710b200010a2b5821d81bf459b2041709111239b0042fb00910b20a010a2b5821d81bf459b00410b210010a2b5821d8
1bf459b00010b015d0b016d03031012111363332041004232732363526262322071123112135210490fe139472fb0118feeefe01898c018f8f8678fdfe7c046e04e4fe7426f0fe50ecbf7984778720fd
7404e4cc00010068ffec04ef05c4001f0071b203202111123900b0004558b00c2f1bb10c1f3e59b0004558b0032f1bb1030f3e59b00c10b213010a2b5821d81bf459b2170c031112397cb0172f18b430
174017025db460177017025db4d017e017025db200170171b218010a2b5821d81bf459b00310b21c010a2b5821d81bf45930310106002322240227353412243332001723262623220607211521161633
32363704ee16fed4f8affef59101920111b4f3012518fc12948ea1b00801fbfe0407ab9d93961401d9e8fefba50136cf7bcf013aaafef6ec9c8ee5d2cadde5879d000002002d0000084105b000190022
0074b2092324111239b00910b01ad000b0004558b0182f1bb1181f3e59b0004558b0082f1bb1080f3e59b0004558b0102f1bb1100f3e59b2001808111239b0002fb01810b20a010a2b5821d81bf459b0
1010b212010a2b5821d81bf459b00010b21a010a2b5821d81bf459b01210b01bd0b01cd0303101211e021514040721112103020206232335373e02371321111121323635342627050d013199eb7ffeeb
e5fdcafe421a0f63bc9e4028575f310a1c03ab01297e918f7a03a10175d487cefd0504e4fdcdfef8fedd86ca03086ad7d102c9fd26fdf49375738f020002009b0000084705b00013001c0087b2011d1e
111239b00110b014d000b0004558b0022f1bb1021f3e59b0004558b0132f1bb1131f3e59b0004558b0102f1bb1100f3e59b0004558b00d2f1bb10d0f3e59b2001013111239b0002fb29f00015db2040d
02111239b0042fb00010b20f010a2b5821d81bf459b00410b214010a2b5821d81bf459b00d10b215010a2b5821d81bf45930310121113311213216161514042321112111231133011121323635342623
01970280fc012b9cee7ffee3f3fde0fd80fcfc037c01297e92947c0345026bfdd26ecb85cdf7027afd8605b0fd08fe1886706f8300010031000005c805b00015005600b0004558b0142f1bb1141f3e59
b0004558b0082f1bb1080f3e59b0004558b0102f1bb1100f3e59b01410b200010a2b5821d81bf459b2041014111239b0042fb20d010a2b5821d81bf459b00010b012d0b013d030310121113633200415
11231134262322071123112135210492fe11838f010c0107fc7d9a8c86fcfe8a046104e4fe9b1bece5fe3701ca8b7a1cfd4d04e4cc0000010092fe98050d05b0000b004800b0092fb0004558b0002f1b
b1001f3e59b0004558b0042f1bb1041f3e59b0004558b0062f1bb1060f3e59b0004558b00a2f1bb10a0f3e59b202010a2b5821d81bf459b003d0303113331121113311211123112192fd0281fdfe4bfd
fe3705b0fb1a04e6fa50fe98016800020090000004c105b0000d0016005bb2101718111239b01010b003d000b0004558b00c2f1bb10c1f3e59b0004558b00a2f1bb10a0f3e59b00c10b200010a2b5821
d81bf459b2020c0a111239b0022fb20e010a2b5821d81bf459b00a10b20f010a2b5821d81bf45930310121112132161615140407211121011121323635342627042cfd61012aa0ee7cfeebeffdd3039c
fd610129808f8c7c04e4fe9f6eca85ccf80205b0fd08fe128b736e80020000020024fe9a05dc05b0000e00140065b2121516111239b01210b00bd000b0004558b00b2f1bb10b1f3e59b0004558b0042f
1bb104173e59b0004558b0022f1bb1020f3e59b00410b001d0b00210b206010a2b5821d81bf459b00dd0b00ed0b00fd0b010d0b00b10b211010a2b5821d81bf459303101231121112303333612371321
113321211121030205cff0fc41f4087557680f260396b9fbdb0270fe57181bfe9a0166fe9a0230540141cb0286fb1a041afe66fe6500000100160000079b05b00015007d00b0004558b0092f1bb1091f
3e59b0004558b00d2f1bb10d1f3e59b0004558b0112f1bb1111f3e59b0004558b0022f1bb1020f3e59b0004558b0062f1bb1060f3e59b0004558b0142f1bb1140f3e59b2100902111239b0102fb20001
0a2b5821d81bf459b004d0b2081000111239b01010b00bd0b213001011123930310123112311230121010121013311331133012101012104ffa3fcaafe9bfec501d5fe4a0132015c9dfc9601590131fe
4e01d1fec60274fd8c0274fd8c030702a9fda00260fda00260fd59fcf70000010049ffed047f05c300290086b2252a2b11123900b0004558b00b2f1bb10b1f3e59b0004558b0172f1bb1170f3e59b00b
10b203010a2b5821d81bf459b2280b171112397cb0282f18b21028015db430284028025db460287028025db4a028b028025db2062803111239b225010a2b5821d81bf459b2112528111239b01710b21f
010a2b5821d81bf459b21c251f1112393031013426232206152334363633320415140607161615140423222626353314163332363534262323353320036c947f6d92fc84ea8dfa0115786c7a81fed4fa
9af97dfc9c7886a38f8aaba2010c04236274735b77ba67dac463a6302aab7fc4e76ebe7b5e817e657b6fc800000100940000050d05b00009004500b0004558b0002f1bb1001f3e59b0004558b0072f1b
b1071f3e59b0004558b0022f1bb1020f3e59b0004558b0052f1bb1050f3e59b2040002111239b20900021112393031013311231101231133110410fdfdfd81fdfd05b0fa50040dfbf305b0fbf2000001
002d0000050d05b00011004db204121311123900b0004558b0002f1bb1001f3e59b0004558b0012f1bb1010f3e59b0004558b0092f1bb1090f3e59b00010b203010a2b5821d81bf459b00910b20b010a
2b5821d81bf4593031011123112103020206232335373e023713050dfcfe421a0f63bc9e4028575f310a1c05b0fa5004e4fdcdfef8fedd86ca03086ad7d102c900010039ffeb04dd05b0000f0049b200
101111123900b0004558b00f2f1bb10f1f3e59b0004558b0062f1bb1060f3e59b2000f06111239b00f10b001d0b0012fb00610b20a010a2b5821d81bf459b20d060f1112393031010121010706232737
1633323737012102a001240119fe052e64e06802183d6c2c34fe0e011402b702f9fb485bb206c8045c7b04240003004fffc4061805ec0016001f00280055b20a292a111239b00a10b01ed0b00a10b020
d000b00a2fb0152fb214150a111239b0142fb000d0b20b0a15111239b00b2fb008d0b221010a2b5821d81bf459b01ed0b01410b21f010a2b5821d81bf459b020d0303101320412151402042315233523
2624023534122433353301220615141617331133113332363534262303aebb01169999feebbcf317a9feec989a0114bef3fefbaac1bbab17f311abbfbfad052698fef0acaafef197bebe0196010daaad
011297c6fe6fcfbcb4cd02030efcf2cfb6b9d00000010092fea105bd05b0000b003b00b0092fb0004558b0002f1bb1001f3e59b0004558b0042f1bb1041f3e59b0004558b00a2f1bb10a0f3e59b20201
0a2b5821d81bf459b006d0303113331121113311330323112192fd0281fdb014e8fbd105b0fb1a04e6fb1cfdd5015f000001008e000004ee05b00011003f00b0004558b0002f1bb1001f3e59b0004558
b0092f1bb1091f3e59b0004558b0012f1bb1010f3e59b20e0109111239b00e2fb205010a2b5821d81bf459303101112311062320242711331116163332371104eefca2b0fefbfef401fc017e97aea405
b0fa50023d29e6e801cefe308b762a02a700000100980000070305b0000b004800b0004558b0002f1bb1001f3e59b0004558b0032f1bb1031f3e59b0004558b0072f1bb1071f3e59b0004558b0092f1b
b1090f3e59b201010a2b5821d81bf459b005d0b006d03031011121113311211133112111019601bcfc01b9fcf99505b0fb1a04e6fb1a04e6fa5005b000010098fea207ad05b0000f005400b00b2fb000
4558b0002f1bb1001f3e59b0004558b0032f1bb1031f3e59b0004558b0072f1bb1071f3e59b0004558b00d2f1bb10d0f3e59b201010a2b5821d81bf459b005d0b006d0b009d0b00ad0b002d030310111
2111331121113311330323112111019601bcfc01b9fcaa14def9dd05b0fb1a04e6fb1a04e6fb12fde0015e05b00000020018000005d405b0000d0016005eb2011718111239b00110b00ed000b0004558
b0002f1bb1001f3e59b0004558b00a2f1bb10a0f3e59b202000a111239b0022fb00010b20c010a2b5821d81bf459b00210b20e010a2b5821d81bf459b00a10b20f010a2b5821d81bf459303113211121
32161615140407211121011121323635342627180287012aa0ee7dfee9eefdd4fe7502870129808f8c7c05b0fdd36ec986cdf70204edfdcbfe128b736e8002000003009b0000065805b0000b000f0018
006db202191a111239b00210b00dd0b00210b017d000b0004558b00b2f1bb10b1f3e59b0004558b00e2f1bb10e1f3e59b0004558b0082f1bb1080f3e59b0004558b00c2f1bb10c0f3e59b200080b1112
39b0002fb210010a2b5821d81bf459b00810b211010a2b5821d81bf4593031012132161615140407211133012311330111213236353426270198012aa0ee7cfeebeffdd3fd04c0fcfcfb400129808f8c
7c03836eca85ccf80205b0fa5005b0fd08fe128b736e800200020090000004c105b0000b0014004db20e1516111239b00e10b001d000b0004558b00b2f1bb10b1f3e59b0004558b0092f1bb1090f3e59
b200090b111239b0002fb20c010a2b5821d81bf459b00910b20d010a2b5821d81bf4593031012132161615140407211133111121323635342627018d012aa0ee7cfeebeffdd3fd0129808f8c7c03836e
ca85ccf80205b0fd08fe128b736e80020001006bffec04f105c4001f007fb203202111123900b0004558b0132f1bb1131f3e59b0004558b01c2f1bb11c0f3e59b209131c1112397cb0092f18b4600970
09025db4d009e009025db430094009025db200090171b206010a2b5821d81bf459b01c10b203010a2b5821d81bf459b2000603111239b01310b20c010a2b5821d81bf459b20f090c1112393031011616
333236372135212626232206072336003332041217151402042322002701681497939cab06fdfe020208b1a08c9512fc180125f2b3011093018ffef4b0f8fed41601d99e86e4d7ccd8e48c9eee0108a8
fec8cd7bcffec7a80105e800000200a0ffec070705c400170025007eb2122627111239b01210b01dd000b0004558b0132f1bb1131f3e59b0004558b00d2f1bb10d1f3e59b0004558b0042f1bb1040f3e
59b0004558b00a2f1bb10a0f3e59b20e0a0d1112397cb00e2f18b4600e700e025db208010a2b5821d81bf459b01310b21b010a2b5821d81bf459b00410b222010a2b5821d81bf4593031011402042322
2402272311231133113336122433320412170734022322020715141233321235070794feedb3a7fef89e0eb6fcfcb3069a010fadb201139601fdb7a8a4b902bba6a8b502b2d6febdad98011cbdfda305
b0fd71c90135a5abfebfd505f20102feffeb54f0fefa0100f600000200200000045f05b0000c00150061b2101617111239b01010b00ad000b0004558b00a2f1bb10a1f3e59b0004558b0002f1bb1000f
3e59b0004558b0032f1bb1030f3e59b2110a00111239b0112fb201010a2b5821d81bf459b2050111111239b00a10b212010a2b5821d81bf4593031211121012101261134243721110114163333112322
060362fee6fee7fef10145fe0113f601effd048a8aebeb8c880220fde0026b780111d1e902fa5003e97b8a0200860002005bffeb043c0613001a00260054b20e2728111239b00e10b01bd000b0004558
b0112f1bb111213e59b0004558b0072f1bb1070f3e59b2001107111239b0002fb2190007111239b21b010a2b5821d81bf459b00710b221010a2b5821d81bf45930310132121515140023220011351012
37363635331406060706060736172206151416333236353426027accf6fef5e5dffeeef8f68a51c44288a6989f1b91937686847a79858503fefeefea0ceafede0128010046015e0198331c3f36657e4f
2320a49195c39fa59caeafb08ca30003008f0000043a043a000e0015001c0078b2021d1e111239b00210b015d0b00210b017d000b0004558b0012f1bb1011b3e59b0004558b0002f1bb1000f3e59b216
01001112397cb0162f18b440165016025db4d016e016025db20f070a2b5821d81bf459b2080f16111239b00010b210010a2b5821d81bf459b00110b21b010a2b5821d81bf45930313311213216151406
0716161514062301112132353423253332353427238f01b7dee85d5b6a7cdfd1fef8010abbbefef9c8cfc4d3043a9b914b772016865b979e01cdfef38687ae7a8004000100850000034d043a0005002b
00b0004558b0042f1bb1041b3e59b0004558b0022f1bb1020f3e59b00410b200010a2b5821d81bf4593031012111231121034dfe2af202c80376fc8a043a00020027febe04c5043a000e0014005bb212
1516111239b01210b004d000b00c2fb0004558b0042f1bb1041b3e59b0004558b00a2f1bb10a0f3e59b200010a2b5821d81bf459b006d0b007d0b00c10b009d0b00710b00fd0b010d0b00410b211010a
2b5821d81bf4593031373636371321113311231121112313212111210702816545070e02ef96f2fd4af6010176019ffeef070ec271cb9e019efc88fdfc0142febe020402a7cffed60001001e0000065c
043a0015008200b0004558b0092f1bb1091b3e59b0004558b00d2f1bb10d1b3e59b0004558b0112f1bb1111b3e59b0004558b0022f1bb1020f3e59b0004558b0062f1bb1060f3e59b0004558b0142f1b
b1140f3e59b2101102111239b0102fb28f10015db200010a2b5821d81bf459b004d0b2081000111239b01010b00bd0b21300101112393031012311231123032101012113331133113313210101210435
81f380f9fed60167feac0129f572f373f60129fead0169fed201b3fe4d01b3fe4d02330207fe5701a9fe5701a9fdfcfdca000001004dffec03c4044d0027008db21e282911123900b0004558b0252f1b
b1251b3e59b0004558b0082f1bb1080f3e59b21925081112397cb0192f18b440195019025db4d019e019025db216070a2b5821d81bf459b2031619111239b00810b210070a2b5821d81bf459b20d1610
111239b4030d130d025db02510b21e070a2b5821d81bf459b221191e11123940090b211b212b213b21045d30310114060716151406232226263533141633323635342623233533363534262322061523
343633321603b0574fbaf2cb7ccc72f2765a59695c60aeb4a35e52506ef2f0b9c9e0031248792441ba95b1539969425953434f46af0284424a4f3c8fb7a40001008600000412043a0009004500b00045
58b0002f1bb1001b3e59b0004558b0072f1bb1071b3e59b0004558b0022f1bb1020f3e59b0004558b0052f1bb1050f3e59b2040702111239b20907021112393031013311231101231133110320f2f2fe
58f2f2043afbc602d2fd2e043afd2e000001008f00000465043a000c006800b0004558b0042f1bb1041b3e59b0004558b0082f1bb1081b3e59b0004558b0022f1bb1020f3e59b0004558b00b2f1bb10b
0f3e59b20602041112397cb0062f18b4d306e306025db443065306025db213060171b201010a2b5821d81bf459b20a010611123930310123112311331133012101012101fd7bf3f36b012b012cfe7901
a8fec401acfe54043afe5001b0fdfafdcc000001002100000414043a000f004db204101111123900b0004558b0002f1bb1001b3e59b0004558b0012f1bb1010f3e59b0004558b0082f1bb1080f3e59b0
0010b203010a2b5821d81bf459b00810b20a010a2b5821d81bf4593031011123112103020623232737363637130414f3fece1413abb04b013250490a14043afbc60376fe87fef0edca050bade501ce00
0001008f0000056f043a000c005900b0004558b0012f1bb1011b3e59b0004558b00b2f1bb10b1b3e59b0004558b0032f1bb1030f3e59b0004558b0062f1bb1060f3e59b0004558b0092f1bb1090f3e59
b2000b03111239b2050b03111239b2080b0311123930310101211123110123011123112102ff01400130f3fed6a5fed5f30132012b030ffbc602ccfd3402d0fd30043a000001008600000411043a000b
007e00b0004558b0062f1bb1061b3e59b0004558b00a2f1bb10a1b3e59b0004558b0002f1bb1000f3e59b0004558b0042f1bb1040f3e59b2090a00111239b0092fb4bf09cf09025db2bf090171b42f09
3f090272b25f090172b4ef09ff090271b41f092f090271b28f09015db48f099f090272b202010a2b5821d81bf45930312123112111231133112111330411f3fe5bf3f301a5f301b5fe4b043afe3d01c3
0001008600000412043a0007003800b0004558b0062f1bb1061b3e59b0004558b0002f1bb1000f3e59b0004558b0042f1bb1040f3e59b00610b202010a2b5821d81bf459303121231121112311210412
f3fe5af3038c0376fc8a043a00010023000003d0043a0007003100b0004558b0062f1bb1061b3e59b0004558b0022f1bb1020f3e59b00610b200010a2b5821d81bf459b004d0b005d030310121112311
21352103d0fea1f3fea503ad0379fc870379c10000030054fe60057f0600001a0024002f007fb2073031111239b00710b020d0b00710b02ad000b0062fb0004558b0032f1bb1031b3e59b0004558b00a
2f1bb10a1b3e59b0004558b0132f1bb113113e59b0004558b0102f1bb1100f3e59b0004558b0172f1bb1170f3e59b00a10b21e010a2b5821d81bf459b01010b223010a2b5821d81bf459b028d0b01e10
b02dd0303113101233321711331136333212111402232227112311062322022725342623220711163332011416333237112623220654d1bb4c3ef24056bad3d4b75345f23d4fafd1090437746a2d2521
33dcfcba6c6a2d21222a6870020e010901371c01cefe2e20fecbfee0f3fee61efe5601a61a0103e33cb6c70dfd3a0a014ba2a90a02c90ac100010086febf04a5043a000b003b00b0082fb0004558b000
2f1bb1001b3e59b0004558b0042f1bb1041b3e59b0004558b00a2f1bb10a0f3e59b202010a2b5821d81bf459b006d0303113331121113311330323112186f301a6f39314ddfcd2043afc880378fc88fd
fd0141000001005f000003e0043b00110048b204121311123900b0004558b0092f1bb1091b3e59b0004558b0102f1bb1101b3e59b0004558b0012f1bb1010f3e59b20d01091112397cb00d2f18b20401
0a2b5821d81bf459303121231106232226351133111416333237113303e0f35e68deeaf3696c6264f3016916d5c7014cfeb4766217020c000001008600000603043a000b004800b0004558b0002f1bb1
001b3e59b0004558b0032f1bb1031b3e59b0004558b0072f1bb1071b3e59b0004558b0092f1bb1090f3e59b201010a2b5821d81bf459b005d0b006d0303101112111331121113311211101790152f301
53f2fa83043afc880378fc880378fbc6043a0001007efebf06b4043a000f004b00b00c2fb0004558b0002f1bb1001b3e59b0004558b0032f1bb1031b3e59b0004558b0072f1bb1071b3e59b0004558b0
0d2f1bb10d0f3e59b201010a2b5821d81bf459b005d0b009d030310111211133112111331133032311211101710152f30153f2b914ddfabb043afc880378fc880378fc88fdfd0141043a0002001f0000
04ea043a000d0015005bb2001617111239b00ed000b0004558b00c2f1bb10c1b3e59b0004558b0082f1bb1080f3e59b2000c08111239b0002fb00c10b20a010a2b5821d81bf459b00010b20e010a2b58
21d81bf459b00810b20f010a2b5821d81bf459303101333216161514060721112135211111333236342627024aee85c667ecc4fe1dfec8022bed5967655602e25ca66ea7ca010376c4fde5fea359a45f
01000003008f000005c9043a000b000f0017006db2071819111239b00710b00dd0b00710b014d000b0004558b00a2f1bb10a1b3e59b0004558b00e2f1bb10e1b3e59b0004558b0082f1bb1080f3e59b0
004558b00c2f1bb10c0f3e59b2000e08111239b0002fb210010a2b5821d81bf459b00810b211010a2b5821d81bf45930310133321616151406072111330123113301113332363426270182ee85c667ec
c4fe1df30447f3f3fbb9ed5967655602e25ca66ea7ca01043afbc6043afde5fea359a45f01000002008f00000422043a000b0013004db20e1415111239b00e10b001d000b0004558b00a2f1bb10a1b3e
59b0004558b0082f1bb1080f3e59b2000a08111239b0002fb20c010a2b5821d81bf459b00810b20d010a2b5821d81bf459303101333216161514060721113311113332363426270182ee85c667ecc4fe
1df3ed5967655602e25ca66ea7ca01043afde5fea359a45f010000010051ffec03e8044e0020007db210212211123900b0004558b0082f1bb1081b3e59b0004558b0102f1bb1100f3e59b00810b20001
0a2b5821d81bf459b21e08101112397cb01e2f18b4401e501e025db2031e00111239b21c03015db20b03015db21b070a2b5821d81bf459b01010b218010a2b5821d81bf459b2151b18111239b4041514
15025d303101220615233436363332001515140606232226263533141633323637213521262602015576e574ca72dc010b79dc917bc86ee57656667e0cfeac01530e7e038b694f64af68fed2fc199bfc
8867ba755d779989a8848f0000020091ffec0638044e0014001f0085b2152021111239b01510b00dd000b0004558b0042f1bb1041b3e59b0004558b0132f1bb1131b3e59b0004558b0112f1bb1110f3e
59b0004558b00c2f1bb10c0f3e59b20111131112397cb0012f18b4d001e001025db440015001025db20f010a2b5821d81bf459b00c10b217010a2b5821d81bf459b00410b21d010a2b5821d81bf45930
3101333624333200171714060623220027231123113301141632363534262322060184cc1b010acbdb01110b017be596d2fef315caf3f301b98af6888d78778c0287cff8fee6e939a0fc8a0104d4fe3c
043afdd8a7bdc0b9a7bdbd0000020027000003df043a000d00160061b2141718111239b01410b004d000b0004558b0002f1bb1001b3e59b0004558b0012f1bb1010f3e59b0004558b0052f1bb1050f3e
59b2120001111239b0122fb203010a2b5821d81bf459b2070312111239b00010b213010a2b5821d81bf4593031011123112303231326263534363703141633331123220603dff2e3e7fcff646be9c6bc
654fefe0596a043afbc6018dfe7301b52a9c6597c102fea0445501385a000001ffdbfe4b03f806000021008bb215222311123900b01e2fb0004558b0042f1bb1041b3e59b0004558b00a2f1bb10a113e
59b0004558b0182f1bb1180f3e59b69f1eaf1ebf1e035db22f1e015db20f1e015db221181e111239b0212fb200070a2b5821d81bf459b2021804111239b00a10b20f010a2b5821d81bf459b00410b215
010a2b5821d81bf459b00010b01ad0b02110b01cd03031012115363320131114062322273716333235113426232207112311233533353315210277fef577b6015a05b9a6463a0f273b7b615e9248f39e
9ef3010b04ade98afe75fcfeb2c411bf0dbf02ed705d82fcfb04adaba8a800010054ffec03f9044e001d007ab2161e1f11123900b0004558b00f2f1bb10f1b3e59b0004558b0082f1bb1080f3e59b200
010a2b5821d81bf459b2190f081112397cb0192f18b41f192f190271b21b070a2b5821d81bf459b203001b111239b404031403025db00f10b216010a2b5821d81bf459b2131916111239b21c13015db2
0b13015d303125323637330e0223220011353400333216172326262322060721152112023e597806e40378ca74e4fef80108e4c0f504e407765b6e7d0a015bfea619ae685066b0640127010219f70129
e2b66075948da8feec000002001e0000069a043a0016001f0079b2092021111239b00910b017d000b0004558b0002f1bb1001b3e59b0004558b0082f1bb1080f3e59b0004558b00f2f1bb10f0f3e59b2
010008111239b0012fb00010b20a010a2b5821d81bf459b00f10b211010a2b5821d81bf459b00110b217010a2b5821d81bf459b00810b218010a2b5821d81bf459303101113316161514060721112103
0206072327373636371301113332363534262703faf8c3e5e9c3fe19fee61513a8af4e023252470a1402f3ed58686456043afe8703bc9fa0c1020376fe87fef2ee01ca050bafe301cefdc5fec1584d48
510100020086000006b1043a0012001b0082b2011c1d111239b00110b013d000b0004558b0022f1bb1021b3e59b0004558b0112f1bb1111b3e59b0004558b00b2f1bb10b0f3e59b0004558b00f2f1bb1
0f0f3e59b201110b111239b0012fb204110b111239b0042fb00110b20d010a2b5821d81bf459b00410b213010a2b5821d81bf459b00b10b214010a2b5821d81bf4593031012111331133161615140607
21112111231133011133323635342623017901a5f3f8c3e5e9c3fe19fe5bf3f30298ed5a66645b029f019bfe8703bc9fa0c10201ddfe23043afdc5fec15a4b4654000001ffee000003f8060000180079
b20c191a11123900b0152fb0004558b0042f1bb1041b3e59b0004558b0072f1bb1070f3e59b0004558b00f2f1bb10f0f3e59b2bf15015db22f15015db20f15015db2180f15111239b0182fb200070a2b
5821d81bf459b2020407111239b00410b20c010a2b5821d81bf459b00010b011d0b01810b013d0303101211536332013112311342623220711231123353335331521028bfee177b6015a05f3615e9248
f38b8bf3011f04b5f18afe75fd3d02ba705d82fcfb04b5aaa1a100010086fe9a0412043a000b004500b0082fb0004558b0002f1bb1001b3e59b0004558b0032f1bb1031b3e59b0004558b0052f1bb105
0f3e59b0004558b0092f1bb1090f3e59b201010a2b5821d81bf4593031011121113311211123112111017901a6f3feb5f3feb2043afc880378fbc6fe9a0166043a0000010088ffeb06c105b0001e0060
b2061f2011123900b0004558b0002f1bb1001f3e59b0004558b00c2f1bb10c1f3e59b0004558b0152f1bb1151f3e59b0004558b0042f1bb1040f3e59b0004558b0082f1bb1080f3e59b2060004111239
b211010a2b5821d81bf459b01ad030310111140623222706232226351133111416333236351121111416333236351106c1f9d2e56d71e9cff3fd675e697201016d63616e05b0fbffd6eea5a5efd50401
fbfc758281770403fbfc74837f79040300010070ffeb05ed043a001e0060b2061f2011123900b0004558b0002f1bb1001b3e59b0004558b00c2f1bb10c1b3e59b0004558b0152f1bb1151b3e59b00045
58b0042f1bb1040f3e59b0004558b0082f1bb1080f3e59b2061504111239b211010a2b5821d81bf459b01ad030310111060623222706232226351133111416333236351133111416333236351105ed01
dabdc76066cbb8d5f354465366f45c4f4a5b043afd4ec1dc8e8eddc302affd51726c6c7202affd51726c6c7202af0002ffe00000042106180012001b0071b2151c1d111239b01510b003d000b0004558
b00f2f1bb10f213e59b0004558b0092f1bb1090f3e59b2120f09111239b0122fb200070a2b5821d81bf459b2020f09111239b0022fb00010b00bd0b01210b00dd0b00210b213010a2b5821d81bf459b0
0910b214010a2b5821d81bf45930310121113316161514060721112335331133112101113332363534262702a3fedef7c4e5e5c0fe12aeaef30122fedeed5b656357043afec903ceaeadd304043aab01
33fecdfd5bfe82655955690200010098ffed06cd05c50025008eb20e262711123900b0004558b0242f1bb1241f3e59b0004558b0052f1bb1051f3e59b0004558b01c2f1bb11c0f3e59b0004558b0222f
1bb1220f3e59b2002224111239b0002fb21f000171b208241c111239b00510b20c010a2b5821d81bf459b00010b00fd0b00010b221010a2b5821d81bf459b012d0b01c10b215010a2b5821d81bf459b2
18241c111239303101333612243332001723262623220607211521161633323637330600232224022723112311330194b50b960109abf1012618fc12938ea1ab0b01e9fe1602a8a2959614fc16fed3f8
acfef89303b4fcfc034fbe011d9bfefaef9d8bddccc3e1f2869ce9fefba10134cafd7405b00000010086ffec05ba044e00230092b20d242511123900b0004558b0042f1bb1041b3e59b0004558b0232f
1bb1231b3e59b0004558b01b2f1bb11b0f3e59b0004558b0202f1bb1200f3e59b20e041b1112397cb00e2f18b4400e500e025db000d0b00410b20b010a2b5821d81bf459b2080e0b111239b00e10b20f
070a2b5821d81bf459b01b10b213010a2b5821d81bf459b216130f111239b00f10b01ed030310133362433321617232626232203211521161633323637330e0223222427231123113301799d140104d2
c1f504e407765bdb1a017cfe850a7d6e597806e40378ca74d3fefd149ef3f30271deffe2b66075fee6ab8a8e685066b064fedcfe3a043a000002001c0000051705b0000b000e005600b0004558b0082f
1bb1081f3e59b0004558b0022f1bb1020f3e59b0004558b0062f1bb1060f3e59b0004558b00a2f1bb10a0f3e59b20d0802111239b00d2fb200010a2b5821d81bf459b004d0b20e080211123930310123
1123112303210133012101210303837ee1738ffefa0206f50200fefafde00153a801aafe5601aafe5605b0fa50026801f8000002000a00000445043a000b0010005600b0004558b0082f1bb1081b3e59
b0004558b0022f1bb1020f3e59b0004558b0062f1bb1060f3e59b0004558b00a2f1bb10a0f3e59b20d0208111239b00d2fb201010a2b5821d81bf459b004d0b20f080211123930310123112311230323
01330123013303270702e45dc35b68f701a9e701abf7fe5cf86419190117fee90117fee9043afbc601c401066464000200ac0000073005b000130016007c00b0004558b0022f1bb1021f3e59b0004558
b0122f1bb1121f3e59b0004558b0042f1bb1040f3e59b0004558b0082f1bb1080f3e59b0004558b00c2f1bb10c0f3e59b0004558b0102f1bb1100f3e59b2150204111239b0152fb000d0b01510b20601
0a2b5821d81bf459b00ad0b00610b00ed0b21602041112393031012101330121032311231123032113211123113301210301a80168012bf50200fefa8e7ee2728ffefa98fedbfcfc02620153a9026703
49fa5001aafe5601aafe5601abfe5505b0fcb801f9000002009d00000618043a00130018007f00b0004558b0022f1bb1021b3e59b0004558b0122f1bb1121b3e59b0004558b0042f1bb1040f3e59b000
4558b0082f1bb1080f3e59b0004558b00c2f1bb10c0f3e59b0004558b0102f1bb1100f3e59b2001012111239b0002fb001d0b20e010a2b5821d81bf459b00bd0b007d0b00110b014d0b015d0b2171204
1112393031013313330123032311231123032313231123113301330327070190fef8e701abf76a5dc35b68f76dbaf3f301edf864191901c40276fbc60117fee90117fee90117fee9043afd8a01066464
000200800000066e05b0001a001d007ab21b1e1f111239b01b10b00dd000b0004558b0192f1bb1191f3e59b0004558b0042f1bb1040f3e59b0004558b00c2f1bb10c0f3e59b0004558b0132f1bb1130f
3e59b2001904111239b0002fb209010a2b5821d81bf459b00ed0b00fd0b00010b018d0b21b1904111239b01910b21c010a2b5821d81bf459303101161617112311262623230711231123220607112311
3636210121011321047afef105fc02768f6806fc7e8f7503fc03fa010ffe8504e4fd8ee9fe2f032804d9d8fe8d016c816f0bfdaf025c6e7efe90016ce1db0288fd8a01a90002008200000564043a001a
001d007ab21b1e1f111239b01b10b014d000b0004558b0052f1bb1051b3e59b0004558b0002f1bb1000f3e59b0004558b00b2f1bb10b0f3e59b0004558b0132f1bb1130f3e59b2040500111239b0042f
b007d0b00410b210070a2b5821d81bf459b015d0b016d0b21b0500111239b00510b21c010a2b5821d81bf45930313335363637012101161617152335262627230711231123220607150113218202c5cc
feeb03f4feeac6be02f3015e722f01f22d796003018595fed6b2ced20d01dbfe2411d3c7b3b17f720203fe5f01a46e7cba0269012200000200a3000008b305b0002000230097b21c2425111239b01c10
b023d000b0004558b0072f1bb1071f3e59b0004558b00b2f1bb10b1f3e59b0004558b0002f1bb1000f3e59b0004558b0052f1bb1050f3e59b0004558b0112f1bb1110f3e59b0004558b0192f1bb1190f
3e59b2090700111239b0092fb203010a2b5821d81bf459b00910b00dd0b00310b01cd0b017d0b2210700111239b00b10b222010a2b5821d81bf459303121113437211123113311210121011616171123
112626232307112311232206071101132102c53bfe9ffcfc0330fe8704e5fe84fef105fc02768f6805fc7f9173030208e9fe2e0160a165fd9a05b0fd7b0285fd7804d9d8fe8d016c816f09fdad025c71
7cfe91033901aa000002008f00000776043a002000230097b21d2425111239b01d10b023d000b0004558b0072f1bb1071b3e59b0004558b00b2f1bb10b1b3e59b0004558b0002f1bb1000f3e59b00045
58b0052f1bb1050f3e59b0004558b0112f1bb1110f3e59b0004558b0192f1bb1190f3e59b2090b00111239b0092fb203070a2b5821d81bf459b00910b00dd0b00310b01cd0b017d0b2210b00111239b0
0b10b222010a2b5821d81bf459303121353637211123113311210121011616171523352626272307112311232206071501132102950135feb7f3f302a5feec03f4feeac5be02f2015e732e01f22d7960
03018595fed6b09464fe58043afe2701d9fe2411d4c6b3b17f720203fe5f01a46e7cba026901220000020028fe4003aa07880027003000a7b2023132111239b00210b028d000b02c2fb0004558b0052f
1bb1051f3e59b0004558b0172f1bb117113e59b0004558b0112f1bb1110f3e59b00510b203010a2b5821d81bf459b22605111112397cb0262f18b21026015db24026015db460267026025db223010a2b
5821d81bf459b20c2326111239b01110b21d010a2b5821d81bf459b20f2c015db02c10b029d0b0292fb40f291f29025db2282c29111239b030d0b0302f30310134262321352132041514060704151404
23230615141707262627343637333636353421233533200337331501230135330296857afee50115ed010b7d6e010cfef7e8357a985284a202b1a43f7289fecf898901109493cffeea97feebce04215e
6ac7cfb570a32c57fec5e803636b419928b77f868b02017d65f3c7039f9b0afee901180900020033fe480388061c002700300095b2023132111239b00210b028d000b02c2fb0004558b0052f1bb1051b
3e59b0004558b0172f1bb117113e59b0004558b0122f1bb1120f3e59b00510b204010a2b5821d81bf459b22512051112397cb0252f18b440255025025db224070a2b5821d81bf459b20c2425111239b0
1210b21d010a2b5821d81bf459b02c10b029d0b0292fb40f291f29025db228292c111239b030d03031013426232135213216151406071615140623230615141707262627343637333236353421233533
3203373315012301353302747369fee40117dcf86157d9f6d0367e9051829602a9a1356c77fef99195e2a092d0fee996feebcd02fe3c47b9a58d4f772442ac96af04626b419130b6707d8701503f94a9
03129b0bfeea01170a000003005fffec051705c400100017001e0066b2041f20111239b00410b011d0b00410b018d000b0004558b00c2f1bb10c1f3e59b0004558b0042f1bb1040f3e59b00c10b21101
0a2b5821d81bf459b214040c1112397cb0142f18b00410b218010a2b5821d81bf459b01410b21c070a2b5821d81bf4593031011402042322240227353412242004121701220607212626033236372116
16051794feedb3b0feee9903960114016401139601fda4a0b60802bc08b4a09fb30afd440ab802b2d6febdadaa013ccd5dd50144afabfebfd501eff0d9dbeefbcae5ded9ea000003004fffec043d044e
000f0016001d0067b2041e1f111239b00410b010d0b00410b017d000b0004558b0042f1bb1041b3e59b0004558b00c2f1bb10c0f3e59b210010a2b5821d81bf459b21b040c1112397cb01b2f18b4401b
501b025db213070a2b5821d81bf459b00410b217010a2b5821d81bf45930311334363633320017171406062322001101323637211616132206072126264f7de494da01130b017be795e3feec01f76b85
10fdff10846b6a8510020010850227a1fd89fee7ea39a0fc8a012e0101fe939289889302dd958282950000010010000004f305c2000f0046b202101111123900b0004558b0062f1bb1061f3e59b00045
58b00f2f1bb10f1f3e59b0004558b00c2f1bb10c0f3e59b2010c0f111239b00610b208010a2b5821d81bf45930310117371336363317072306070123012102611b1be4359c7a2d02185427fe98f4fe0e
010d018b726f02f7ac9701d7027cfb9405b00001002000000418044e00110046b202121311123900b0004558b0052f1bb1051b3e59b0004558b0112f1bb1111b3e59b0004558b00e2f1bb10e0f3e59b2
01050e111239b00510b20a010a2b5821d81bf459303101173713123332170726232206070123013301e314147a5acf4327170c20223b0dfef6d3fe92fb016e616101be012216c006362afce2043a0002
005fff760517062e001300270055b2052829111239b00510b021d000b0004558b00d2f1bb10d1f3e59b0004558b0032f1bb1030f3e59b006d0b00d10b010d0b00d10b21a010a2b5821d81bf459b017d0
b00310b224010a2b5821d81bf459b021d03031011000071523352600033510003735331516001127342627152335060615151416173533153636350517fef3e9c6e8feef030112e9c6ea010dfd8278c6
7985847bc6798002b2fedafe8b237e7e230173011d550124017a23717223fe86fed906cef523606123f5cf4cc7fd25605f23f6cf0002004fff88043d04b4001300250058b2032627111239b00310b014
d000b0004558b0032f1bb1031b3e59b0004558b0102f1bb1100f3e59b00310b006d0b01010b00dd0b01010b223010a2b5821d81bf459b014d0b00310b21d010a2b5821d81bf459b01ad0303113341237
353315161215151402071523352602350136363534262715233506061514161735334fddbdb8bfdddfbfb8bbdd0250525a5a50b84f58564fb80227da01261f6e6d1ffed8dd11dbfed91d6b6c1f0126dd
fea71eb59782b21f606021b29583ae21680000030088ffeb06b5073f002a003d004600bab2304748111239b03010b009d0b03010b045d000b0004558b0002f1bb1001f3e59b0004558b0122f1bb1121f
3e59b0004558b0072f1bb1070f3e59b0004558b00b2f1bb10b0f3e59b2090007111239b01210b213010a2b5821d81bf459b00b10b21a010a2b5821d81bf459b21e0b12111239b023d0b01310b02ad0b0
1210b036d0b0362fb02cd0b02c2fb22b080a2b5821d81bf459b02c10b032d0b0322fb239080a2b5821d81bf459b02c10b042d0b0422fb046d0b0462f3031013216171114062322270623222627113436
33152206151114163332363511331116163332363511342623131523222e022322151523353433321e0201363735331514060704f4cef201f1d0e37272e3cef004f3cf5f66665f6972f50171685f6666
5f6a21538abf30146886eb2546c96ffe294103a9603b05b0faddfdeaddfb9e9ef6d50220ddfdcc8e80fded808e81770182fe7973808e800213808e01e386234b0a681022dc0f4f1afe87523c68673178
1f0000030074ffeb05d105e3002a003d004600afb2094748111239b00910b03ad0b00910b046d000b0004558b0122f1bb1121b3e59b0004558b00b2f1bb10b0f3e59b01210b000d0b0002fb00b10b007
d0b209120b111239b01210b213010a2b5821d81bf459b00b10b21a010a2b5821d81bf459b21e0b12111239b023d0b01310b02ad0b01210b036d0b0362fb02dd0b02d2fb22b080a2b5821d81bf459b02d
10b032d0b0322fb239080a2b5821d81bf459b03610b041d0b0412fb046d0b0462f3031013216171514062322270623222627113436331522061515141633323637353315161633323635353426231315
23222e022322151523353433321e02013637353315140607043abadc01d4b5c56163c2b2d304dcbb495b5343505e01ec015e5142545b49bd24538ac12c156887eb2546c570fe304103a9603b0447e5cc
f8cce79191e0c50103cde7c3757cf57c75706acaca6a70757cf57c7501e786234c09681022dc0f4e1bfe85523c686731781f00020088ffeb06c10711001e0026007db2062728111239b00610b023d000
b0004558b00d2f1bb10d1f3e59b0004558b0082f1bb1080f3e59b004d0b206080d111239b00810b211010a2b5821d81bf459b00d10b015d0b0152fb01110b01ad0b01510b01ed0b01e2fb00d10b025d0
b0252fb026d0b0262fb220080a2b5821d81bf459b02610b023d0b0232f303101111406232227062322263511331114163332363511211114163332363511253521172115233506c1f9d2e56d71e9cff3
fd675e697201016d63616efc39035501fea6b505b0fbffd6eea5a5efd50401fbfc758281770403fbfc74837f790403e77a7a7f7f00020070ffeb05ed05b1001e00260089b2062728111239b00610b025
d000b0004558b00d2f1bb10d1b3e59b0004558b0152f1bb1151b3e59b0004558b01e2f1bb11e1b3e59b0004558b0042f1bb1040f3e59b0004558b0082f1bb1080f3e59b2060815111239b211010a2b58
21d81bf459b01ad0b00d10b025d0b0252fb01fd0b01f2fb220080a2b5821d81bf459b01f10b022d0b023d030310111060623222706232226351133111416333236351133111416333236351125352117
2115233505ed01dabdc76066cbb8d5f354465366f45c4f4a5bfc9d033804feb2b5043afd4ec1dc8e8eddc302affd51726c6c7202affd51726c6c7202affc7b7b7f7f00010066fe8c04b605c500180053
b217191a11123900b0004558b00a2f1bb10a1f3e59b0004558b0002f1bb100173e59b0004558b0022f1bb1020f3e59b00a10b00ed0b00a10b210010a2b5821d81bf459b00210b217010a2b5821d81bf4
593031012311260035113412243320001523102122061511141617330334fbd3ff008d0101a30100011ffcfedd8ca9a98a9ffe8c0166200147f90111af01189bfef7e90126dfbcfeedb6df010001005c
fe8903f3044e001a0053b2191b1c11123900b0004558b00a2f1bb10a1b3e59b0004558b0002f1bb100173e59b0004558b0022f1bb1020f3e59b00a10b00fd0b00a10b212010a2b5821d81bf459b00210
b219010a2b5821d81bf459303101231126023535343636333216161523342623220615151416173302d5f3b3d379db927cc66fe5745871827e7098fe89016a200123dc1c9bfc8967bb765b7abda81ba1
bb020001006d00000493053e0013001300b00e2fb0004558b0042f1bb1040f3e5930310105072503231325370513253705133303050725025b012148feddb5afe1fedf470125cafede490123b9ace401
254cfee001c1ac80aafec1018eab80ab0168ab82ab0146fe6bab7faa0001fc6604a2ff3905fd0007001100b0002fb203060a2b5821d81bf45930310115273721271715fd17b101022201b105207e01ee
6c01dc000001fc730517ff6d0615000f002e00b00b2fb007d0b0072fb200080a2b5821d81bf459b00b10b004d0b0042fb00b10b20c080a2b5821d81bf459303101321515233534232204072335333624
fe7fee886a36fee28b29277901180615dc2210687701860177000001fd7b0516fe7206600005000c00b0012fb005d0b0052f3031013533071707fd7bbd013b5205dc849670440001fda50516fe9c0660
0005000c00b0032fb000d0b0002f3031012737273315fdf7523b01bd0516447096840008fa24fec401bf05af000c001a002700350042004f005c006a007a00b0452fb0532fb0602fb0382fb0004558b0
022f1bb1021f3e59b209090a2b5821d81bf459b04510b010d0b04510b24c090a2b5821d81bf459b017d0b05310b01ed0b05310b25a090a2b5821d81bf459b025d0b06010b02bd0b06010b267090a2b58
21d81bf459b032d0b03810b23f090a2b5821d81bf45930310134363216152334262322061501343633321615233426232206151334363332161523342622061501343633321615233426232206150134
363216152334262322061501343632161523342623220615013436333216152334262206151334363332161523342623220615fd1173be747033302e3301de745d5f7571352e2c3348755d5f7470355c
33fecb745d5f7470352e2d33fd4f73be747033302e33fd4d74be747033302e33fede755d5f7470355c3335755d5f7571352e2d3304f3546868542e373530feeb5468675531343530fe09556768543134
372efdf9546868543134372efee4546868542e37372e051a546868542e373530fe09556768543134372efdf955676755313435300008fa4dfe63018c05c600040009000e00130018001d00220027002f
00b0212fb0162fb0122fb00b2fb01b2fb0262fb0004558b0072f1bb1071f3e59b0004558b0022f1bb102113e593031051703231303271333030137051525050725350501372517050107052725032703
37130117130703fe500b7a60463a0c7a6046021d0d014dfea6fb750dfeb3015a039c02014044fedbfcf302fec04501262b119441c60360119442c43c0efead016104a20e0152fea0fe110c7c62473b0c
7c624701ae109944c8fc8e119945c802e402014645fed5fce302febb47012b000002ffe00000042106620012001b0074b2151c1d111239b01510b003d000b0004558b00d2f1bb10d1f3e59b0004558b0
112f1bb1111f3e59b0004558b0092f1bb1090f3e59b01110b200070a2b5821d81bf459b2020d09111239b0022fb00010b00bd0b00cd0b00210b213010a2b5821d81bf459b00910b214010a2b5821d81b
f45930310121113316161514060721112335333533152101113332363534262702a3fedef7c4e5e5c0fe12aeaef30122fedeed5b6563570505fdfe03ceaeadd3040505abb2b2fc90fe82655955690200
00020094000004d905b0000e001b004db2041c1d111239b00410b017d000b0004558b0032f1bb1031f3e59b0004558b0012f1bb1010f3e59b2160301111239b0162fb200010a2b5821d81bf459b00310
b214010a2b5821d81bf4593031011123112132041514071707270623133635342627211121323727370191fd022df4011f757a6d8879aaf91c907efec901304f3a736e021dfde305b0fed1c177876496
370143354a768d02fe04168064000002007cfe600430044e00130022006eb2172324111239b01710b010d000b0004558b0102f1bb1101b3e59b0004558b00d2f1bb10d1b3e59b0004558b00a2f1bb10a
113e59b0004558b0072f1bb1070f3e59b2091007111239b20e1007111239b01010b217010a2b5821d81bf459b00710b21c010a2b5821d81bf45930310114071707270623222711231133173633321211
27342623220711163332372737173604306e6a6f685970b26bf3e00a6bb8c6e1f281789541429646326a6e59220212f4977a63783675fdff05da6e82fed9fefa06a2be7bfe207e217b6467580001008f
00000434071000070032b201080911123900b0004558b0042f1bb1041f3e59b0004558b0022f1bb1020f3e59b00410b200010a2b5821d81bf459303101211123112111330434fd58fd02b2f304e4fb1c
05b001600001007e0000035b05730007002b00b0004558b0042f1bb1041b3e59b0004558b0022f1bb1020f3e59b00410b200010a2b5821d81bf45930310121112311211133035bfe16f301ebf20376fc
8a043a0139000001009bfec6049d05b00014005bb20f151611123900b0092fb0004558b0132f1bb1131f3e59b0004558b0112f1bb1110f3e59b01310b200010a2b5821d81bf459b2031309111239b003
2fb00910b20a070a2b5821d81bf459b00310b20f010a2b5821d81bf45930310121113320001110002327323635022523112311210437fd60a80122013cfef6f301838802feabbcfc039c04e4fe5ffecd
feecfef4fed6bab3c2017b09fd8705b00001007efee203db043a0015004ab20b161711123900b00a2fb0004558b0142f1bb1141b3e59b0004558b0122f1bb1120f3e59b01410b200010a2b5821d81bf4
59b203140a111239b0032fb210010a2b5821d81bf4593031012115332000151406060727363534262323112311210346fe2b49010101205eab7355de9b8e4ef302c80376e5fefadd60c28d1dae4ad481
97fe3a043a00000100900000053605b00014006100b0004558b0002f1bb1001f3e59b0004558b00c2f1bb10c1f3e59b0004558b0022f1bb1020f3e59b0004558b00a2f1bb10a0f3e59b20f0a0c111239
b00f2fb29f0f015db208010a2b5821d81bf459b201080f111239b005d0b00f10b012d030310902210123152335231123113311333533153301050dfe7c01adfec1fed341a359fdfd59a337011b05b0fd
5bfcf5026de9e9fd9305b0fd9afefe0266000001008e000004ae043a0014005c00b0004558b00d2f1bb10d1b3e59b0004558b0142f1bb1141b3e59b0004558b00a2f1bb10a0f3e59b0004558b0032f1b
b1030f3e59b20e0a0d111239b00e2fb209010a2b5821d81bf459b201090e111239b005d0b00e10b012d0303109022103231523352311231133113335331533130494fec40156fecbd82f9b57f2f2579b
27cf043afdfefdc801acb2b2fe54043afe50c7c701b000010034000006a205b0000e006100b0004558b0062f1bb1061f3e59b0004558b00a2f1bb10a1f3e59b0004558b0022f1bb1020f3e59b0004558
b00d2f1bb10d0f3e59b2080602111239b0082fb201010a2b5821d81bf459b00610b204010a2b5821d81bf459b20c0108111239303101231123112135211133012101012103b6adfcfe2702d58b01ad01
36fe0c021ffed00270fd9004ecc4fd9c0264fd47fd090001003d000005a8043a000e006b00b0004558b0062f1bb1061b3e59b0004558b00a2f1bb10a1b3e59b0004558b0022f1bb1020f3e59b0004558
b00d2f1bb10d0f3e59b2090a02111239b0092fb22f090171b28c09015db200010a2b5821d81bf459b00610b204010a2b5821d81bf459b20c000911123930310123112311213521113301210101210340
7bf2fe6a02886c012a012dfe7801a8fec501acfe540376c4fe5001b0fdf9fdcd000100940000078305b0000d008700b0004558b0022f1bb1021f3e59b0004558b00c2f1bb10c1f3e59b0004558b0062f
1bb1060f3e59b0004558b00a2f1bb10a0f3e59b2010206111239b0012fb29f01015db26f010171b2df010171b20f010172b29f010171b23f010171b42f013f010272b27c01015db00210b204010a2b58
21d81bf459b00110b208010a2b5821d81bf459303101211121152111231121112311330191028b0367fd95fcfd75fdfd0352025ec3fb130287fd7905b0000001007e00000566043a000d006600b00045
58b0022f1bb1021b3e59b0004558b00c2f1bb10c1b3e59b0004558b0062f1bb1060f3e59b0004558b00a2f1bb10a0f3e59b2010c061112397cb0012f18b440015001025db00210b204010a2b5821d81b
f459b00110b208010a2b5821d81bf45930310121112115211123112111231133017101a50250fea3f3fe5bf3f3027701c3c4fc8a01b5fe4b043a0001009bfec407ef05b000160068b210171811123900
b0072fb0004558b0152f1bb1151f3e59b0004558b0132f1bb1130f3e59b0004558b0102f1bb1100f3e59b2011507111239b0012fb00710b208070a2b5821d81bf459b00110b20d010a2b5821d81bf459
b01510b211010a2b5821d81bf4593031013320001110002327323635022523112311211123112105147d0122013cfef6f301838802feab91fcfd7ffc04790341fecdfeecfef4fed6bab3c2017b09fd89
04e4fb1c05b00001007efee606ba043a00180057b212191a11123900b0082fb0004558b0172f1bb1171b3e59b0004558b0152f1bb1150f3e59b0004558b0122f1bb1120f3e59b2011708111239b0012f
b20f010a2b5821d81bf459b01710b213010a2b5821d81bf459303101332000151406060727363635342623231123112111231121040a7d0107012c5dab73557569a59a7ff3fe5af3038c0294fefbde61
bf8e1dad288f678297fe360376fc8a043a0000020067ffeb05d705c5002500320085b2163334111239b01610b026d000b0004558b00d2f1bb10d1f3e59b0004558b01d2f1bb11d1f3e59b0004558b004
2f1bb1040f3e59b000d0b0002fb202041d111239b0022fb00d10b20e010a2b5821d81bf459b00410b215010a2b5821d81bf459b00010b225010a2b5821d81bf459b00210b029d0b01d10b22f010a2b58
21d81bf459303105222706232224022735341236331522061515141233323726113534123332121115100716330114161736113534262322061505d7dfb394b7bbfed4a9037de18c667edbb23129e2ed
b8c2f3bb5c6afd8e6563a26058545e154747ae0136bfc9af011ea1d4e1bdb8d7fef907cb0144cbf00135febffefac6fedaca14021984d5488f0109d5aeabafa100020061ffeb04c9044e0022002e008c
b2042f30111239b00410b023d000b0004558b00b2f1bb10b1b3e59b0004558b01a2f1bb11a1b3e59b0004558b0042f1bb1040f3e59b0004558b0002f1bb1000f3e59b202041a111239b0022fb00b10b2
0c010a2b5821d81bf459b00410b213010a2b5821d81bf459b00010b222030a2b5821d81bf459b00210b025d0b01a10b22b010a2b5821d81bf45930310522270623220011353412331506061515141633
37263535343633321615151407163301141736353534262322061504c9ba937a90e5fed4dbaa404b9a7d258fb69496bd814d58fe0e78633d31323b1236390142010442cf010cca04947b49a6cc0295e2
7abbeaffcd77d39411018faa6c63a97b6b87786a0001002dfea106b705b0000f004f00b00d2fb0004558b0082f1bb1081f3e59b0004558b0022f1bb1021f3e59b0004558b00e2f1bb10e0f3e59b00210
b200010a2b5821d81bf459b005d0b00e10b206010a2b5821d81bf459b00ad0303101213521152111211133113303231121018dfea003befe9f0281fcb014e7fbd104ecc4c4fbde04e6fb1cfdd5015f00
00010026febf053a043a000f004b00b00d2fb0004558b0032f1bb1031b3e59b0004558b00f2f1bb10f0f3e59b00310b204010a2b5821d81bf459b000d0b00f10b206010a2b5821d81bf459b00310b008
d0b00610b00ad0303101233521152311211133113303231121011bf502c3db01a6f39314ddfcd20377c3c3fd4b0378fc88fdfd01410000010080000004e105b00018004fb205191a11123900b0004558
b0002f1bb1001f3e59b0004558b00b2f1bb10b1f3e59b0004558b00e2f1bb10e0f3e59b2050e00111239b0052fb008d0b00510b214010a2b5821d81bf459b011d0303101111617161711331136371133
112311060715233526262711017d024f356ea36c64fdfd6070a3f6fa0105b0fe2c98392705012bfedc0a1902a7fa50023c180aebe506eadf01cd00010074000003f5043b00160051b206171811123900
b0004558b0152f1bb1151b3e59b0004558b00c2f1bb10c1b3e59b0004558b0012f1bb1010f3e59b20f010c1112397cb00f2f18b207010a2b5821d81bf459b004d0b00f10b012d0303121231106071523
3526262711331116171133113637113303f5f34531a3b6be01f20182a33b3bf301690e058a8b13d0b10150feb0ac1f010bfeef060e020c0000010085000004e505b000110046b205121311123900b000
4558b0012f1bb1011f3e59b0004558b0002f1bb1000f3e59b0004558b0092f1bb1090f3e59b2050100111239b0052fb20e010a2b5821d81bf459303133113311363320041711231126262322071185fc
a0b20105010c01fc017e97aea405b0fdc329e6e9fe3301d08b762afd590000020016ffe905bc05c4001c00240064b2162526111239b01610b023d000b0004558b00e2f1bb10e1f3e59b0004558b0002f
1bb1000f3e59b21e000e111239b01e2fb212010a2b5821d81bf459b004d0b01e10b00ad0b00010b217010a2b5821d81bf459b00e10b222010a2b5821d81bf45930310520001135262635331417341224
172000111521151416333237170606012135342623220603dcfed2feaa9ba7b58d9401089e01080122fc98cbbdb1ac3143d8fe05026c9a948eb0170154012b3c18d4aab62aae011ca001fe9cfeb98435
cad746c5282e036c1fb8c0dd0002ffcbffec048b044e001a0021008cb2202223111239b02010b014d000b0004558b00d2f1bb10d1b3e59b0004558b0002f1bb1000f3e59b21c000d111239b01c2fb4bf
1ccf1c025db45f1c6f1c0271b41f1c2f1c0271b28f1c015db4ef1cff1c0271b211070a2b5821d81bf459b004d0b01c10b00ad0b00010b215010a2b5821d81bf459b217000d111239b00d10b220010a2b
5821d81bf45930310522242727262635331417362433321211152116163332371706060121352626220602d8d4fee614038286a9681f0107bbddf1fd3d0b9d77a8678441dafe6d01cf0872ca7a14fbd1
321dc1939530c5f3fee6fefe62869c877d616b0296127a7d8c0000010090febf04ed05b000160066b215171811123900b0102fb0004558b0042f1bb1041f3e59b0004558b0082f1bb1081f3e59b00045
58b0022f1bb1020f3e59b20704021112397cb0072f18b400071007025db00ad0b01010b211010a2b5821d81bf459b00710b216010a2b5821d81bf4593031012311231133113301210116001510002327
2011022521019508fdfd7101b20132fe22e90100fef0f401010902feaefef80271fd8f05b0fda4025cfd8a1ffed7f9fef3fed3c2016f017a06000001008efeea0443043a00160059b20d171811123900
b0072fb0004558b0112f1bb1111b3e59b0004558b0152f1bb1151b3e59b0004558b00f2f1bb10f0f3e59b214150f1112397cb0142f18b440145014025db20e010a2b5821d81bf459b200140e11123930
31011616151406060727362734262723112311331133012102cdafbc5eaa7355e0028d8baef2f2550141012d026129e3ad60ba881cad47ca768509fe54043afe5001b0000001009bfe4b051305b00014
0074b20a151611123900b0004558b0002f1bb1001f3e59b0004558b0032f1bb1031f3e59b0004558b0122f1bb1120f3e59b0004558b0082f1bb108113e59b20200121112397cb0022f18b46002700202
5db430024002025db00810b20d010a2b5821d81bf459b00210b210010a2b5821d81bf45930310111211133111406232227371633323511211123110197027ffdbea9453c0e243e7bfd81fc05b0fd8302
7dfa18b7c611c70cba0298fd9705b0000001007efe4b0409043a0014006db20b151611123900b0004558b0002f1bb1001b3e59b0004558b0032f1bb1031b3e59b0004558b0122f1bb1120f3e59b00045
58b0082f1bb108113e59b20203121112397cb0022f18b440025002025db00810b20d010a2b5821d81bf459b00210b210010a2b5821d81bf4593031011121113311060623222737163332351121112311
017101a5f301baa6453a0f273b7cfe5bf3043afe3d01c3fb85b3c111bf0dc001e7fe4b043a0000020051ffeb051e05c40016001e005eb2081f20111239b00810b017d000b0004558b0002f1bb1001f3e
59b0004558b0082f1bb1080f3e59b20d0008111239b00d2fb00010b210010a2b5821d81bf459b00810b217010a2b5821d81bf459b00d10b21a010a2b5821d81bf4593031012000111514020427200011
3521262623220707273736013236372115141602710140016da0fee3a9fedcfebd03d005dfcca79734311ba6012996be12fd2fba05c4fe8cfeb66bc1fec2b1010160014989e0f03413c60d4afafcdabd
1fb9bf000001005bffeb044b05b0001b006bb20b1c1d11123900b0004558b0022f1bb1021f3e59b0004558b00b2f1bb10b0f3e59b00210b200010a2b5821d81bf459b2040200111239b21b0b02111239
7cb01b2f18b005d0b2100b02111239b00b10b213010a2b5821d81bf459b01b10b219070a2b5821d81bf45930310121352117011616151404232226263533141633323635342623233502fffd92039101
fe86c8dafee5ea8be27efc8768799099918c04e4cca3fe4f18eac2c5e867bf835f807f649485ac000001005dfe750446043a001b005cb20b1c1d11123900b00b2fb0004558b0022f1bb1021b3e59b200
010a2b5821d81bf459b2040002111239b21b0b02111239b01b2fb005d0b2100b02111239b00b10b213010a2b5821d81bf459b01b10b219070a2b5821d81bf45930310121352117011616151404232226
263533141633323635342623233502f4fd9b038c01fe88cbd7feeaeb89e47bf3896c7a949a938f0376c49bfe4319e9bfc2ea68bf81608580699683abffff0034fe4b048905b0002600b05200002601de
a429000701af01350000ffff002dfe4903a2043a002600eb5500002701deff9dff7a000701af010bfffe000200520000048305b0000b00140050b2041516111239b00410b00ed000b0004558b0012f1b
b1011f3e59b0004558b0032f1bb1030f3e59b2000103111239b0002fb00310b20c010a2b5821d81bf459b00010b20d010a2b5821d81bf459303101113311212226263534243701112122061514161703
86fdfdda9dee800115eb0134fed77c928b79039b0215fa5074d488ccfc03fd2f020689757491030000020068000006b005b0001800210060b2072223111239b00710b019d000b0004558b0082f1bb108
1f3e59b0004558b0002f1bb1000f3e59b2070800111239b0072fb00010b20a010a2b5821d81bf459b2110800111239b019d0b00710b21a010a2b5821d81bf459b01910b021d030312122243534243721
11331133363637362627331616070606072511212206151416170272ecfee20115eb0134fc4b5e6c0502211df51f260204f3ccfeb1fed67d908e7afdd3cefa030215fb1a028a7d4ad94c5ecc45d4fc03
ca02068a747592010002005effe7067f0618001f002b0083b2192c2d111239b01910b02ad000b0004558b0062f1bb106213e59b0004558b0032f1bb1031b3e59b0004558b0182f1bb1180f3e59b00045
58b01c2f1bb11c0f3e59b2050318111239b01810b20b010a2b5821d81bf459b2100318111239b21a0318111239b00310b222010a2b5821d81bf459b01c10b228010a2b5821d81bf45930311310123332
171133110616333636373627331716070e0223042706232202270126232206151416333237275ee4c3a365f3024e437482040440ec172f03027de28cfeff556bcbb9e00b02ae4783737f7a768d450602
0e010a0136780242fb4f4f6902b7a9bed559b783a8f98504b7b30105de015168c1cd9eaa72440001003cffe705e305b000290063b2232a2b11123900b0004558b0092f1bb1091f3e59b0004558b0222f
1bb1220f3e59b2012a09111239b0012fb200010a2b5821d81bf459b00910b207010a2b5821d81bf459b20f0001111239b02210b215010a2b5821d81bf459b21a22091112393031133533363635342121
352116041514071613150616333636373627331616070e022306262735342623e6a79384fef3fea50164fa0106fff605013c336572040440f51a2b02027ada8aa7b2087c670262cd016d75d1cd01d3cc
e6643ffefe4d394902b6a3bed562ca67a9f88504a7aa3e6e7e000001002fffe204fe043a00240060b20f252611123900b0004558b01d2f1bb11d1b3e59b0004558b00e2f1bb10e0f3e59b202010a2b58
21d81bf459b2070e1d111239b216251d111239b0162fb214070a2b5821d81bf459b01d10b21b010a2b5821d81bf459b22214161112393031250633363637362733161607060623062627353423232733
363534232327211616100716170301024e5a60030441ec2d180104e9bc9ea008a2e602c2b9cbff060114cbe4b0b906eb58028f7f96a9868039ccf2037183487fbd048396c302a6feca4a30ac00010048
feba043705b00022005fb20b232411123900b0172fb0004558b0092f1bb1091f3e59b0004558b01b2f1bb11b0f3e59b201091b111239b0012fb200010a2b5821d81bf459b00910b207010a2b5821d81b
f459b20f0001111239b01b10b212010a2b5821d81bf459303113273336363534212127211604151407161315331514060727363637232627353426239701ce9181feebfeea03012eef0103e4e303cd64
5a83243808a33c037e74025cc301736febc303dcc9df6647fef686ac63d84b4d39774931b184718500010074fea9041a043a0022005fb206232411123900b0182fb0004558b0092f1bb1091b3e59b000
4558b01c2f1bb11c0f3e59b201091c111239b0012fb200070a2b5821d81bf459b00910b207010a2b5821d81bf459b2100001111239b01c10b213010a2b5821d81bf45930311327333235342623212721
321716151407161715331514060727363637232627353423b301e1d26b63fee1040120e3786aadb102bb685583263806a62b01c3019bb38e4a53c16459929e4f3cc324ac65da474d3d7e4f1e8354a600
00010042ffeb077f05b000220062b200232411123900b0004558b00d2f1bb10d1f3e59b0004558b01f2f1bb11f0f3e59b0004558b0062f1bb1060f3e59b00d10b200010a2b5821d81bf459b00610b208
010a2b5821d81bf459b01f10b212010a2b5821d81bf459b2171f0d1112393031012103020206072335373636131321111416333236373627331616070e02232226350407fe61180e61b99c4a287a680f
1c038e4c3f6e7f040441f61c2902027fe08cc3c604e3fde0fef6fed38a02ca0309df011c02dffbbc5264b4a7bbd866c766a7fb84c1bd00010040ffeb065a043a00210062b220222311123900b0004558
b00c2f1bb10c1b3e59b0004558b01e2f1bb11e0f3e59b0004558b0052f1bb1050f3e59b00c10b200010a2b5821d81bf459b00510b207010a2b5821d81bf459b01e10b211010a2b5821d81bf459b2161e
0c11123930310121030206072327373636371321111616333236373627331716070e02232226270317fef71311a8ad53023250490a1402e10151455867040440ec1630030270c77dc2c7010374fe9afe
e9f403ca050bade501cefd2b5264a099b5c850b17c9be67cbeb900010094ffe7078605b0001d0065b2141e1f11123900b0004558b0002f1bb1001f3e59b0004558b0192f1bb1191f3e59b0004558b017
2f1bb1170f3e59b0004558b0112f1bb1110f3e59b204010a2b5821d81bf459b2090017111239b21c0017111239b01c2fb215010a2b5821d81bf459303101111416333636373627331716070e02230626
27352111231133112111050a4d3e707e040441f6172f03027ce28ebbc309fd82fcfc027e05b0fbbc566002b3a6bbd859b783a8f78704c0c3fffd9705b0fd83027d0000010077ffe3065c043a001c0078
b21b1d1e11123900b0004558b0042f1bb1041b3e59b0004558b0082f1bb1081b3e59b0004558b0022f1bb1020f3e59b0004558b01a2f1bb11a0f3e59b20708021112397cb0072f18b4d007e007025db4
40075007025db200010a2b5821d81bf459b01a10b20d010a2b5821d81bf459b2120802111239303101211123113311211133110616333636373627331616070e02230403031afe50f3f301b0f3025246
5e64030440eb1a2b020270c77efe8a1301bafe46043afe4301bdfd2d526602a691afce5dbf619be67c0801840001005dffeb04bb05c500210047b200222311123900b0004558b0092f1bb1091f3e59b0
004558b0002f1bb1000f3e59b00910b20e010a2b5821d81bf459b00010b215010a2b5821d81bf459b21a0009111239303105222402271134122433321707262322061511141633363637362733171607
0e0202bbacfeeb9b029a0117addf883f86a29dc5c49e7d83030335f52713010281ea159c0118ad010faf011d9e59b844e7bcff00b6e902857495ccb158588bcd6e0000010055ffeb03e7044e001e0044
b2131f2011123900b0004558b0132f1bb1131b3e59b0004558b00b2f1bb10b0f3e59b200010a2b5821d81bf459b2050b13111239b01310b218010a2b5821d81bf4593031253636373427331607060623
22003535343636333217072623220615151416025a51450213eb1d0204d2b5e7fee27ce292bb602e638a728b94af02434777678c52a0b00131f81e97fa8b42bd3abda4209abf00010021ffe7055a05b0
0019004db2051a1b11123900b0004558b0022f1bb1021f3e59b0004558b0162f1bb1160f3e59b00210b200010a2b5821d81bf459b004d0b005d0b01610b209010a2b5821d81bf459b20e160211123930
31012135211521111416333636373627331616070e022306262701e3fe3e0480fe3e4d3e707e040441f51b2b03027de28cbbc30904e3cdcdfc87546002b6a3bbd862ca67a8f98504c0c300010044ffe3
04cb043a0017004db205181911123900b0004558b0022f1bb1021b3e59b0004558b0152f1bb1150f3e59b00210b200010a2b5821d81bf459b004d0b005d0b01510b209010a2b5821d81bf459b20e1502
11123930310121352115211114163336363736273316160706062304030189febb038bfead52455e63030440eb2c190104f1c2fe89130377c3c3fdf05464028474939e7c7e37ccf20801840000010081
ffeb04ff05c500280073b226292a11123900b0004558b0162f1bb1161f3e59b0004558b00b2f1bb10b0f3e59b203010a2b5821d81bf459b224160b1112397cb0242f18b27324015db26024015db22501
0a2b5821d81bf459b2060325111239b2102524111239b01610b21e010a2b5821d81bf459b21b241e11123930310114163332363533140604232024353425262635342421321616152334262322061514
213315232206017fb79986aefc8dfefda0fef3febf010e7682012f010997fa8bfda37c90aa0133b6bf9da30198657e815e82be69e9c4fd5731a662c5db69ba7759757363d9c8700000020067046f02d6
05d70005000d001b00b00b2fb007d0b0072fb001d0b0012fb00b10b004d0b0042f30310113331503230133151617072635019370d3e65dfed4b1034c50b00498013f15fec101545f7b46485abe00ffff
00470209025402cd000600110000ffff00470209025402cd000600110000ffff009d026d0499033100460197e0004ccd4000ffff0081026d05d1033100460197850066664000ffff0004fe3f03990000
002700430001fefe010600430100001c00b6000210022002035db4100220020271b680029002a002035d30310001006304200196061a0008001db208090a11123900b0004558b0002f1bb100213e59b0
04d0b0042f3031011706071523353636011a7c5b03d50167061a4d8590988a60d100000100330400016506000008001db208090a11123900b0004558b0042f1bb104213e59b000d0b0002f3031132736
373533151406af7c5a03d56904004d83929e8a67d10000010032fed6016400ca00080018b208090a11123900b0092fb2040d0a2b5821d81bf4593031132736373533150606ad7b5503da0166fed64e7f
9493855dd0000001004a0400017c06000008001600b0004558b0082f1bb108213e59b004d0b0042f3031011516170726263535011f035a7c4d6906009e8f864d3ed1678affff006c042002ef061a0026
016c09000007016c01590000ffff0040040002c006000026016d0d000007016d015b000000020032fec202aa00ff000900120021b20b1314111239b00b10b005d000b0132fb2040d0a2b5821d81bf459
b00ed0303113273637353315060706172736373533151406b17f5503da013731f87f5804da66fec24e899dc9ba6c7264414e8e96cbb663dd000100400000041e05b0000b004b00b0004558b0082f1bb1
081f3e59b0004558b0062f1bb1061b3e59b0004558b00a2f1bb10a1b3e59b0004558b0022f1bb1020f3e59b00a10b200010a2b5821d81bf459b004d0b005d03031012111231121352111331121041efe
88f3fe8d0173f301780372fc8e0372c80176fe8a0001005cfe60043905b00013007c00b0004558b00c2f1bb10c1f3e59b0004558b00a2f1bb10a1b3e59b0004558b00e2f1bb10e1b3e59b0004558b002
2f1bb102113e59b0004558b0002f1bb1000f3e59b0004558b0042f1bb1040f3e59b206010a2b5821d81bf459b00e10b208010a2b5821d81bf459b009d0b010d0b011d0b00610b012d0b013d030312121
1123112135211121352111331121152111210439fe88f3fe8e0172fe8e0172f30178fe880178fe6001a0c202b4c40176fe8ac4fd4c00000100880206024403db000d0016b2030e0f11123900b0032fb1
0a0a2b58d81bdc59303113343633321615151406232226278879646778776763790203035f797962255e77735d00ffff008afff5036f01000026001203000007001201cd0000ffff008afff505280100
0026001203000027001201cd00000007001203860000000100470209012102cd00030018b200040511123900b0032fb200010a2b5821d81bf4593031012335330121dada0209c4000006004affec075f
05c400150023002700340041004e00b8b2284f50111239b02810b002d0b02810b01bd0b02810b026d0b02810b035d0b02810b047d000b0242fb0262fb0004558b0192f1bb1191f3e59b0004558b0122f
1bb1120f3e59b003d0b0032fb2050312111239b007d0b0072fb01210b00ed0b00e2fb2101203111239b01910b020d0b0202fb01210b22b020a2b5821d81bf459b00310b232020a2b5821d81bf459b02b
10b038d0b03210b03fd0b02010b245020a2b5821d81bf459b01910b24c020a2b5821d81bf459303101343633321736333216151514062322270623222635013436333216151514062322263501270117
031416333236353534262206150514163332363535342622061501141633323635353426220615032fac88964e4e9586afa98a974e4e948aacfd1ba8858aabab8885aa01777d02c77db04f3e404a4e7c
4d01c74f3e404a4e7c4dfb4e4d3f3e4c4d7e4b016582aa6f6fa78c4781aa6e6eaa86037b83aaaa894682a9a989fc1b48047248fc384457524c4b4654544a4a4457524c4b4654544a02ea455555494846
565749000001006c008a023303a90006001000b0052fb2020705111239b0022f303101132301350133013cf7a7fee00120a70219fe7101861301860000010054008a021b03a90006001000b0002fb203
0700111239b0032f303113011501231303fb0120fee0a7f7f703a9fe7a13fe7a018f01900001002d006d037105270003000900b0002fb0022f303137270117aa7d02c77d6d4804724800ffff00350293
02be05a8030701d800000293001300b0004558b0092f1bb1091f3e59b00dd030310000010069028c02ff05ba000f0053b20a101111123900b0004558b0002f1bb1001f3e59b0004558b0032f1bb1031f
3e59b0004558b00d2f1bb10d133e59b0004558b0072f1bb107133e59b201030d111239b00310b20a030a2b5821d81bf4593031011736332011112311262322071123110101204b900103c5057d6327c5
05ac7987fec9fe0901daad59fdd203200001005f0000047c05c30027008eb21f282911123900b0004558b0172f1bb1171f3e59b0004558b0062f1bb1060f3e59b2270617111239b0272fb20d020a2b58
21d81bf459b001d0b00610b205010a2b5821d81bf459b009d0b02710b010d0b02710b023d0b0232fb60f231f232f23035db225020a2b5821d81bf459b011d0b02310b014d0b01710b21e010a2b5821d8
1bf459b21b231e1112393031012117140721072135333636352723353327233533273436201615233426232206151721152117210332fed0024002b801fbe752272b02a5a0049c9705fa0196e8f5695f
586706013ffec605013501d42e8755caca096f5b37917990a1caeadab85f698268a19079000500210000064f05b0001b001f00230026002900bdb20a2a2b111239b00a10b01fd0b00a10b021d0b00a10
b026d0b00a10b028d000b0004558b01a2f1bb11a1f3e59b0004558b0172f1bb1171f3e59b0004558b00c2f1bb10c0f3e59b0004558b0092f1bb1090f3e59b205091a111239b0052fb001d0b0012fb20f
01015db203030a2b5821d81bf459b00510b207030a2b5821d81bf459b025d0b00ad0b00ed0b00510b01dd0b021d0b011d0b00310b01ed0b022d0b012d0b00110b019d0b027d0b015d0b00910b024d0b0
1710b029d030310133152315331523112301211123112335333523353311330121113301333523053327230135230133270577d8d8d8d8fdfec9feadfcd3d3d3d3fc01350157fbfe7194f3fe67ee5f8f
028c2ffda32b2b03c5a097a0fe1201eefe1201eea097a001ebfe1501ebfcde979797fe7e4b01d74400020098ffec063a05b0001e002500a2b2212627111239b02110b010d000b0004558b0152f1bb115
1f3e59b0004558b0192f1bb1191b3e59b0004558b01d2f1bb11d1b3e59b0004558b00a2f1bb10a0f3e59b0004558b0132f1bb1130f3e59b01d10b200010a2b5821d81bf459b00a10b205010a2b5821d8
1bf459b00010b00dd0b00ed0b2201315111239b0202fb211010a2b5821d81bf459b01d10b01cd0b01c2fb01510b224010a2b5821d81bf459303101231114163332371506232011112306060723112311
213216173311331133013332113427230633bf323f262f534dfee8781cf4ca9efa018cd4fd1875f2bffb5f92f4e6a00386fda43d380abc1701350265adbb03fde505b0c3b30107fef9fead0100f70600
ffff0094ffec083c05b00026003600000007005704720000000700350000075305b0001f00230027002b002e0031003400ebb2323536111239b03210b01ed0b03210b022d0b03210b027d0b03210b02a
d0b03210b02ed0b03210b030d000b0004558b0022f1bb1021f3e59b0004558b01f2f1bb11f1f3e59b0004558b01b2f1bb11b1f3e59b0004558b0102f1bb1100f3e59b0004558b00d2f1bb10d0f3e59b2
091002111239b0092fb005d0b0052fb20f05015db001d0b00510b207030a2b5821d81bf459b00910b20a030a2b5821d81bf459b02dd0b00ed0b030d0b012d0b00910b025d0b029d0b021d0b015d0b007
10b026d0b02ad0b022d0b016d0b00110b01dd0b019d0b01010b02fd0b02cd0b01f10b032d0b00110b034d030310121133303331523073315210323032103230321353327233533033313211333013337
2305333723053327230137230537230107330498013157fb629abf25e4fef77ef390fef292f27ffefdde25b99462fb5801346cd4fdce9f2aea030e9f21e9fea6ba2a6501b02656fd322f5501a7081004
0701a9fe57a0a2a0fddb0225fddb0225a0a2a001a9fe5701a9fd15a2a2a2a2a2fe00beb9b902011f0002007c00000610043a000d001b006bb2081c1d111239b00810b010d000b0004558b0002f1bb100
1b3e59b0004558b0162f1bb1161b3e59b0004558b00b2f1bb10b0f3e59b0004558b00e2f1bb10e0f3e59b211010a2b5821d81bf459b00010b209010a2b5821d81bf459b2051109111239b21009111112
39303101321617112311342623211123110111331121323637113311060623030cbbae02f35a69feaef30199f301506a5901f401efdc043ac0cbfeb501426d63fc8a043afbc602d6fded616802aefd57
bcd50001005effed043005c30023008ab215242511123900b0004558b0162f1bb1161f3e59b0004558b0092f1bb1090f3e59b2231609111239b0232fb200020a2b5821d81bf459b00910b204010a2b58
21d81bf459b00010b00cd0b02310b00ed0b02310b013d0b0132fb60f131f132f13035db210020a2b5821d81bf459b01610b21b010a2b5821d81bf459b01310b01ed0b01010b020d03031012116163332
371706232000032335333523353336003332170726232206072115211521036afe9c06a3986e5f1c7880ff00feda08acacacad0d012cfd6a851c666597a2090163fe9c0164020faeac21cc1d01200102
8d808dff011b1fcd22aca48d800000040021000005d405b0001a001f0024002900e3b20c2a2b111239b00c10b01cd0b00c10b023d0b00c10b028d000b0004558b00b2f1bb10b1f3e59b0004558b0012f
1bb1010f3e59b00b10b224010a2b5821d81bf459b020d0b0202f4013002010202020302040205020602070208020095db01ed0b01e2fb6b01ec01ed01e035d400b001e101e201e301e401e055db22603
0a2b5821d81bf459b027d0b0272f400f3027402750276027702780279027075db200010a2b5821d81bf459b02610b003d0b01e10b006d0b02010b00fd0b212030a2b5821d81bf459b01cd0b01dd0b007
d0b02010b00ad0b01e10b014d0b02610b017d0303101112311233533352335331121320417331523170733152306062301272115212521262721012115213201d6fdb8b8b8b8022dad01013ce4bd0201
bce136fabd011503fdbe0243fdbd01f04672fec801f4fe0c01317b021dfde3031fa048a001098881a02622a07d8501c22848e83b02fe3b37000100280000040c05b0001a006db2161b1c11123900b000
4558b0192f1bb1191f3e59b0004558b00c2f1bb10c0f3e59b01910b218010a2b5821d81bf459b001d0b01910b014d0b0142fb003d0b01410b213070a2b5821d81bf459b006d0b01410b00ed0b00e2fb2
09070a2b5821d81bf459b20d090e111239303101231617330723060607011521012733323637213721262321372103d9da330fca329716dcc901d2fee1fe0301fd708316fde63301e331d8fef33603ae
04f94b65b6a5af11fddf0d0251995d4cb69bcc0000010021ffec045105b0001e0091b21b1f2011123900b0004558b0112f1bb1111f3e59b0004558b0052f1bb1050f3e59b2131105111239b0132fb017
d0b0172fb20017015db218010a2b5821d81bf459b019d0b008d0b009d0b01710b016d0b00bd0b00ad0b01310b214010a2b5821d81bf459b015d0b00cd0b00dd0b01310b012d0b00fd0b00ed0b00510b2
1a010a2b5821d81bf459b21e051111123930310115060204232227110735373507353711331537150715371507113636353504510296feedb26b8cdcdcdcdcfce1e1e1e1aab202ff59d2fec3ab14025d
57c7578957c857013bd75ac85a895ac859fdfb02fcf84d000001004f0000050f043a0017005cb200181911123900b0004558b0172f1bb1171b3e59b0004558b0102f1bb1100f3e59b0004558b00b2f1b
b10b0f3e59b0004558b0052f1bb1050f3e59b2150b17111239b0152fb000d0b01510b20c010a2b5821d81bf459b009d030310116001315233526262711231106061515233512003735330328e0010304
f3018172f37182f3030104dff3036a29fe92feecbfb8c5ef2afd6a02952af3c7b1ba011401702bd1000200280000053305b00016001f0078b2182021111239b01810b00dd000b0004558b00c2f1bb10c
1f3e59b0004558b0022f1bb1020f3e59b206020c111239b0062fb205010a2b5821d81bf459b001d0b00610b00ad0b00a2fb20f0a015db209010a2b5821d81bf459b014d0b00610b015d0b00a10b017d0
b00c10b21f010a2b5821d81bf459303125211523352335333523353311213204151404072115210121323635342627210333febefccdcdcdcd022df10120feeef4fec40142febe012d88908d7cfec4e7
e7e7cb6bcb02c8fbd0d4f1036b01367e7d708e0300040070ffec058905c500190026003400380094b21a393a111239b01a10b000d0b01a10b027d0b01a10b037d000b0352fb0372fb0004558b0092f1b
b1091f3e59b0004558b0242f1bb1240f3e59b00910b003d0b0032fb20d0903111239b00910b210020a2b5821d81bf459b00310b216020a2b5821d81bf459b2190309111239b02410b01dd0b01d2fb024
10b22a020a2b5821d81bf459b01d10b231020a2b5821d81bf459303101140620263535343633321615233426232206151514163236350134363332161515140620263517141633323635353426232206
150527011702b19fff00a29e8280a1aa41363442436a400118ae8788ada7fee8abaa4f3e40494e3d3e4dfdfb7e02c77e04257392a78a4782ab94733540544a4a45554331fd4086a6a68d4782a9a78905
4457534b4b4654544af4480472480002004cffeb039005f900170021005ab2012223111239b00110b018d000b00c2fb0004558b0002f1bb1000f3e59b2060c00111239b0062fb205070a2b5821d81bf4
59b013d0b00010b217010a2b5821d81bf459b00610b018d0b00c10b21f010a2b5821d81bf45930310522263506233532371136363332161515140207151416330336363535342623220702dbe1ed6160
616003b29a88acd7b2686cd44d572b20560315ebe513bb1801e9bfd6b49b26adfea9674d8e7a02444bcc66293f40b20000040090000007c205c00003000f001d002700a6b21e2829111239b01e10b001
d0b01e10b004d0b01e10b010d000b0004558b0262f1bb1261f3e59b0004558b0242f1bb1241f3e59b0004558b0062f1bb1061f3e59b0004558b0212f1bb1210f3e59b0004558b01f2f1bb11f0f3e59b0
0610b00dd0b00d2fb002d0b0022fb20002015db201020a2b5821d81bf459b00d10b213020a2b5821d81bf459b00610b21a020a2b5821d81bf459b2202421111239b2251f261112393031012135210134
362016151514062026351714163332363535342623220615012101112311210111330797fd9f0261fd76be0138bfbafec2bdaf5c514f5b5c504f5cfec7fef4fe0df4010b01f6f2019c95022f9fc1c0a6
4e9cc2c2a206606c6c63515f6d6d62fba3040afbf605b0fbf3040d000002006d0394045705b0000c0014006d00b0004558b0062f1bb1061f3e59b0004558b0092f1bb1091f3e59b0004558b0132f1bb1
131f3e59b2011506111239b0012fb2000901111239b2030106111239b004d0b2080109111239b00110b00bd0b00610b10d0a2b58d81bdc59b00110b00fd0b00d10b011d0b012d0303101032303112311
331313331123012311231123352103e87c3e7c6f898185856ffe118a758d018c0509fe8b0174fe8c021cfe83017dfde401bdfe4501bb5f0000020096ffec0491044e0015001c0062b2021d1e111239b0
0210b016d000b0004558b00a2f1bb10a1b3e59b0004558b0022f1bb1020f3e59b2190a02111239b0192fb20f0a0a2b5821d81bf459b00210b2130c0a2b5821d81bf459b2150a02111239b00a10b2160a
0a2b5821d81bf459303125062322260235341236333216161715211116333237012207112111260414b7bb91f48790f88485e38403fd00779ac4acfe90977a021c735e729d0101938f01039f8bf3903e
feb86e7a032a7afeeb011e71ffff0059fff505cb0599002701d5ffd902860027017c00fb0000010701dc03210000001000b0004558b0062f1bb1061f3e593031ffff0054fff5066805b4002701d7001d
02940027017c01a80000010701dc03be0000001000b0004558b00d2f1bb10d1f3e593031ffff005bfff5065c05a8002701d9000c02930027017c018c0000010701dc03b20000001000b0004558b0012f
1bb1011f3e593031ffff0058fff5061a05a3002701db0022028e0027017c01330000010701dc03700000001000b0004558b0052f1bb1051f3e59303100020062ffeb044305f500190026005bb2132728
111239b01310b020d000b00b2fb0004558b0132f1bb1130f3e59b2000b13111239b0002fb2020b13111239b00b10b205010a2b5821d81bf459b00010b21a010a2b5821d81bf459b01310b220010a2b58
21d81bf45930310132172626232207273736332000111514020623220035353412172206151416333236353526260238ae771ac5847c8b1d3c6e8f010d01277ae394e3fef3fef47b85847a7985168b04
047dc2e535b7192cfe4efe7235c1fed3a70124f70ddf0112c2a7a49ab0d0c5554c5f000100a6ff1b04f405b00007002700b0042fb0004558b0062f1bb1061f3e59b00410b001d0b00610b202010a2b58
21d81bf4593031052311211123112104f4f4fd99f3044ee505d4fa2c069500010040fef304c105b0000c003500b0032fb0004558b0082f1bb1081f3e59b00310b202010a2b5821d81bf459b005d0b008
10b20a010a2b5821d81bf459b007d0303101012115213501013521152101038ffdee0344fb7f024ffdb10447fcf602120243fd73c39702c802c698c3fd730001009e026d03ef03310003001100b0022f
b201010a2b5821d81bf45930310121352103effcaf0351026dc40001003b0000049205b00008003cb200090a11123900b0072fb0004558b0012f1bb1011f3e59b0004558b0032f1bb1030f3e59b20001
03111239b00710b206010a2b5821d81bf459303101013301230323352102410178d9fe17c5d8d10167012b0485fa500241c50003005effec07df044e001a002a00390072b2073a3b111239b00710b022
d0b00710b032d000b0004558b0042f1bb1040f3e59b0004558b0092f1bb1090f3e59b00410b016d0b0162fb2071604111239b012d0b0122fb2141604111239b01610b21e010a2b5821d81bf459b00410
b227010a2b5821d81bf459b02ed0b01e10b037d0303101140606232226270221222626353534123633201312213216161707342623220706071516171633323635051416333236373735262726232206
07df80e6908de955aafedf8fe58181e48e0124a9a901248ee48101ef927aa46e280f0f2e6b9f7995fa5d927b69ac2b070f286ea47992021198fd90a3a7feb68eff99159801008ffeb901478ffd97049a
c6c94a42244555c3c3a2059dc3b3901a24424ac9c3000001ffaffe4b02a806150015003db202161711123900b0004558b00e2f1bb10e213e59b0004558b0032f1bb103113e59b208010a2b5821d81bf4
59b00e10b213010a2b5821d81bf4593031051406232227371633323711343633321707262322150190b6aa423f122c258a02c0b23f59192a32a34fb0b613bd0d9d04f4b3c315b90bb800000200650101
041503fa0015002b0078b2102c2d111239b01010b01cd000b0192fb003d0b0032fb008d0b0082fb00310b00ad0b00810b20d010a2b5821d81bf459b00310b212010a2b5821d81bf459b00d10b015d0b0
1910b01ed0b01e2fb01910b020d0b01e10b223010a2b5821d81bf459b01910b228010a2b5821d81bf459b02310b02bd03031133636333617171633323715062322272726072206071536363336171716
333237150623222727260722060765308442524c9c46518465667f5146984f54428730308042544f9846518765668351469c4c52428430038e323802224e207ed96a204c2402423ccb323802244c207e
d96a204e2202423c00010091008003ef04c30013003700b0132fb200010a2b5821d81bf459b004d0b01310b007d0b01310b00fd0b00f2fb210010a2b5821d81bf459b008d0b00f10b00bd03031012107
273723352137213521371707331521072103effde2806d5db001217efe610210866e63bdfed17d01ac0164e43ea6c9dfcaed3eafcadfffff003c0013038d046b006700200000008b4000399a00070197
ff9efda6ffff0080001303e0046b006700220000008b4000399a00070197ffe2fda600020024000003eb05b0000500090038b2060a0b111239b00610b004d000b0004558b0002f1bb1001f3e59b00045
58b0032f1bb1030f3e59b2060003111239b208000311123930310133010123010103131301a4c40183fe80c5fe7e01e1edf2ec05b0fd27fd2902d701d6fe2afe2901d700ffff00a100ab01bc05070027
0012001a00b601070012001a0407000900b0002fb011dc30310000020063027f023e043900030007002ab2000809111239b005d000b0022fb0004558b0062f1bb1061b3e59b2000802111239b0002fb0
04d03031012311330123113301009d9d013e9d9d027f01bafe4601ba00010045ff67015a01060008000c00b0042fb000d0b0002f3031172736373533150606c5804903c90153994d737b644f5dbaffff
002d0000051a06150026004a00000007004a02440000000200180000041706150017001b0073b2091c1d111239b00910b019d000b0004558b0092f1bb109213e59b0004558b0042f1bb1041b3e59b000
4558b01a2f1bb11a1b3e59b0004558b0172f1bb1170f3e59b0004558b0192f1bb1190f3e59b00410b013d0b216010a2b5821d81bf459b001d0b00910b20f010a2b5821d81bf45930313311233533353e
0233321617072623220615153315231121231133bda5a5016ac28850934f258a726f64d5d50267f3f30386b44a7fb65c221ac930616144b4fc7a043a0001002d0000042c061500160063b21217181112
3900b0004558b0122f1bb112213e59b0004558b00e2f1bb10e1b3e59b0004558b0092f1bb1090f3e59b0004558b0162f1bb1160f3e59b01210b202010a2b5821d81bf459b00e10b005d0b00e10b20b01
0a2b5821d81bf459b008d0303101262322151533152311231123353335363633320511230339664ac4dcdcf3a5a501d7c47a0144f3053f0eb85bb4fc7a0386b461b7c330fa1b0002002d000006930615
0028002c00b5b2142d2e111239b01410b02ad000b0004558b0082f1bb108213e59b0004558b0162f1bb116213e59b0004558b02b2f1bb12b1b3e59b0004558b0212f1bb1211b3e59b0004558b0112f1b
b1111b3e59b0004558b0042f1bb1041b3e59b0004558b0282f1bb1280f3e59b0004558b0252f1bb1250f3e59b0004558b02a2f1bb12a0f3e59b02110b222010a2b5821d81bf459b026d0b001d0b00810
b20d010a2b5821d81bf459b01610b21c010a2b5821d81bf4593031331123353335343633321707262322151521353e023332161707262322061515331523112311211121231133d2a5a5c8b440480628
35ae0174016ac28850934f2688736f64d5d5f3fe8c04cef3f30386b463b4c412be08b3604a7fb65c221ac930616144b4fc7a0386fc7a043a0001002d000006930615002700a5b213282911123900b000
4558b0152f1bb115213e59b0004558b0082f1bb108213e59b0004558b0042f1bb1041b3e59b0004558b0102f1bb1101b3e59b0004558b01f2f1bb11f1b3e59b0004558b0272f1bb1270f3e59b0004558
b0242f1bb1240f3e59b0004558b0192f1bb1190f3e59b00410b201010a2b5821d81bf459b00810b20d010a2b5821d81bf459b01510b21c010a2b5821d81bf459b00110b026d0b022d030313311233533
3534363332170726232215152135363633320511231126232215153315231123112111d2a5a5c8b44048062835ae017401d7c47a0144f3664ac4dcdcf3fe8c0386b463b4c412be08b36061b7c330fa1b
053f0eb85bb4fc7a0386fc7a0001002dffec04d1061500240085b213252611123900b0004558b00f2f1bb10f1b3e59b0004558b01a2f1bb11a1b3e59b0004558b0232f1bb1231b3e59b0004558b00a2f
1bb10a0f3e59b02310b200070a2b5821d81bf459b00a10b205010a2b5821d81bf459b00010b00dd0b00ed0b02310b21f010a2b5821d81bf459b213010a2b5821d81bf459b00e10b018d0b019d0303101
23111416333237150623201111233533352623221511231123353335343633321617113304cbbf313f262f534dfee8b2b2456ca3f3a5a5c2b065f172bf0386fda43e370abc1701350265b4f820b9fb67
0386b462b6c33831fe8e0001004bffec06800618004c00a7b2464d4e11123900b0004558b0472f1bb147213e59b0004558b0402f1bb1401b3e59b0004558b00f2f1bb10f1b3e59b0004558b04b2f1bb1
4b1b3e59b0004558b0092f1bb1090f3e59b0004558b02c2f1bb12c0f3e59b04b10b200070a2b5821d81bf459b00910b204010a2b5821d81bf459b00010b00dd0b00ed0b04710b214070a2b5821d81bf4
59b04010b220070a2b5821d81bf459b02c10b234070a2b5821d81bf4593031012311143332371506232226271123353335342623220615141e0215233426232206151416041616151406232226263533
1616333236353426262726353436333217263534363332161515330679bf71262f534d879001acac60584f581d211cf4685650655e011ea34ff2c485d074ec05786360646bf853b6ecb65b4d2dd9aec9
debf0386fdb7880abc17aaa2024eb458626954453a6966794d465d4a3e383e3f577a5792b560a861565d493b4144342858a78cbc176c4f81a5cac54f00160059fe7207ec05ae000d001a00280037003d
00430049004f0056005a005e00620066006a006e0076007a007e00820086008a008e01c0b2108f90111239b01010b000d0b01010b01bd0b01010b030d0b01010b03cd0b01010b03ed0b01010b046d0b0
1010b04ad0b01010b050d0b01010b057d0b01010b05bd0b01010b061d0b01010b063d0b01010b067d0b01010b06dd0b01010b070d0b01010b077d0b01010b07bd0b01010b07fd0b01010b084d0b01010
b088d0b01010b08cd000b03d2fb0004558b0462f1bb1461f3e59b27d44032bb27c79032bb27881032bb28039032bb20a463d111239b00a2fb003d0b0032fb00ed0b00e2fb00a10b00fd0b00f2fb26f0e
0f1112397cb06f2f18b2500b0a2b5821d81bf459b215506f111239b00a10b21e0b0a2b5821d81bf459b00310b2250b0a2b5821d81bf459b00f10b029d0b0292fb00e10b02ed0b02e2fb2340b0a2b5821
d81bf459b03d10b06bd0b067d0b063d0b03ed0b23f0c0a2b5821d81bf459b065d0b069d0b06dd0b03cd0b03910b041d0b04610b2470c0a2b5821d81bf459b05bd0b057d0b04ad0b04610b060d0b05cd0
b058d0b04bd0b04410b04ed0b00e10b2510b0a2b5821d81bf459b04710b05fd0b00f10b2760b0a2b5821d81bf459b07810b084d0b07910b085d0b07c10b088d0b07d10b089d0b08010b08cd0b08110b0
8dd0303101140623222627353436333216171311333216151407161615142301342623220615151416333236350133111406232226353314333236350111331533152135333533110111211523152535
21112335011533323534271335211521352115213521150135211521352115213521151333323534262323012335333523353311233533252335333523353311233533033781646680027e6865800243
bc6272543234d0fe8f4a41404a4a42404903ba5c6952586d5d682936f9c471c40528c76ff86d0135c405ec01366ffc5c7e6762cb0116fd5b0115fd5c0114020a0116fd5b0115fd5c0114bc5d763a3c5d
fcf171717171717107226f6f6f6f6f6f01d46279785e755f7c785efeb30225494d54200d462d9b0148454e4e4570454e4e45014ffe864e5d51535b362cfcc9013bca7171cafec5061f011d74a9a974fe
e3a9fcb6a9535204034a747474747474f93871717171717103c450291efed3fc7efafc15f97efc7efafc15f90005005cfdd507d708730003001c002000240028004c00b0212fb0252fb000d0b0002fb0
2110b002d0b0022fb2200200111239b0202fb01dd0b01d2fb004d0b0042fb20d0002111239b00d2fb014d0b0142fb2070414111239b21914041112393031090305343637363635342623220607333636
333216151407060615172315330333152303331523041803bffc41fc44040f1e244a5ca79590a002cb023a2b39385d5b2fcacaca4b04040204040652fc31fc3103cff13a3a1827874a80978b7f333440
345f3c415c4c5baafd4c040a9e040001003a000003ea05b00006003200b0004558b0052f1bb1051f3e59b0004558b0012f1bb1010f3e59b00510b203010a2b5821d81bf459b200030511123930310101
230121352103eafdd4f4022cfd4403b00529fad704edc3000002004ffe560417044e001b00260083b21f2728111239b01f10b00cd000b0004558b0042f1bb1041b3e59b0004558b0072f1bb1071b3e59
b0004558b00c2f1bb10c113e59b0004558b0182f1bb1180f3e59b2060418111239b00c10b212010a2b5821d81bf459b2101218111239b2160418111239b01810b21f010a2b5821d81bf459b00410b224
010a2b5821d81bf45930311334363633321737331114002322262737163332363535062322262637141633323711262322064f6dcd85bf6910d1fefbef55b9493582908e836aae7fcc72f38f78954645
947c8d0226a0fb8d8672fc1cf6fef62f2db04c9c9b16778cfc9d9fc08101d97bc1000001ffb0fe4b018e00cd000d002eb2030e0f11123900b00e2fb0004558b0052f1bb105113e59b20a010a2b5821d8
1bf459b00e10b00dd0b00d2f30312511140706232227371633323511018e705b9546380e243d7ccdfef7c8624f11c60cb20105000001005cfe9a014f00b50003001200b0042fb002d0b0022fb001d0b0
012f303101231133014ff3f3fe9a021b0002007504d002f706dc000c0020007b00b0032fb006d0b0062f400b0f061f062f063f064f06055db00310b209060a2b5821d81bf459b00610b00cd0b00c2fb0
0610b010d0b0102fb013d0b0132f400d0f131f132f133f134f135f13065db01010b016d0b0162fb01310b21a080a2b5821d81bf459b01010b21d080a2b5821d81bf459b01a10b020d030310114062026
3533141633323635131406232226232206152734363332163332363502f7b0fedeb0af4c46484a905f4738812a1f2a6861452f882c1e2c05b0657b7b65353a3c33010f4b6b4732251b4d6c4732240002
007504d502f60708000d001c005900b0032fb007d0b0072f400b0f071f072f073f074f07055db00310b20a060a2b5821d81bf459b00710b00dd0b00d2fb00710b00ed0b00e2fb014d0b0142fb20f0e14
111239b2150c0a2b5821d81bf459b21b0e0f1112393031011406232226353314163332363527273636353423373216151406070702f6af9192afad5044454ddf08483f92079e9f4e440105b062797962
34393a33197602171a366050442f3a083a000002007504d30300067e000d0011005d00b0032fb006d0b0062f400b0f061f062f063f064f06055db00310b20a060a2b5821d81bf459b00610b00dd0b00d
2fb00610b010d0b0102fb00ed0b00e2f400f0f0e1f0e2f0e3f0e4f0e5f0e6f0e075db01010b011d019b0112f1830310114062322263533141633323635273307230300af9695b1b14c49474c65b6a980
05b0617c7a63343c3c34cec00002007504e7035c06d10006001a008d00b0012fb003d0b0032fb004d019b0042f18b000d019b0002f18b00310b005d0b0052f40090f051f052f053f05045db202050311
1239b00ad0b00a2f40093f0a4f0a5f0a6f0a045db00dd0b00d2f400f0f0d1f0d2f0d3f0d4f0d5f0d6f0d075db00a10b010d0b0102fb00d10b214060a2b5821d81bf459b00a10b217060a2b5821d81bf4
59b01410b01ad03031012327072325333714062322262322061527343633321633323635035cc1b3b2c1012a93ba593d317b241b295a593c2a7f261a2c04e78e8eeddf3e5f422c1b184060412d1c0002
007504e7040a06cb00060015006000b0012fb003d0b0032fb004d019b0042f18b000d019b0002f18b00310b005d0b0052f40090f051f052f053f05045db2020305111239b00110b007d0b0072fb00dd0
b00d2fb208070d111239b20e060a2b5821d81bf459b2140807111239303101232707232533172736363534233732161514060707035cc1b3b2c10116bbb9073f388107898c49380104e7a2a2fa747d05
181d3e69594b3741073b0002ff4c04da035c06830006000a005b00b0032fb004d019b0042f18b000d019b0002f18b00310b001d0b0012fb006d0b0062f40090f061f062f063f06045db2020306111239
b00310b008d0b0082fb007d019b0072f18b00810b00ad0b00a2fb60f0a1f0a2f0a035d30310123270723253305230333035cd59f9fd40123a1fe879dd7dd04da8e8efa5c010b0002007a04e7048b0690
0006000a005b00b0032fb005d0b0052fb000d0b0002f40090f001f002f003f00045db00310b002d019b0022f18b2040300111239b006d019b0062f18b00310b009d0b0092fb007d0b0072fb60f071f07
2f07035db00910b00ad019b00a2f1830310133052327072301330323019da10123d49f9fd50333ded89d05e1fa8e8e01a9fef5000002007504d40300067e000d0011005d00b0032fb006d0b0062f400b
0f061f062f063f064f06055db00310b20a060a2b5821d81bf459b00610b00dd0b00d2fb00610b011d0b0112fb00ed0b00e2f400f0f0e1f0e2f0e3f0e4f0e5f0e6f0e075db01110b010d019b0102f1830
310114062322263533141633323635253317230300af9695b1b14c49474cfe94b7728005b1617c7a63343c3c34cdc00000010094046901a9062b0008001db208090a11123900b0004558b0002f1bb100
213e59b004d0b0042f30310117060707233534360126833f0201d355062b536d7c868559b6000002000900000494048d0007000a004600b0004558b0042f1bb1041d3e59b0004558b0022f1bb1020f3e
59b0004558b0062f1bb1060f3e59b2090402111239b0092fb200010a2b5821d81bf459b20a040211123930312521072301330123012103033ffe1e5ff501d7df01d5f6fe060154aaf9f9048dfb7301b2
01ba000300760000040a048d000e0016001f00a4b21e2021111239b01e10b002d0b01e10b011d000b0004558b0012f1bb1011d3e59b0004558b0002f1bb1000f3e59b2170100111239b0172fb4af17bf
17025db46f177f170271b2ff170171b20f170172b48f179f170272b25f170172b2cf170171b23f170171b41f172f17025db4bf17cf170272b20f010a2b5821d81bf459b2080f17111239b00010b21001
0a2b5821d81bf459b00110b21e010a2b5821d81bf459303133112132161514060716161514062303113332363534272733363635342623237601afdeeb595b6070e2dde2e46664b4fad45b636765c604
8da59c4f8323178f63a3ab01fbfec755419e05aa0248454f46000001004ffff00443049d001b004eb2031c1d11123900b0004558b00b2f1bb10b1d3e59b0004558b0032f1bb1030f3e59b20f0b031112
39b00b10b212010a2b5821d81bf459b00310b218010a2b5821d81bf459b21b030b111239303101060423220011353436363332041723262623201115141633323637044211fef7d9ecfeec7eec9cd601
0414f30c7d72feed8687787c0d0184bfd5012c010b44a9ff8adac27069fe8e48b9b56270000200760000042a048d000b00130046b2131415111239b01310b002d000b0004558b0012f1bb1011d3e59b0
004558b0002f1bb1000f3e59b00110b20c010a2b5821d81bf459b00010b20d010a2b5821d81bf4593031331121320416171514060423031133201335102576017ba4010390028ffef9a88382014706fe
c9048d8afb9f3da3fe8b03c9fcf9015c4301600800010076000003b5048d000b004e00b0004558b0062f1bb1061d3e59b0004558b0042f1bb1040f3e59b20b0604111239b00b2fb200010a2b5821d81b
f459b00410b202010a2b5821d81bf459b00610b208010a2b5821d81bf4593031012111211521112115211121035ffe0a024cfcc1033cfdb701f601f8fecac2048dc4fef2000100760000039e048d0009
004000b0004558b0042f1bb1041d3e59b0004558b0022f1bb1020f3e59b2090402111239b0092fb200010a2b5821d81bf459b00410b206010a2b5821d81bf459303101211123112115211121035bfe0e
f30328fdcb01f201dbfe25048dc4fed500010054fff00448049d001c005cb21a1d1e11123900b0004558b00a2f1bb10a1d3e59b0004558b0032f1bb1030f3e59b20e030a111239b00a10b211010a2b58
21d81bf459b00310b217010a2b5821d81bf459b21b030a111239b01b2fb219070a2b5821d81bf4593031250706212200113510003332161723262623201115141620373523352104481796fed5f8fedc
0116f4d7fa19ed12796cfee4a0012846f901eb93188b012e0109410109012cc3c0645cfe8940b7ba39c8b1000001007600000468048d000b008600b0004558b0062f1bb1061d3e59b0004558b00a2f1b
b10a1d3e59b0004558b0002f1bb1000f3e59b0004558b0042f1bb1040f3e59b2090600111239b0092fb4af09bf09025db23f090171b2cf090171b23f090172b2ff090171b20f090172b46f097f090271
b4df09ef09025db25f090172b41c092c09025db202010a2b5821d81bf45930312123112111231133112111330468f3fdf4f3f3020cf301dbfe25048dfe1101ef0001008500000177048d0003001d00b0
004558b0022f1bb1021d3e59b0004558b0002f1bb1000f3e593031212311330177f2f2048d0000010024fff00364048d000e0022b2050f1011123900b0004558b0052f1bb1050f3e59b20b010a2b5821
d81bf45930310133111406232226353314333236350271f3e3b2cae1f4b74b57048dfce0aecfc0afad5e5d000001007600000468048d000c004b00b0004558b0042f1bb1041d3e59b0004558b0082f1b
b1081d3e59b0004558b0022f1bb1020f3e59b0004558b00b2f1bb10b0f3e59b2060204111239b00610b001d0b20a010611123930310107112311331137012101012101f087f3f36e014f012cfe4301d3
fede01db83fea8048dfdfd86017dfdf7fd7c0001007600000394048d0005002800b0004558b0042f1bb1041d3e59b0004558b0022f1bb1020f3e59b200010a2b5821d81bf45930312521152111330169
022bfce2f3c2c2048d00000100760000058f048d000e0060b2010f1011123900b0004558b0002f1bb1001d3e59b0004558b0022f1bb1021d3e59b0004558b0042f1bb1040f3e59b0004558b0082f1bb1
080f3e59b0004558b00c2f1bb10c0f3e59b2010004111239b2070004111239b20a00041112393031090221112311130123011311231101b20151014e013ef219fea0a8fea119f2048dfcb5034bfb7301
3b023afc8b0370fdcbfec5048d000001007600000467048d0009004500b0004558b0052f1bb1051d3e59b0004558b0082f1bb1081d3e59b0004558b0002f1bb1000f3e59b0004558b0032f1bb1030f3e
59b2020500111239b20705001112393031212301112311330111330467f2fdf4f3f3020cf2031bfce5048dfce4031c000002004ffff0046f049d000e001c0046b2031d1e111239b00310b012d000b000
4558b00b2f1bb10b1d3e59b0004558b0032f1bb1030f3e59b00b10b212010a2b5821d81bf459b00310b219010a2b5821d81bf45930310110002322001135341236333200112734262322061515141633
323635046ffedfedecfeda85f09bf00120f29688869899878894022cfef8fecc0135010c2eac01078bfec7fef508b7c0c0b735b2c7c3b600000200760000042c048d000a0013004db2041415111239b0
0410b00cd000b0004558b0032f1bb1031d3e59b0004558b0012f1bb1010f3e59b20b0103111239b00b2fb200010a2b5821d81bf459b00310b212010a2b5821d81bf45930310111231121321615140607
2733323635342623230169f301e5d4fdf1d4fef268777965f30199fe67048dd5ada9c603c458545769000002004cff30046c049d001400220046b2082324111239b00810b01fd000b0004558b0112f1b
b1111d3e59b0004558b0082f1bb1080f3e59b01110b218010a2b5821d81bf459b00810b21f010a2b5821d81bf45930310114060717072506232226022735341236333200112734262322061515141633
323635046c6e63cf9dfef632349af2840182f19cef0122f19789869797888995022ca3f1489888c9098b0101aa39ab01058efec8fef408b7c0c3b633b0c9c3b60002007600000439048d000d00160061
b2051718111239b00510b00fd000b0004558b0042f1bb1041d3e59b0004558b0022f1bb1020f3e59b0004558b00d2f1bb10d0f3e59b20e0204111239b00e2fb200010a2b5821d81bf459b20a000e1112
39b00410b215010a2b5821d81bf459303101231123112132161514070115210133323635342623230248dff301c8daf0e10112fefcfe34d56c6c696fd501a9fe57048db7aaeb5bfe250b026b5f4e5160
0001003efff003ef049d00250063b209262711123900b0004558b0092f1bb1091d3e59b0004558b01c2f1bb11c0f3e59b2031c09111239b20d091c111239b00910b210010a2b5821d81bf459b00310b2
15010a2b5821d81bf459b2211c09111239b01c10b223010a2b5821d81bf45930310134262426263534363332161523342623220615141617161615140623222626353314213236030268fecfb053f6c3
d2fef378655f6e718fddc0f8cc8ae57ef40100616f0132424f4c62835c92bbc8a0515d4d403a4c2336b28e99ae5daa71c04a0001002400000416048d0007002e00b0004558b0062f1bb1061d3e59b000
4558b0022f1bb1020f3e59b00610b200010a2b5821d81bf459b004d0303101211123112135210416fe7ef3fe8303f203c9fc3703c9c400010067fff0041e048d000f0035b20c101111123900b0004558
b0082f1bb1081d3e59b0004558b0042f1bb1040f3e59b20c010a2b5821d81bf459b00810b00fd0303101111404202435113311141633323711041efefffe4aff00f17e6ce504048dfd01bee0ddc102ff
fd007368d40307000001000900000472048d0008003100b0004558b0032f1bb1031d3e59b0004558b0072f1bb1071d3e59b0004558b0052f1bb1050f3e59b20103051112393031011737012101230121
022a131201220101fe46f6fe47010101384d4b0357fb73048d0000010028000005e5048d000c005900b0004558b0012f1bb1011d3e59b0004558b0082f1bb1081d3e59b0004558b00b2f1bb10b1d3e59
b0004558b0032f1bb1030f3e59b0004558b0062f1bb1060f3e59b2000103111239b2050103111239b20a0103111239303101133301230303230133131333044aafecfee6ebd8dbebfee6ecb1d8d6012b
0362fb730341fcbf048dfc9c0364000100150000044a048d000b005300b0004558b0012f1bb1011d3e59b0004558b00a2f1bb10a1d3e59b0004558b0042f1bb1040f3e59b0004558b0072f1bb1070f3e
59b2000104111239b2060104111239b2030006111239b209060011123930310113210101210303210101210227f2011cfe89018cfee0fffafee40181fe88011a02fa0193fdbefdb50199fe67024b0242
0001000500000436048d0008003100b0004558b0012f1bb1011d3e59b0004558b0072f1bb1071d3e59b0004558b0042f1bb1040f3e59b20001041112393031010121011123110121021d010e010bfe5d
f2fe64010b027a0213fd07fe6c01a102ec0000010041000003f3048d0009004400b0004558b0072f1bb1071d3e59b0004558b0022f1bb1020f3e59b200010a2b5821d81bf459b2040002111239b00710
b205010a2b5821d81bf459b20905071112393031252115213501213521150178027bfc4e026cfd9503a0c2c28d033cc48a000002004bfff502aa0320000d00170046b2031819111239b00310b010d000
b0004558b00a2f1bb10a193e59b0004558b0032f1bb1030f3e59b00a10b210020a2b5821d81bf459b00310b215020a2b5821d81bf4593031011406232226353534363332161527342322071514333237
02aa9e90929f9e9190a0bb757203776f04013e9faaaa9e989daead9e0ca99fb8a99a000100800000020203130006003100b0004558b0052f1bb105193e59b0004558b0012f1bb1010f3e59b00510b004
d0b0042fb203020a2b5821d81bf4593031212311073525330202b9c9016f13023a3092770001003c000002b2032000170059b208181911123900b0004558b00f2f1bb10f193e59b0004558b0002f1bb1
000f3e59b216020a2b5821d81bf459b2021600111239b2030f00111239b00f10b208020a2b5821d81bf459b20c000f111239b215000f111239303121213501363534262322061523343633321615140f
022102b2fd9c011d7136343a42baa9878f9c6a628c01737d010567432a354236749980736b66577100010037fff502a903200024007fb21e252611123900b0004558b00d2f1bb10d193e59b0004558b0
172f1bb1170f3e59b200170d1112397cb0002f18b4500060000271b680009000a000035db00d10b206020a2b5821d81bf459b20a0006111239b00010b224020a2b5821d81bf459b2122400111239b017
10b21e020a2b5821d81bf459b21b241e111239303101333235342623220615233436333216151407161514062322263533141633323635342723010c5184363e3041baa5828fa38795b18f87abba453c
3f3d865c01d26123352723637c79697733298e6a7e7f712635372a65010000020035000002be0315000a000e004900b0004558b0092f1bb109193e59b0004558b0042f1bb1040f3e59b2010904111239
b0012fb202020a2b5821d81bf459b006d0b00110b00bd0b2080b06111239b20d09041112393031013315231523352127013301333507025f5f5fbbfe9a09016dbdfe8bba0e013a97a3a37901f9fe25f2
16000001004ffff502ae0315001a006ab20d1b1c11123900b0004558b0022f1bb102193e59b0004558b00d2f1bb10d0f3e59b00210b203020a2b5821d81bf459b207020d111239b0072fb218020a2b58
21d81bf459b2051807111239b00d10b213020a2b5821d81bf459b2111318111239b21a18131112393031131321152107363332161514062322262733163332353426232207623401ecfeac143e47838c
a38c81ad02b905727543424335017f019696941b867a78998463527d384428000002004dfff502b903220013001e005bb2141f20111239b01410b00cd000b0004558b0002f1bb100193e59b0004558b0
0c2f1bb10c0f3e59b00010b201020a2b5821d81bf459b2060c00111239b0062fb214020a2b5821d81bf459b00c10b21a020a2b5821d81bf4593031011522060736333216151406232226353534363303
22060715143332363534023291890d476b7587a88693abf0de962d420f7f35440322995f62458e7a7799a79b31d2e8fe572417249146367400010036000002ae03150006003200b0004558b0052f1bb1
05193e59b0004558b0022f1bb1020f3e59b00510b204020a2b5821d81bf459b200040511123930310101230121352102aefeadc40153fe4c027802acfd54027f96000003004bfff502aa03200013001c
00240096b2072526111239b00710b014d0b00710b022d000b0004558b0112f1bb111193e59b0004558b0072f1bb1070f3e59b22207111112397cb0222f18b680229022a022035db4502260220271b400
2210220271b440225022025db4d022e0220271b219020a2b5821d81bf459b2022219111239b20c1922111239b00710b214020a2b5821d81bf459b01110b21f020a2b5821d81bf4593031011407161514
0623222635343726353436333216013236342622061416133422151416323602977184a18e8ca484719b81829bfee43540416a404097c4336031024174373d806a7a796b803d3774697676fde0335a30
305a3301ab565627303000020046fff702a303200013001f0060b2142021111239b01410b008d000b0004558b0082f1bb108193e59b0004558b0102f1bb1100f3e59b20210081112397cb0022f18b010
10b211020a2b5821d81bf459b00210b214020a2b5821d81bf459b00810b21a020a2b5821d81bf4593031010623222635343633321617151406072335323627323735342623220615141601e7425a7e87
aa848ba202dce0138f79634e23423433413c0136398a7d78a4a6973bd7d9019352ac344548414e393744000100900287032d03310003001100b0022fb201010a2b5821d81bf459303101213521032dfd
63029d0287aa00030096044802a206950003000f001b004e00b00d2fb019d0b0192fb207090a2b5821d81bf459b002d0b0022fb000d0b0002f400f0f001f002f003f004f005f006f00075db00210b003
d019b0032f18b00d10b213090a2b5821d81bf45930310133072307343633321615140623222637141633323635342623220601bce6f595826e4e4c6c694f516b6334252430302425340695c2de4e6465
4d4a63624b253131252733330003000afe4a041b044e002900360043009bb2084445111239b00810b030d0b00810b03ad000b0004558b0262f1bb1261b3e59b0004558b0162f1bb116113e59b02610b0
28d0b0282fb200030a2b5821d81bf459b2081626111239b0082fb20f1608111239b00f2fb235010a2b5821d81bf459b21b350f111239b21f0826111239b01610b230010a2b5821d81bf459b00810b23a
010a2b5821d81bf459b02610b241010a2b5821d81bf459303101231615151406062322270615141733161615140606232224353437263534372626353534363332172101060615141633323635342725
03141633323635353426220615041b8a3a73ce8051452573c2c3ca8ffa9ad9fef5b632755a64fcc7554b0171fd302431887286ac93feea407a59587775b87503a055691664a95f12232f4a03019a8e58
a6629b79a55932487751319e5f16a2ca14fbe5134830424d5e406b090202b34b66674e124a66664d00020056ffeb045f044e0010001d006eb21b1e1f111239b01b10b009d000b0004558b0092f1bb109
1b3e59b0004558b00c2f1bb10c1b3e59b0004558b0022f1bb1020f3e59b0004558b0102f1bb1100f3e59b2000902111239b20b0902111239b00210b214010a2b5821d81bf459b00910b21b010a2b5821
d81bf459303125062322023535101233321737330313230114163332363735262623220603636ef2c7e6e8c7e9711cdd6c73ddfdc77c74607c17117d63737fc4d90120f40f010a0136d7c3fde2fde401
f9a0acaba62fa5b9c5000002009b000004f205b00016001e0061b2181f20111239b01810b004d000b0004558b0032f1bb1031f3e59b0004558b0012f1bb1010f3e59b0004558b00f2f1bb10f0f3e59b2
170301111239b0172fb200010a2b5821d81bf459b2090017111239b00310b21d010a2b5821d81bf4593031011123112132161514071613151417152126273534262325213236353421210197fc0229f5
fff7e50547fefc3b047b70fed301149081fef8fee30256fdaa05b0d9cde36545fef673a93d1a31b8797480ca716de6000001009b0000053005b0000c005800b0004558b0042f1bb1041f3e59b0004558
b0082f1bb1081f3e59b0004558b0022f1bb1020f3e59b0004558b00b2f1bb10b0f3e59b2060204111239b0062fb21f060171b201010a2b5821d81bf459b20a0106111239303101231123113311330121
0101210243acfcfc8b01ac0136fe0c0220fed00270fd9005b0fd9c0264fd47fd090000010081000004350600000c005300b0004558b0042f1bb104213e59b0004558b0082f1bb1081b3e59b0004558b0
022f1bb1020f3e59b0004558b00b2f1bb10b0f3e59b2070802111239b0072fb200010a2b5821d81bf459b20a000711123930310123112311331133012101012101e26ff2f269010f011cfe9f018ffee6
01d9fe270600fc9c019efe11fdb50001009b0000051205b0000b004c00b0004558b0032f1bb1031f3e59b0004558b0072f1bb1071f3e59b0004558b0012f1bb1010f3e59b0004558b00a2f1bb10a0f3e
59b2000301111239b2050301111239b209000511123930310111231133113301210101210197fcfc0602190138fda5027ffec8029afd6605b0fd7f0281fd35fd1b0000010081000004220618000a004c
00b0004558b0032f1bb103213e59b0004558b0062f1bb1061b3e59b0004558b0012f1bb1010f3e59b0004558b0092f1bb1090f3e59b2000601111239b2050601111239b2080005111239303101112311
331101210101210173f2f20159012afe5001dcfedb01ebfe150618fc84019efe0cfdba000001003eff1303ef0573002a006fb2132b2c11123900b0004558b0092f1bb1091d3e59b0004558b0222f1bb1
220f3e59b2032209111239b00910b00cd0b00310b218010a2b5821d81bf459b00910b213010a2b5821d81bf459b2101813111239b02210b01fd0b02210b228010a2b5821d81bf459b226032811123930
3101342624262635343637353315161615233426232206151416171616151406071523352626353314213236030268fecfb053cfa9a0a6cbf378655f6e718fddc0c3aea0bde3f40100616f0132424f4c
62835c86b410d9dc15c08d515d4d403a4c2336b28e86ac11e1e113c79ac04a00000100380000041a049d001f006eb21b202111123900b0004558b0132f1bb1131d3e59b0004558b0052f1bb1050f3e59
b21f1305111239b01f2fb200020a2b5821d81bf459b00510b203010a2b5821d81bf459b007d0b008d0b00010b00cd0b01f10b00ed0b01310b21a010a2b5821d81bf459b2171f1a111239303101211607
210721353336362727233533272636333216152334262322061717210347fe850650029801fc650a292b0301a09b0306d8bfc2d9f357504d570504018001e5b270c3c30b937d079369ceeed4bc616a7e
79690001000e0000043f048d00180095b200191a11123900b0004558b0012f1bb1011d3e59b0004558b0182f1bb1181d3e59b0004558b00c2f1bb10c0f3e59b2000c18111239b2090c01111239b0092f
b004d0b0042f400d0f041f042f043f044f045f04065db6cf04df04ef04035db206020a2b5821d81bf459b00910b20a020a2b5821d81bf459b00ed0b00910b010d0b0102fb00610b013d0b00410b016d0
b0162f3031010121013315210715211521152335213521352721353301210225010f010bfebed5feda100136fecaf2feca013609fed3dcfebe010b027a0213fdb7931d2a91d9d9913611930249000001
007600000397048d00050032b201060711123900b0004558b0042f1bb1041d3e59b0004558b0032f1bb1030f3e59b00410b200010a2b5821d81bf45930310121112311210397fdd2f3032103c9fc3704
8d000002000900000472048d00030008003cb205090a111239b00510b002d000b0004558b0022f1bb1021d3e59b0004558b0002f1bb1000f3e59b2050002111239b207010a2b5821d81bf45930312121
013303270703210472fb9701b9f6691213de01e3048dfec94b4dfd6f0003004ffff0046f049d0003001200200076b2072122111239b00710b001d0b00710b016d000b0004558b00f2f1bb10f1d3e59b0
004558b0072f1bb1070f3e59b2030f071112397cb0032f18b460037003025db430034003025db200030171b200010a2b5821d81bf459b00f10b216010a2b5821d81bf459b00710b21d010a2b5821d81b
f45930310121352105100023220011353412363332001127342623220615151416333236350338fe5a01a60137fedfedecfeda85f09bf00120f2968886989987889401dfc376fef8fecc0135010c2eac
01078bfec7fef508b7c0c0b735b2c7c3b6000001000900000472048d00080038b207090a11123900b0004558b0022f1bb1021d3e59b0004558b0002f1bb1000f3e59b0004558b0042f1bb1040f3e59b2
0702001112393031212101330121012707010afeff01b9f601bafefffede1213048dfb7303564b4d0003004200000355048d00030007000b005eb2040c0d111239b00410b000d0b00410b008d000b000
4558b00a2f1bb10a1d3e59b0004558b0002f1bb1000f3e59b202010a2b5821d81bf459b2070a00111239b0072fb204010a2b5821d81bf459b00a10b208010a2b5821d81bf45930312121352103213521
132135210355fced031349fd7e028249fced0313c30138c4010ac4000001007600000462048d0007003fb201080911123900b0004558b0062f1bb1061d3e59b0004558b0042f1bb1040f3e59b0004558
b0012f1bb1010f3e59b00610b202010a2b5821d81bf459303121231121112311210462f4fdfbf303ec03c9fc37048d0000010044000003e6048d000c004bb2000d0e11123900b0004558b0082f1bb108
1d3e59b0004558b0032f1bb1030f3e59b201010a2b5821d81bf459b2050103111239b00810b20a010a2b5821d81bf459b2070a081112393031010121152135010135211521010290fee60270fc5e013f
fec1037cfdba01160245fe7fc49801b701a698c4fe8f000300500000054d048d00110016001c006fb2081d1e111239b00810b014d0b00810b01ad000b0004558b0102f1bb1101d3e59b0004558b0082f
1bb1080f3e59b20f1008111239b00f2fb000d0b2090810111239b0092fb006d0b00910b214010a2b5821d81bf459b00f10b215010a2b5821d81bf459b01ad0b01410b01bd03031011604151404071523
35262435342437353301020511040534262711240349f00114fee9edf3f0feea0117eff3fdf9040118feec03199082011204150ff6cad0fa0f6d6c0ff9d0cdf70d78fdb7fefd15022a15fb85810afdd6
15000001005000000503048d0018004bb200191a11123900b0004558b0122f1bb1121d3e59b0004558b00c2f1bb10c0f3e59b2160c12111239b0162fb000d0b01210b017d0b004d0b01610b20d010a2b
5821d81bf459b00ad030310136363511331106070607112311260203113311141617113303237f6ef301687dfaf3e3fb02f3707df301dd18c2a7012ffecde393af1dfee8011716012a01000136fed1a8
c01802af0001005f00000484049d0023005cb207242511123900b0004558b0192f1bb1191d3e59b0004558b00f2f1bb10f0f3e59b0004558b0222f1bb1220f3e59b00f10b211010a2b5821d81bf459b0
0ed0b000d0b01910b207010a2b5821d81bf459b01110b020d0b021d0303125363635353426232206151514161715213533261135343636333200151514060733152102ad786c948d8a947674fe30b0bd
83f29cea012a6359b6fe2fc822c9b02b9eaca9a428b1c723c8c49b01271691ec84fee3ed198ddf4ac40000010024ffec0552048d0019006bb2161a1b11123900b0004558b0022f1bb1021d3e59b00045
58b00e2f1bb10e0f3e59b0004558b0182f1bb1180f3e59b00210b200010a2b5821d81bf459b004d0b005d0b208020e111239b0082fb00e10b20f070a2b5821d81bf459b00810b215010a2b5821d81bf4
5930310121352115211536333216151406233532363534262322071123017efea603adfea08a8ddaf0f0eb737674758185f303c9c4c4ee27d4c6bcc0bd5469726726fde70001004ffff00443049d001d
008fb2031e1f11123900b0004558b00b2f1bb10b1d3e59b0004558b0032f1bb1030f3e59b20f0b03111239b00b10b212010a2b5821d81bf459b2150b03111239b0152fb2ff150171b20f150172b23f15
0171b2cf150171b46f157f150271b4af15bf15025db25f150172b28f150172b216010a2b5821d81bf459b00310b21a010a2b5821d81bf459b21d030b1112393031010604232200113534363633320417
232626232203211521161633323637044211fef7d9ecfeec7eec9cd6010414f30c7d72fb160180fe800a7e83787c0d0184bfd5012c010b44a9ff8adac27069fecfc4949f62700002002400000715048d
001700200076b2042122111239b00410b018d000b0004558b0122f1bb1121d3e59b0004558b0032f1bb1030f3e59b0004558b00b2f1bb10b0f3e59b01210b205010a2b5821d81bf459b00b10b20e010a
2b5821d81bf459b2141203111239b0142fb218010a2b5821d81bf459b00310b219010a2b5821d81bf45930310114060721112103060206232337373636371321113332162511333236353426230715f9
cffe15fea40e0b58ac91340126604e0c15033becdafafd40f167757666017fabd20203c9fe9ceffeff75cd02079fed022bfe6cd00cfe8e6b535163000002007600000718048d0013001c00c1b2011d1e
111239b00110b014d000b0004558b0132f1bb1131d3e59b0004558b0022f1bb1021d3e59b0004558b0102f1bb1100f3e59b0004558b00d2f1bb10d0f3e59b2001013111239b0002fb4af00bf00025db2
3f000171b2cf000171b23f000172b25f000172b2ff000171b20f000172b46f007f000271b4df00ef00025db41f002f00025db29f000172b2040d02111239b0042fb00010b20f010a2b5821d81bf459b0
0410b214010a2b5821d81bf459b00d10b215010a2b5821d81bf45930310121113311333216161514062321112111231133011133323635342623016901fdf3f28cd26fffd2fe1ffe03f3f302f0f16775
7666029e01effe6c5fab70afd001dbfe25048dfda8fe8e6b535163000001002400000552048d00150057b212161711123900b0004558b0032f1bb1031d3e59b0004558b0142f1bb1140f3e59b0004558
b00d2f1bb10d0f3e59b00310b204010a2b5821d81bf459b000d0b2081403111239b0082fb211010a2b5821d81bf459303101213521152115363332161711231134262322071123017efea603adfea086
8edeeb04f374748185f303c9c4c4ed26cfcbfe98015a7c6926fde70000010076fe9f0461048d000b004fb2030c0d11123900b0022fb0004558b0062f1bb1061d3e59b0004558b00a2f1bb10a1d3e59b0
004558b0002f1bb1000f3e59b0004558b0042f1bb1040f3e59b208010a2b5821d81bf459b009d030312121112311211133112111330461fe8af3fe7ef30205f3fe9f0161048dfc3603ca000200760000
0428048d000b0014005eb2081516111239b00810b00cd000b0004558b00a2f1bb10a1d3e59b0004558b0082f1bb1080f3e59b00a10b200010a2b5821d81bf459b2030a08111239b0032fb00810b20c01
0a2b5821d81bf459b00310b212010a2b5821d81bf459303101211533161610062321112101323635342627231103b2fdb7fccff4f8d9fe1f033cfea868737066f603cbe003c4fea8cc048dfc3663544f
5d01fe9c00020027feaf0515048d000f0015005bb2131617111239b01310b005d000b00d2fb0004558b0052f1bb1051d3e59b0004558b00b2f1bb10b0f3e59b200010a2b5821d81bf459b007d0b008d0
b00d10b00ad0b00810b010d0b011d0b00510b212010a2b5821d81bf4593031373e02371321113311231121112313212111210702824a4223050c033d96f2fcf7f301017401f0fea1070dc35186b47e01
c1fc36fdec0151feaf02140306fcfeae0001001a0000061f048d0015009eb201161711123900b0004558b0112f1bb1111d3e59b0004558b00e2f1bb10e1d3e59b0004558b00a2f1bb10a1d3e59b00045
58b0062f1bb1060f3e59b0004558b0032f1bb1030f3e59b0004558b0152f1bb1150f3e59b20c030e111239b00c2fb23f0c0171b25f0c0172b2cf0c0171b4af0cbf0c025db48f0c9f0c0272b00fd0b201
010a2b5821d81bf459b004d0b2080f04111239b213010f11123930310123112311230321010121133311331133132101012103f55ff360fcfed3015cfec4011ef754f354f7011efec2015efed301d5fe
2b01d5fe2b02540239fe2001e0fe2001e0fdd0fda30000010042fff003e7049d0027008ab226282911123900b0004558b00a2f1bb10a1d3e59b0004558b0162f1bb1160f3e59b00a10b203010a2b5821
d81bf459b2060a16111239b2260a16111239b0262fb2cf260171b23f260171b4af26bf26025db2ff260171b20f260172b25f260172b223010a2b5821d81bf459b2102326111239b21c160a111239b016
10b21e010a2b5821d81bf45930310134262322061523343633321615140607161615140423222627263533163332363534272335333602e2706b5b66f3f3c3d8f46e5d6f6efefedc5daf3f7cf30bca77
74e0949ac70343464f463c94b3a7965b8a2724915b9fb52d2f5b9f935748a603b004000100760000046e048d0009004cb2000a0b11123900b0004558b0002f1bb1001d3e59b0004558b0082f1bb1081d
3e59b0004558b0052f1bb1050f3e59b0004558b0032f1bb1030f3e59b2040300111239b2090508111239303101331123110123113311037bf3f3fdeef3f3048dfb730323fcdd048dfce0000100760000
0440048d000c0077b2000d0e11123900b0004558b0082f1bb1081d3e59b0004558b0052f1bb1051d3e59b0004558b0022f1bb1020f3e59b0004558b00c2f1bb10c0f3e59b2060205111239b0062fb23f
060171b25f060172b2cf060171b4af06bf06025db48f069f060272b201010a2b5821d81bf459b20a010611123930310123112311331133012101012101d36af3f3630138011dfe7201adfed101d5fe2b
048dfe2001e0fdc5fdae0001002400000455048d0010004db204111211123900b0004558b0002f1bb1001d3e59b0004558b0012f1bb1010f3e59b0004558b0092f1bb1090f3e59b00010b203010a2b58
21d81bf459b00910b20c010a2b5821d81bf459303101112311210306020607233737363637130455f3fea40f0c57aa8c3a0127624a0c16048dfb7303c9fe9fedfefe7801cd040ba0e6022b000001001f
ffec0439048d000f0043b200101111123900b0004558b00f2f1bb10f1d3e59b0004558b0022f1bb1021d3e59b0004558b0082f1bb1080f3e59b201080f111239b20b010a2b5821d81bf4593031011713
21010e022327371732370121022913f3010afe70385a7e5a6601576033fe5b010e024b370279fc7e7e693805c00461037f0000010076feaf0524048d000b0042b2090c0d11123900b0032fb0004558b0
072f1bb1071d3e59b0004558b00a2f1bb10a1d3e59b0004558b0052f1bb1050f3e59b208010a2b5821d81bf459b000d030312533032311211133112111330462c214ddfc43f30205f4c3fdec0151048d
fc3603ca0001004100000416048d00110046b204121311123900b0004558b0092f1bb1091d3e59b0004558b0102f1bb1101d3e59b0004558b0012f1bb1010f3e59b20d0109111239b00d2fb204010a2b
5821d81bf45930312123110623222627113311141633323711330416f38681eaf001f36f798285f301aa26d2d10166fe9e776c26021f000100760000060e048d000b0041b2070c0d11123900b0004558
b0032f1bb1031d3e59b0004558b0012f1bb1010f3e59b204010a2b5821d81bf459b00310b006d0b00410b008d0b00610b00ad03031212111331121113311211133060efa68f3015ff30160f3048dfc36
03cafc3603ca00010076feaf06d1048d000f0041b20b101111123900b0032fb0004558b0072f1bb1071d3e59b0004558b0042f1bb1040f3e59b200010a2b5821d81bf459b00dd0b009d0b00710b00ad0
b00ed0303125330323112111331121113311211133060fc214ddfa96f3015ff30160f4c3fdec0151048dfc3603cafc3603ca0002000a0000051b048d000c0015005eb2081617111239b00810b014d000
b0004558b0072f1bb1071d3e59b0004558b0032f1bb1030f3e59b00710b205010a2b5821d81bf459b20a0703111239b00a2fb00310b20d010a2b5821d81bf459b00a10b213010a2b5821d81bf4593031
01140607211121352111333216013236353426272311051bf9cffe15fea20252ebdbf9fe3266757162f9017fabd20203c9c4fe6cd0fe9a6b534f6302fe8effff0076000005a9048d0026020800000007
01c2043200000002007600000428048d000b0014004db2031516111239b00310b00cd000b0004558b0062f1bb1061d3e59b0004558b0042f1bb1040f3e59b2070406111239b0072fb213010a2b5821d8
1bf459b00410b214010a2b5821d81bf45930310114062321113311333216160132363534262723110428ffd2fe1ff3f28cd26ffe3266757162f9017fafd0048dfe6c5fabfed46b534f6302fe8e000001
003cfff00430049d001d0087b2031e1f11123900b0004558b0122f1bb1121d3e59b0004558b01a2f1bb11a0f3e59b2001a12111239b203010a2b5821d81bf459b209121a111239b0092fb2cf090171b2
3f090171b46f097f090271b4af09bf09025db2ff090171b20f090172b25f090172b206010a2b5821d81bf459b01210b20b010a2b5821d81bf459b20e121a111239303101161633323637213521022322
0607233624333200171714060623222427012f0d7c7882800afe7f018016fb727d0cf3140104d6e201170c017bea9bdcfef80f018470629f94c401316970c2dafee8f075a9ff88daba0000020076fff0
0641049d0013002100afb2042223111239b00410b019d000b0004558b0102f1bb1101d3e59b0004558b00b2f1bb10b1d3e59b0004558b0032f1bb1030f3e59b0004558b0082f1bb1080f3e59b20d080b
111239b00d2fb4af0dbf0d025db46f0d7f0d0271b2ff0d0171b20f0d0172b48f0d9f0d0272b25f0d0172b2cf0d0171b23f0d0171b41f0d2f0d025db2cf0d0172b206010a2b5821d81bf459b01010b217
010a2b5821d81bf459b00310b21e010a2b5821d81bf4593031011000232200272311231133113336003332001127342623220615151416333236350641fedfeddefee213bcf2f2bc14011ddcf00120f2
9688869899878894022cfef8fecc0110e2fe1e048dfe18e9010ffec7fef508b7c0c0b735b2c7c3b60002004300000412048d000c0015005ab2061617111239b00610b010d000b0004558b0072f1bb107
1d3e59b0004558b0092f1bb1090f3e59b2110907111239b0112fb20a010a2b5821d81bf459b2010a11111239b00910b00cd0b00710b212010a2b5821d81bf45930313301263534363321112311230313
1416333311232206430116d6f0d301ccf3f1e62e616bdddd616b020a56d1a3b9fb7301bcfe4403224a59014a57000001000a000003ff048d000d0050b2010e0f11123900b0004558b0082f1bb1081d3e
59b0004558b0022f1bb1020f3e59b2070208111239b0072fb204070a2b5821d81bf459b001d0b00810b20b010a2b5821d81bf459b00710b00cd03031012311231123353311211521113302a7d6f3d4d4
0321fdd2d601e6fe1a01e6aa01fdc4fec7000001001afeaf066d048d001900a4b2081a1b11123900b0032fb0004558b0112f1bb1111d3e59b0004558b0052f1bb1050f3e59b0004558b0092f1bb1090f
3e59b0004558b00d2f1bb10d0f3e59b2170911111239b0172fb23f170171b25f170172b2cf170171b4af17bf17025db48f179f170272b207010a2b5821d81bf459b2000717111239b00510b201010a2b
5821d81bf459b00710b00bd0b20f1707111239b01710b012d0b01110b014d0b018d03031011333112311230323112311230321010121133311331133132104c1eebed0abfd5ff360fcfed3015cfec401
1ef754f354f7011e025dfe65fded015101d5fe2b01d5fe2b02540239fe2001e0fe2001e000010076feaf047c048d00100088b200111211123900b0042fb0004558b00c2f1bb10c1d3e59b0004558b00f
2f1bb10f1d3e59b0004558b0092f1bb1090f3e59b0004558b0062f1bb1060f3e59b20d090c111239b00d2fb23f0d0171b25f0d0172b2cf0d0171b4af0dbf0d025db48f0d9f0d0272b208010a2b5821d8
1bf459b200080d111239b00610b201010a2b5821d81bf4593031010133112311230123112311331133012102930121c8d09bfec26af3f3630138011d0252fe70fded015101d5fe2b048dfe2001e00001
0076000004fe048d00140080b205151611123900b0004558b0142f1bb1141d3e59b0004558b0062f1bb1061d3e59b0004558b0112f1bb1110f3e59b0004558b00a2f1bb10a0f3e59b2001114111239b0
002fb23f000171b25f000172b2cf000171b4af00bf00025db48f009f000272b004d0b00010b210010a2b5821d81bf459b00cd0b2080c0011123930310133353315330121010121012315233523112311
33016947a3370138011cfe7201aefed1fec23ea347f3f302addede01e0fdc4fdaf01d5cbcbfe2b048d00000100240000054e048d000e0085b2090f1011123900b0004558b0072f1bb1071d3e59b00045
58b00a2f1bb10a1d3e59b0004558b0022f1bb1020f3e59b0004558b00e2f1bb10e0f3e59b2080207111239b0082fb23f080171b25f080172b2cf080171b4af08bf08025db48f089f080272b201010a2b
5821d81bf459b00710b204010a2b5821d81bf459b20c0108111239303101231123112135211133012101012102e16af3fea00253630138011dfe7201adfed101d5fe2b03cac3fe2001e0fdc4fdaf0002
004fffeb059804a50023002e008cb2152f30111239b01510b024d000b0004558b01b2f1bb11b1d3e59b0004558b00b2f1bb10b1d3e59b0004558b0042f1bb1040f3e59b0004558b0002f1bb1000f3e59
b202041b111239b0022fb00b10b20c010a2b5821d81bf459b00410b213010a2b5821d81bf459b00010b223010a2b5821d81bf459b00210b026d0b01b10b22c010a2b5821d81bf4593031052227062320
00033534003315220615151416333337260335341233321217151007163301101736373534262322110598e3ae91a9fedafeac040108db717fcbc01b1bc002dcbfc6dd01a35f5cfd94bea201535bb310
393e013c01183afe012eccb4b126cbcd02aa011e2cea010dfefcec48feffad0b01d2fef46f78f335a090fed2ffff000500000436048d002601d20000000701de003bfed500010015feaf048b048d000f
005ab20a101111123900b0072fb0004558b0012f1bb1011d3e59b0004558b00f2f1bb10f1d3e59b0004558b00b2f1bb10b0f3e59b0004558b0092f1bb1090f3e59b2000f0b111239b204010a2b5821d8
1bf459b20a0b0f1112393031011321010133112311230303210101210227f2011cfe890109c4cf92fffafee40181fe88011a02fa0193fdbefe77fded01510199fe67024b024200010024feaf062e048d
000f005cb209101111123900b0022fb0004558b0082f1bb1081d3e59b0004558b00e2f1bb10e1d3e59b0004558b0042f1bb1040f3e59b200010a2b5821d81bf459b00810b206010a2b5821d81bf459b0
0ad0b00bd0b00010b00cd0b00dd0303125330323112111213521152111211133056ac414defc44fea403a2feac0206f2c3fdec015103c9c4c4fcfa03ca000001004100000416048d0017004fb2041819
11123900b0004558b00c2f1bb10c1d3e59b0004558b0162f1bb1161d3e59b0004558b0012f1bb1010f3e59b210010c111239b0102fb207010a2b5821d81bf459b004d0b01010b013d030312123110607
152335262627113311141617353315363711330416f34c56a3cccf02f35456a34a58f301aa160accc80dd1bf016afe9f6b690cf3f20918021f00000100760000044b048d00110046b204121311123900
b0004558b0012f1bb1011d3e59b0004558b0102f1bb1100f3e59b0004558b0092f1bb1090f3e59b2041001111239b0042fb20d010a2b5821d81bf4593031133311363332161511231134262322071123
76f38680edeff375748185f3048dfe5626d6d1fe9e01617c6926fde00002000afff005a804a3001b00230064b20d2425111239b00d10b01dd000b0004558b00e2f1bb10e1d3e59b0004558b0002f1bb1
000f3e59b2200e00111239b0202fb212010a2b5821d81bf459b003d0b02010b00ad0b00010b215010a2b5821d81bf459b00e10b21c010a2b5821d81bf459303105200027262635331416173e02332000
1115211221323737170606032206072135342603c9fefafec00caebfc15458098ff19101000117fcc012014f86732f413bc5a180a008024c95100111ea0bddbb5d760c92e47efee5fef795fed02b12ba
212c03eea58c168695000002004ffff0048104a30016001e005eb2081f20111239b00810b017d000b0004558b0002f1bb1001d3e59b0004558b0082f1bb1080f3e59b20d0008111239b00d2fb00010b2
10010a2b5821d81bf459b00810b217010a2b5821d81bf459b00d10b21a010a2b5821d81bf4593031012000171514060623200011352126262322070727363613323637211514160239010b013b028cf9
96fefefeeb033f07b3a686762d4140c998819e0afdb49404a3fedcf97a9bf988011c010895969a2c11ba222bfc12a38e1686950000010042ffec03e8048d00190069b2121a1b11123900b0004558b002
2f1bb1021d3e59b0004558b00b2f1bb10b0f3e59b00210b200010a2b5821d81bf459b2040200111239b2190b02111239b0192fb005d0b20f0b02111239b00b10b212010a2b5821d81bf459b01910b218
070a2b5821d81bf45930310121352117011616151404232226353316163332363534232335028dfdde035201fec6a2c2ff00dfd0f7f30471657373f17d03c9c49bfec014bf8ba8c0b9a149505a53b0bb
0003004ffff0046f049d000e0015001c007eb2031d1e111239b00310b00fd0b00310b016d000b0004558b00b2f1bb10b1d3e59b0004558b0032f1bb1030f3e59b00b10b20f010a2b5821d81bf459b213
0b031112397cb0132f18b460137013025db430134013025db2f013015db200130171b00310b216010a2b5821d81bf459b01310b219010a2b5821d81bf459303101100023220011353412363332001101
22060721262603323637211616046ffedfedecfeda85f09bf00120fdf079940e02360e937879910efdcc0f95022cfef8fecc0135010c2eac01078bfec7fef5017f9d95959dfcdb9d93939d0000010038
0000041a049d002700aeb225282911123900b0004558b01d2f1bb11d1d3e59b0004558b00c2f1bb10c0f3e59b2061d0c111239b0062fb20f06015db001d0b0012fb2cf01015d40091f012f013f014f01
045db20001015db202020a2b5821d81bf459b00610b207020a2b5821d81bf459b00c10b20a010a2b5821d81bf459b00ed0b00fd0b00710b011d0b00610b013d0b00210b016d0b00110b018d0b01d10b2
24010a2b5821d81bf459b2212401111239b20c21015d30310121152117152115210607210721353336372335333527233533272636333216152334262322061701c40183fe8203017bfe731226029801
fc650a341296a1039e990106d8bfc4d7f354534d570502ba924216934535c3c30e6c930e4a9227ceeed0b65a677e790000010046fff003b0049e002200a0b20a232411123900b0004558b0162f1bb116
1d3e59b0004558b0092f1bb1090f3e59b2221609111239b0222fb20f22015db410222022025db200020a2b5821d81bf459b00910b204010a2b5821d81bf459b00010b00cd0b02210b00ed0b02210b013
d0b0132fb2cf13015db61f132f133f13035db20013015db210020a2b5821d81bf459b01610b21b010a2b5821d81bf459b01310b01dd0b01010b01fd03031012116163332371706232224272335333523
3533363633321707262322072115211521034efe83117b6f50791b766ed4feff1a979292981affd36c7a165b75d622017cfe7d018301846a681cbf1fd0c4925c93c3d620bf1cd6935c00000400760000
07c7049e0003000f001d002700aab21e2829111239b01e10b001d0b01e10b004d0b01e10b010d000b0004558b0262f1bb1261d3e59b0004558b0242f1bb1241d3e59b0004558b0062f1bb1061d3e59b0
004558b0212f1bb1210f3e59b0004558b01f2f1bb11f0f3e59b00610b00dd0b00d2fb002d0b0022fb6000210022002035db201020a2b5821d81bf459b00d10b213020a2b5821d81bf459b00610b21a02
0a2b5821d81bf459b2202421111239b2251f261112393031252135210134362016151514062026351714163332363735342623220615012301112311330111330788fdc5023bfd8abf0136c0befecac1
af5a535058025d4f4e5dfea6f2fdf4f3f3020cf2c89501f296b9b89c4896b8b89b0557656254535764635bfcb4031bfce5048dfce4031c0000020028000004aa048d0015001e008cb20d1f20111239b0
0d10b017d000b0004558b00c2f1bb10c1d3e59b0004558b0032f1bb1030f3e59b206030c111239b0062fb205010a2b5821d81bf459b001d0b00610b00ad0b00a2fb60f0a1f0a2f0a035db68f0a9f0aaf
0a035db41f0a2f0a0271b209010a2b5821d81bf459b013d0b00610b014d0b00a10b016d0b00c10b21e010a2b5821d81bf459303125211523352335333523353311213216100607211521013332363534
26232302f6fef5f3d0d0d0d001ebd1f6edc8fef6010bfef5f86173755ef9999999b64db7023ad3feb4cd054d0104675556650002007cffec04460600000f001a0064b2131b1c111239b01310b00cd000
b0092fb0004558b00c2f1bb10c1b3e59b0004558b0032f1bb1030f3e59b0004558b0062f1bb1060f3e59b2050c03111239b20a0c03111239b00c10b213010a2b5821d81bf459b00310b218010a2b5821
d81bf45930310114022322270723113311363332121127342623220711163332360446f3c7c06d11d2f369b2ccf0f38b7b9a4447997a8a0211f4fecf8e7a0600fdd27cfed6fefa08a6bb85fe3787bc00
00010050ffec0400044e001d004bb2171e1f11123900b0004558b0102f1bb1101b3e59b0004558b0082f1bb1080f3e59b200010a2b5821d81bf459b2030810111239b2141008111239b01010b217010a
2b5821d81bf459303125323637330e022322003535343636333216172326262322061515141602425a7a06e4047aca74e6fef27ae198c3f406e407785c798585ae694f66b064012bfe199efb87e4b45f
76b3b21badb00002004fffec041706000011001c0064b21a1d1e111239b01a10b004d000b0072fb0004558b0042f1bb1041b3e59b0004558b00d2f1bb10d0f3e59b0004558b0092f1bb1090f3e59b206
040d111239b20b040d111239b00d10b215010a2b5821d81bf459b00410b21a010a2b5821d81bf459303113343636333217113311232706232226263537141633323711262322064f70cd82ac6af3d311
6cbb7ecb74f38d7b944646927d8d02269ffd8c770229fa0075898cfd9b019dc28101d77dc100ffff005b000002b205b500060015b3000002004cffec0455044e000f00190043b2041a1b111239b00410
b017d000b0004558b0042f1bb1041b3e59b0004558b00c2f1bb10c0f3e59b212010a2b5821d81bf459b00410b217010a2b5821d81bf45930311334363633320015151406062322003517141632363534
2622064c82eb96e601207fed98e6fee1f295fc9397f89502279ffd8bfecdfc0d9dfc8d0131fe09a0c4c4b59fc5c60002007cfe600444044e0010001b006eb2191c1d111239b01910b00dd000b0004558
b00d2f1bb10d1b3e59b0004558b00a2f1bb10a1b3e59b0004558b0072f1bb107113e59b0004558b0042f1bb1040f3e59b2060d04111239b20b0d04111239b00d10b214010a2b5821d81bf459b00410b2
19010a2b5821d81bf45930310114060623222711231133173633321217073426232207111633323604446fc881b16cf3d90e6cbac1ef0af1917c92444593789302119efd8a74fe0005da7185feebec27
9fc278fe1778c3000002004ffe600416044e0010001b006bb2191c1d111239b01910b004d000b0004558b0042f1bb1041b3e59b0004558b0072f1bb1071b3e59b0004558b0092f1bb109113e59b00045
58b00d2f1bb10d0f3e59b206040d111239b20b040d111239b214010a2b5821d81bf459b00410b219010a2b5821d81bf4593031133436363332173733112311062322022737141633323711262322064f
6fcd86b76b11d2f36aaabef60bf293789046488c7e8f0226a2fc8a826efa2601fc70011ce2279ec57601f473c60000020053ffec040b044e0016001e007cb2081f20111239b00810b017d000b0004558
b0082f1bb1081b3e59b0004558b0002f1bb1000f3e59b21b0800111239b01b2fb4bf1bcf1b025db45f1b6f1b0271b41f1b2f1b0271b28f1b015db4ef1bff1b0271b20c070a2b5821d81bf459b00010b2
10010a2b5821d81bf459b00810b217010a2b5821d81bf4593031052200353534363633321215152116163332363717060603220607213534260276f2fecf7de28bddf1fd3e0fa98d5592313a3fbda766
7c1001d073140128f7219ef98bfef4f77b859d2f20a63239039f8d7c1a707f0000020051fe560404044e001900240083b2222526111239b02210b00bd000b0004558b0032f1bb1031b3e59b0004558b0
062f1bb1061b3e59b0004558b00b2f1bb10b113e59b0004558b0172f1bb1170f3e59b2050317111239b00b10b211010a2b5821d81bf459b20f1117111239b2150317111239b01710b21d010a2b5821d8
1bf459b00310b222010a2b5821d81bf45930311334123332173733111400232226273716333236353506232202371416333237112623220651e7c3bd6b11d0fefaed57af373575838e826aaebeeaf281
739743449476800226fd012b8672fc10f2fefe2e21b03f96942276012ff6a8b78501d17fb5000001006bffeb052605c5001d0040b20c1e1f11123900b0004558b00c2f1bb10c1f3e59b0004558b0032f
1bb1030f3e59b00c10b213010a2b5821d81bf459b00310b21a010a2b5821d81bf4593031010600232224022735341224333200172326262322021515141233323637052417fed2f9b6fedca0019e0120
b7fb013417fd16a390acccd2ac919b1601dae9fefab40145d23cd5014ab4fef3e99892fee6ef34ebfee48f960001006bffeb052605c500200055b20c212211123900b0004558b00c2f1bb10c1f3e59b0
004558b0032f1bb1030f3e59b00c10b212010a2b5821d81bf459b00310b219010a2b5821d81bf459b2200c03111239b0202fb21d010a2b5821d81bf45930312506042322240227353412243332041723
02212202071514123332363711213521052646fedcb0c0fecead029f0123b7f8012b1ff92efee9aad303e8bc649b1ffedd021fbc5f72b20148d131d9014fb6f0e30107fee5e933ecfedf3024011bc000
0002009b0000051705b0000b00150046b2031617111239b00310b00fd000b0004558b0012f1bb1011f3e59b0004558b0002f1bb1000f3e59b00110b20c010a2b5821d81bf459b00010b20d010a2b5821
d81bf4593031331121320412171514020407031133321235353402239b01bec80141b203b0fec0ccc4aedcf8f1da05b0b1fec3c838ccfebfb20304e4fbe6010ef026ea010c000002006bffeb057205c5
001100200046b2042122111239b00410b01dd000b0004558b00d2f1bb10d1f3e59b0004558b0042f1bb1040f3e59b00d10b215010a2b5821d81bf459b00410b21d010a2b5821d81bf459303101140204
23222402273534122433320412170734022322021515141616333212370572a6fed8b4b2fed8aa01a5012ab4b20126a804fbdcada9df66b66ea4d80a02c3cefeb0baba014ec931cb014dc0b7feb9c612
e40122fedbe82593f1860109da000002006bff03057205c5001400230046b2082425111239b00810b020d000b0004558b0102f1bb1101f3e59b0004558b0082f1bb1080f3e59b01010b218010a2b5821
d81bf459b00810b220010a2b5821d81bf459303101140207170725062322240227353412242004121707340223220215151416163332123505729789efa5fed5433eb3fedaaa02a7012801680127a801
fbdcadaade66b56faed902c6cafebd62c094f50db7014dcb2ed00152bbb7feafce05ec011ffeddef1d97f2840120f50000010097000002ef048c0006003200b0004558b0052f1bb1051d3e59b0004558
b0002f1bb1000f3e59b2040005111239b0042fb203010a2b5821d81bf45930312123110535253302eff3fe9b02391f03697acdd00001006e0000042c049e00190059b2091a1b11123900b0004558b011
2f1bb1111d3e59b0004558b0002f1bb1000f3e59b218010a2b5821d81bf459b2021800111239b2030011111239b01110b209010a2b5821d81bf459b20c0011111239b217110011123930312121350136
363534262322061523343636333216151406070121042cfc6001fb4639695a677bf379d785caea576efeb102499f01ba3f6340485a786073bc6ab79c5a9f66fed600000100760000039705c400070032
b203080911123900b0004558b0062f1bb1061d3e59b0004558b0052f1bb1050f3e59b00610b202010a2b5821d81bf4593031013311211123112102a4f3fdd2f3022e05c4fe05fc37048d0001000ffea3
03f2048d00190059b2121a1b11123900b00c2fb0004558b0022f1bb1021d3e59b200010a2b5821d81bf459b2040002111239b2050c02111239b0052fb00c10b211010a2b5821d81bf459b00510b21703
0a2b5821d81bf459b219170511123930310121352115011616151406042322273716333236353426232335029efdba0377fe9dabdb90fef2b0c7ce399dada4c4aab74803c9c48ffe801af7b0a3f38467
b658b89296927b0000020035fec4048b048c000a000e005200b0004558b0092f1bb1091d3e59b0004558b0022f1bb1020f3e59b0004558b0062f1bb1060f3e59b200010a2b5821d81bf459b00610b005
d0b0052fb2080600111239b00010b00cd0b20d0902111239303125331523112311212701330121110703d5b6b6f2fd580602a6fafd6401aa17c2c3fec5013b9403f9fc3602802a00ffff004b028d02aa
05b8030701d400000298001300b0004558b00a2f1bb10a1f3e59b010d0303100ffff0035029802be05ad030701d800000298001300b0004558b0092f1bb1091f3e59b00dd0303100ffff004f028d02ae
05ad030701d900000298001000b0004558b0012f1bb1011f3e593031ffff004d028d02b905ba030701da00000298001300b0004558b0002f1bb1001f3e59b014d0303100ffff0036029802ae05ad0307
01db00000298001000b0004558b0052f1bb1051f3e593031ffff004b028d02aa05b8030701dc00000298001900b0004558b0112f1bb1111f3e59b019d0b01110b01fd0303100ffff0046028f02a305b8
030701dd00000298001300b0004558b0082f1bb1081f3e59b01ad030310000010066fea0041e048c001c005db2191d1e11123900b00e2fb0004558b0012f1bb1011d3e59b203010a2b5821d81bf459b2
07010e111239b0072fb219010a2b5821d81bf459b2050719111239b00e10b213010a2b5821d81bf459b2111319111239b21c191311123930311313211521033637361215140606232227371633323635
342623220607875a0329fd9a2d6586cfed85f5a5e4b54a84bd8fab8e7853661b01750317d2feaa320202fef7e498f38275b263b39487a2353b0000010043fec40410048c0006002500b0012fb0004558
b0052f1bb1051d3e59b203010a2b5821d81bf459b20003051112393031010123012135210410fdb6f3023efd3203cd0406fabe0505c30002004ffff0066d049d0014001e0091b2161f20111239b01610
b00bd000b0004558b00a2f1bb10a1d3e59b0004558b00b2f1bb10b1d3e59b0004558b0002f1bb1000f3e59b0004558b0022f1bb1020f3e59b00b10b20d010a2b5821d81bf459b210000b111239b0102f
b211010a2b5821d81bf459b00010b213010a2b5821d81bf459b00210b215010a2b5821d81bf459b00a10b218010a2b5821d81bf459303121210522001135341236330521152111211521112105371127
220615151416066dfd47feadecfeda85f09b015302b8fdb701f6fe0a024cfbf4cdcf869899100135010c2eac01078b10c4fef2c3feca0f08031409c0b735b2c700020073feb4045404a0001800240053
b21f2526111239b01f10b00cd000b0142fb0004558b00c2f1bb10c1d3e59b01410b200010a2b5821d81bf459b219140c1112397cb0192f18b205010a2b5821d81bf459b00c10b21f010a2b5821d81bf4
5930310532363706232202353436363332001115140204232227371613323735342623220615141601e998bd1972aad1f77bda87f1011491fef3b29e842f7dd1b052887f6d878a89c8be5a0112e599ed
80fed1fef6cee5feb2b23cb62f01e978aca5b4b1928ab00000020062ffeb048504a0000d001a0046b2031b1c111239b00310b017d000b0004558b00a2f1bb10a1d3e59b0004558b0032f1bb1030f3e59
b00a10b211010a2b5821d81bf459b00310b216010a2b5821d81bf45930310110002322260235100033321612073426200615151416333236370485fee3f39ef382011ff29ff281f29bfef6999a868597
02023efee9fec48e010cc70116013e8efef3a7b8c7c8ba2cb5cdc5b4ffffffb5fe4b0193043a0206009b0000ffffffb5fe4b0193043a0206009b0000ffff008f00000182043a0006008c0000fffffffb
fe5c0182043a0026008c0000000600a3d20affff008f00000182043a0006008c000000010076ffeb0416049c00210065b201222311123900b0004558b0152f1bb1151d3e59b0004558b01f2f1bb11f0f
3e59b0004558b0102f1bb1100f3e59b01f10b202010a2b5821d81bf459b20a1f15111239b00a2fb019d0b208030a2b5821d81bf459b01510b20d010a2b5821d81bf45930312516333236353426232335
132623221511231136363332161703161615140623222701eb4b484d5c7c7454ca4651b1ef01d1cf78cd68f9a1aad9af7c6cdb3165525847a3010139f9fd1c02f0d7d5616ffed417a481afca3600ffff
00470209025402cd0206001100000002fff7000004f005b0000f001d0082b2101e1f111239b01010b006d000b0004558b0052f1bb1051f3e59b0004558b0002f1bb1000f3e59b2030005111239b0032f
b2cf03015db23f030171b26f030171b21f030171b29f03015db20f030172b202070a2b5821d81bf459b011d0b00010b212010a2b5821d81bf459b00510b21b010a2b5821d81bf459b00310b01dd03031
331123353311213204121515140204231323113332363535342623231133b2bbbb01aec1012ba4a5fecfc53fe5a3cbd5cec4b1e5028caa027aacfec4cc49cffec6aa028cfe3efdf046edfafe52000002
fff7000004f005b0000f001d0082b2101e1f111239b01010b006d000b0004558b0052f1bb1051f3e59b0004558b0002f1bb1000f3e59b2030005111239b0032fb2cf03015db23f030171b26f030171b2
1f030171b29f03015db20f030172b202070a2b5821d81bf459b011d0b00010b212010a2b5821d81bf459b00510b21b010a2b5821d81bf459b00310b01dd0303133112335331121320412151514020423
1323113332363535342623231133b2bbbb01aec1012ba4a5fecfc53fe5a3cbd5cec4b1e5028caa027aacfec4cc49cffec6aa028cfe3efdf046edfafe52000001ffd400000416060000180074b20c191a
11123900b0152fb0004558b0042f1bb1041b3e59b0004558b0072f1bb1070f3e59b0004558b00f2f1bb10f0f3e59b22f15015db20f15015db2180f15111239b0182fb200070a2b5821d81bf459b20204
0f111239b00410b20c010a2b5821d81bf459b00010b011d0b01810b013d03031012311363320131123113426232207112311233533353315330271e777b6015a05f3615e9248f3c3c3f3e704c7fefd8a
fe75fd3d02ba705d82fcfb04c7aa8f8f0001002d000004b005b0000f004c00b0004558b00a2f1bb10a1f3e59b0004558b0022f1bb1020f3e59b20f0a02111239b00f2fb200070a2b5821d81bf459b004
d0b00f10b006d0b00a10b208010a2b5821d81bf459b00cd030310123112311233533112135211521113303b9cffbd3d3fe3e0483fe3acf0312fcee0312aa0128ccccfed80001ffe8ffec02850541001c
0072b2001d1e11123900b0004558b01b2f1bb11b1b3e59b0004558b0112f1bb1110f3e59b01b10b001d0b01b10b218010a2b5821d81bf459b004d0b01b10b017d0b0172fb005d0b0052fb01710b21407
0a2b5821d81bf459b008d0b01110b20c010a2b5821d81bf459b01b10b01cd0b01c2f3031011133152315331523111416333237150623201111233533352335331101adbfbfd8d8313f2a2b534dfee8d2
d2b2b20541fef9b4a5aafef33e370abc1701350116aaa5b40107ffff00120000054207360226002500000107004401230136001300b0004558b0042f1bb1041f3e59b00cdc303100ffff001200000542
07360226002500000107007501c20136001300b0004558b0052f1bb1051f3e59b00ddc303100ffff00120000054207370226002500000107009d00c30136001300b0004558b0042f1bb1041f3e59b00f
dc303100ffff001200000542072c022600250000010700a400c50137000900b0042fb016dc303100ffff00120000054207020226002500000107006a00ee0136001600b0004558b0042f1bb1041f3e59
b012dcb01bd03031ffff0012000005420794022600250000010700a20158016a000c00b0042fb010dcb015d03031ffff00120000054207b1022600250000000701df015e011cffff0066fe3c04eb05c4
0226002700000007007901c9fffbffff00940000044c073d0226002900000107004400e8013d001300b0004558b0062f1bb1061f3e59b00ddc303100ffff00940000044c073d02260029000001070075
0187013d001300b0004558b0062f1bb1061f3e59b00edc303100ffff00940000044c073e0226002900000107009d0088013d001300b0004558b0062f1bb1061f3e59b010dc303100ffff00940000044c
07090226002900000107006a00b3013d001600b0004558b0062f1bb1061f3e59b013dcb01cd03031ffffffc8000001a0073d0226002d000001070044ff97013d001300b0004558b0022f1bb1021f3e59
b005dc303100ffff00a30000027d073d0226002d0000010700750035013d001300b0004558b0032f1bb1031f3e59b006dc303100ffffffcb0000027a073e0226002d00000107009dff37013d001300b0
004558b0022f1bb1021f3e59b008dc303100ffffffbf0000028507090226002d00000107006aff62013d001600b0004558b0022f1bb1021f3e59b00bdcb014d03031ffff009400000517072c02260032
0000010700a400ee0137000900b0052fb015dc303100ffff0066ffec051e073602260033000001070044013a0136001300b0004558b00c2f1bb10c1f3e59b020dc303100ffff0066ffec051e07360226
003300000107007501d90136001300b0004558b00d2f1bb10d1f3e59b021dc303100ffff0066ffec051e07370226003300000107009d00da0136001300b0004558b00c2f1bb10c1f3e59b023dc303100
ffff0066ffec051e072c022600330000010700a400dc0137001300b0004558b00d2f1bb10d1f3e59b022dc303100ffff0066ffec051e07020226003300000107006a01050136001600b0004558b00c2f
1bb10c1f3e59b026dcb02fd03031ffff007dffec04bd07360226003900000107004401110136001300b0004558b0092f1bb1091f3e59b012dc303100ffff007dffec04bd073602260039000001070075
01b00136000900b0002fb013dc303100ffff007dffec04bd07370226003900000107009d00b10136001300b0004558b0092f1bb1091f3e59b015dc303100ffff007dffec04bd07020226003900000107
006a00dc0136001600b0004558b0092f1bb1091f3e59b018dcb021d03031ffff0007000004d607360226003d00000107007501870136001300b0004558b0012f1bb1011f3e59b00bdc303100ffff005a
ffec03fb06000226004500000107004400ad0000001300b0004558b0172f1bb1171b3e59b02bdc303100ffff005affec03fb060002260045000001070075014c0000000900b0172fb02cdc303100ffff
005affec03fb06010226004500000106009d4d00001300b0004558b0172f1bb1171b3e59b02edc303100ffff005affec03fb05f6022600450000010600a44f01001300b0004558b0172f1bb1171b3e59
b02ddc303100ffff005affec03fb05cc0226004500000106006a7800001600b0004558b0172f1bb1171b3e59b031dcb03ad03031ffff005affec03fb065e022600450000010700a200e20034001600b0
004558b0172f1bb1171b3e59b02fdcb037d03031ffff005affec03fb067c022600450000000701df00e8ffe7ffff004ffe3c03f5044e02260047000000070079013dfffbffff0053ffec040b06000226
004900000107004400a10000001300b0004558b0082f1bb1081b3e59b01fdc303100ffff0053ffec040b06000226004900000107007501400000000900b0082fb020dc303100ffff0053ffec040b0601
0226004900000106009d4100001300b0004558b0082f1bb1081b3e59b022dc303100ffff0053ffec040b05cc0226004900000106006a6c00001600b0004558b0082f1bb1081b3e59b025dcb02ed03031
ffffffb40000018c05f90226008c00000106004483f9001300b0004558b0022f1bb1021b3e59b005dc303100ffff008f0000026905f90226008c00000106007521f9001300b0004558b0032f1bb1031b
3e59b006dc303100ffffffb70000026605fa0226008c00000107009dff23fff9001300b0004558b0022f1bb1021b3e59b008dc303100ffffffab0000027105c50226008c00000107006aff4efff90016
00b0004558b0022f1bb1021b3e59b00bdcb014d03031ffff0079000003f805f6022600520000010600a45501000900b0032fb01cdc303100ffff004fffec043d06000226005300000107004400b60000
001300b0004558b0042f1bb1041b3e59b01cdc303100ffff004fffec043d06000226005300000107007501550000000900b0042fb01ddc303100ffff004fffec043d06010226005300000106009d5600
001300b0004558b0042f1bb1041b3e59b01fdc303100ffff004fffec043d05f6022600530000010600a45801000900b0042fb026dc303100ffff004fffec043d05cc0226005300000107006a00810000
001600b0004558b0042f1bb1041b3e59b022dcb02bd03031ffff0077ffec03f706000226005900000107004400af0000001300b0004558b0072f1bb1071b3e59b012dc303100ffff0077ffec03f70600
02260059000001070075014e0000000900b0062fb013dc303100ffff0077ffec03f706010226005900000106009d4f00001300b0004558b0072f1bb1071b3e59b015dc303100ffff0077ffec03f705cc
0226005900000106006a7a00001600b0004558b0072f1bb1071b3e59b018dcb021d03031ffff000cfe4b03d606000226005d00000107007501160000000900b0012fb012dc303100ffff000cfe4b03d6
05cc0226005d00000106006a4200001600b0004558b00f2f1bb10f1b3e59b017dcb020d03031ffff00120000054206ea0226002500000107007000be013a001300b0004558b0042f1bb1041f3e59b00c
dc303100ffff005affec03fb05b4022600450000010600704804000900b0172fb02adc303100ffff001200000542071c022600250000010700a000f60136001300b0004558b0042f1bb1041f3e59b00e
dc303100ffff005affec03fb05e6022600450000010700a000800000001300b0004558b0172f1bb1171b3e59b02ddc30310000020012fe52054205b0001600190074b2191a1b111239b01910b016d000
b0004558b0162f1bb1161f3e59b0004558b0142f1bb1140f3e59b0004558b0012f1bb1010f3e59b0004558b00c2f1bb10c113e59b207030a2b5821d81bf459b00110b011d0b0112fb2171416111239b0
172fb213010a2b5821d81bf459b219161411123930310101230606151433323717062322263534370321032101032103031b02273e574a472c2e15495c5f749573fdcc76fef902266201a6d305b0fa50
385e3144178e2c6e5b8d620149fead05b0fc6f025c000002005afe5203fb044e002d003800a6b217393a111239b01710b02fd000b0004558b0172f1bb1171b3e59b0004558b0292f1bb129113e59b000
4558b0042f1bb1040f3e59b0004558b01e2f1bb11e0f3e59b000d0b0002fb2021704111239b20b1704111239b00b2fb01710b20f010a2b5821d81bf459b2120b0f11123940090c121c122c123c12045d
b02910b224030a2b5821d81bf459b00410b22e010a2b5821d81bf459b00b10b232010a2b5821d81bf4593031252627062322263534243333353426232206152334363633321617111417152306061514
33323717062322263534033236373523220615141602ff0b0d74a8a3ce0101ef955e60536af376cb7dbee203292a574a472c2e15495c5f7476487f208387885d07194579ba89adb94754655340599b58
bfadfe18925711385e3144178e2c6e5b8c0108463bcc5e564653ffff0066ffec04eb074b0226002700000107007501c0014b000900b00c2fb020dc303100ffff004fffec03f506000226004700000107
007501290000000900b00f2fb01fdc303100ffff0066ffec04eb074c0226002700000107009d00c1014b001300b0004558b00c2f1bb10c1f3e59b020dc303100ffff004fffec03f50601022600470000
0106009d2a00001300b0004558b00f2f1bb10f1b3e59b01fdc303100ffff0066ffec04eb0729022600270000010700a101a70154001300b0004558b00c2f1bb10c1f3e59b026dc303100ffff004fffec
03f505de022600470000010700a101100009001300b0004558b00f2f1bb10f1b3e59b025dc303100ffff0066ffec04eb074c0226002700000107009e00d8014b000900b00c2fb022dc303100ffff004f
ffec03f506010226004700000106009e4100000900b00f2fb021dc303100ffff0094000004d2073e0226002800000107009e0067013d000900b0012fb01adc303100ffff004fffec055b060200260048
0000010701a2040104fc000600b01e2f3031ffff00940000044c06f10226002900000107007000830141001300b0004558b0062f1bb1061f3e59b00ddc303100ffff0053ffec040b05b4022600490000
010600703c04000900b0082fb01edc303100ffff00940000044c0723022600290000010700a000bb013d001300b0004558b0062f1bb1061f3e59b00fdc303100ffff0053ffec040b05e6022600490000
010600a07400001300b0004558b0082f1bb1081b3e59b021dc303100ffff00940000044c071b022600290000010700a1016e0146001300b0004558b0062f1bb1061f3e59b014dc303100ffff0053ffec
040b05de022600490000010700a101270009001300b0004558b0082f1bb1081b3e59b026dc30310000010094fe52044c05b0001b0080b2111c1d11123900b0004558b0162f1bb1161f3e59b0004558b0
0f2f1bb10f113e59b0004558b0042f1bb1040f3e59b0004558b0142f1bb1140f3e59b21a1416111239b01a2fb201010a2b5821d81bf459b01410b202010a2b5821d81bf459b003d0b00f10b20a030a2b
5821d81bf459b01610b218010a2b5821d81bf45930310121112115230606151433323717062322263534372111211521112103e7fdaa02bb6f574a472c2e15495c5f7487fd9303b1fd4c0256028afe40
ca385e3144178e2c6e5b865f05b0ccfe6e0000020053fe6d040b044e0023002b00a5b2112c2d111239b01110b024d000b0004558b0192f1bb1191b3e59b0004558b00c2f1bb10c113e59b0004558b011
2f1bb1110f3e59b2021119111239b00c10b207030a2b5821d81bf459b2281911111239b0282fb41f282f280271b4bf28cf28025db28f28015db45f286f280271b4ef28ff280271b21d070a2b5821d81b
f459b01110b221010a2b5821d81bf459b2231911111239b01910b224010a2b5821d81bf45930312506070606151433323717062322263534372600273534363633321211152116163332370122060721
35262603fa4971574a472c2e15495c5f7450cffefb067de28bddf1fd3d0b9d77a769fec5647b1101cf0872b86a33385e3144178e2c6e5b66520d0113d73aa2ff8efee6fefe62869c8702568c7d127a7d
ffff00940000044c073e0226002900000107009e009f013d001300b0004558b0062f1bb1061f3e59b011dc303100ffff0053ffec040b06010226004900000106009e5800000900b0082fb022dc303100
ffff006affec04f0074c0226002b00000107009d00be014b001300b0004558b00b2f1bb10b1f3e59b021dc303100ffff0052fe56040c06010226004b00000106009d4000001300b0004558b0032f1bb1
031b3e59b027dc303100ffff006affec04f007310226002b0000010700a000f1014b001300b0004558b00b2f1bb10b1f3e59b022dc303100ffff0052fe56040c05e60226004b0000010600a073000013
00b0004558b0032f1bb1031b3e59b028dc303100ffff006affec04f007290226002b0000010700a101a40154001300b0004558b00b2f1bb10b1f3e59b027dc303100ffff0052fe56040c05de0226004b
0000010700a101260009001300b0004558b0032f1bb1031b3e59b02ddc303100ffff006afdf904f005c40226002b0000000701a201bbfe92ffff0052fe56040c06a90226004b0000010701b90127007e
000900b0032fb029dc303100ffff009400000518073e0226002c00000107009d00e2013d001300b0004558b0072f1bb1071f3e59b010dc303100ffff0079000003f8075e0226004c00000107009d0017
015d000900b0102fb013dc303100ffffffb30000029007330226002d0000010700a4ff39013e001300b0004558b0032f1bb1031f3e59b007dc303100ffffff9f0000027c05ef0226008c0000010700a4
ff25fffa000900b0022fb00fdc303100ffffffb90000029006f10226002d000001070070ff320141001300b0004558b0022f1bb1021f3e59b005dc303100ffffffa50000027c05ad0226008c00000107
0070ff1efffd001300b0004558b0022f1bb1021b3e59b005dc303100ffffffdf0000026507230226002d0000010700a0ff6a013d001300b0004558b0022f1bb1021f3e59b007dc303100ffffffcb0000
025105df0226008c0000010700a0ff56fff9001300b0004558b0022f1bb1021b3e59b007dc303100ffff0017fe58019f05b00226002d0000000600a3ee06ffff0000fe52019005d50226004d00000006
00a3d700ffff009d000001a3071b0226002d0000010700a1001c0146001300b0004558b0022f1bb1021f3e59b00cdc303100ffff00a3ffec062605b00026002d00000007002e02420000ffff007dfe4b
039005d50026004d00000007004e020b0000ffff002dffec04ab07370226002e00000107009d01680136001300b0004558b0002f1bb1001f3e59b014dc303100ffffffb5fe4b026b05df0226009b0000
0107009dff28ffde001300b0004558b00c2f1bb10c1b3e59b011dc303100ffff0094fdf9051805b00226002f0000000701a2019dfe92ffff007dfdf9043606000226004f0000000701a2012dfe92ffff
00940000042607360226003000000107007500290136001300b0004558b0052f1bb1051f3e59b008dc303100ffff008a00000262079102260050000001070075001a0191001300b0004558b0032f1bb1
03213e59b006dc303100ffff0094fdf9042605b0022600300000000701a2016dfe92ffff0055fdf9017f0600022600500000000701a20010fe92ffff00940000042605b1022600300000010701a2020a
04ab001000b0004558b00a2f1bb10a1f3e593031ffff008c000002e70602002600500000010701a2018d04fc001000b0004558b0082f1bb108213e593031ffff00940000042605b00226003000000007
00a101cafdd4ffff008c000002eb0600002600500000000700a10164fdafffff00940000051707360226003200000107007501eb0136001300b0004558b0082f1bb1081f3e59b00cdc303100ffff0079
000003f806000226005200000107007501520000000900b0032fb013dc303100ffff0094fdf9051705b0022600320000000701a201dcfe92ffff0079fdf903f8044e022600520000000701a20141fe92
ffff00940000051707370226003200000107009e01030136001300b0004558b0062f1bb1061f3e59b00fdc303100ffff0079000003f806010226005200000106009e6a00000900b0032fb015dc303100
ffffffa5000003f80603022600520000010701a2ff6004fd001000b0004558b0152f1bb115213e593031ffff0066ffec051e06ea0226003300000107007000d5013a001300b0004558b00c2f1bb10c1f
3e59b020dc303100ffff004fffec043d05b4022600530000010600705104000900b0042fb01bdc303100ffff0066ffec051e071c022600330000010700a0010d0136001300b0004558b00c2f1bb10c1f
3e59b022dc303100ffff004fffec043d05e6022600530000010700a000890000001300b0004558b0042f1bb1041b3e59b01edc303100ffff0066ffec051e0735022600330000010700a5016301360016
00b0004558b00d2f1bb10d1f3e59b021dcb025d03031ffff004fffec043d05ff022600530000010700a500df0000001600b0004558b0042f1bb1041b3e59b01ddcb021d03031ffff0094000004de0736
0226003600000107007501710136000900b0042fb01adc303100ffff007c000002f506000226005600000107007500ad0000000900b00b2fb010dc303100ffff0094fdf904de05b00226003600000007
01a2016efe92ffff004ffdf902b4044e022600560000000701a2000afe92ffff0094000004de07370226003600000107009e00890136000900b0042fb01cdc303100ffff0038000002fa060102260056
00000106009ec600000900b00b2fb012dc303100ffff004affec048a073602260037000001070075018e0136000900b0092fb02adc303100ffff004bffec03ca060002260057000001070075013a0000
000900b0092fb029dc303100ffff004affec048a07370226003700000107009d008f0136001300b0004558b0092f1bb1091f3e59b02adc303100ffff004bffec03ca06010226005700000106009d3b00
001300b0004558b0092f1bb1091b3e59b029dc303100ffff004afe41048a05c402260037000000070079019d0000ffff004bfe3803ca044e022600570000000700790144fff7ffff004afdf9048a05c4
022600370000000701a20189fe92ffff004bfdf903ca044e022600570000000701a20130fe92ffff004affec048a07370226003700000107009e00a60136000900b0092fb02cdc303100ffff004bffec
03ca06010226005700000106009e5200000900b0092fb02bdc303100ffff002dfdf904b005b0022600380000000701a20177fe92ffff0008fdf902720541022600580000000701a200c8fe92ffff002d
fe4404b005b002260038000000070079018b0003ffff0008fe4102a505410226005800000007007900dc0000ffff002d000004b007370226003800000107009e00980136001300b0004558b0062f1bb1
061f3e59b00ddc303100ffff0008ffec03270683002600580000000701a201cd057dffff007dffec04bd072c022600390000010700a400b30137001300b0004558b0102f1bb1101f3e59b014dc303100
ffff0077ffec03f705f6022600590000010600a45101001300b0004558b00d2f1bb10d1b3e59b014dc303100ffff007dffec04bd06ea0226003900000107007000ac013a000900b0002fb011dc303100
ffff0077ffec03f705b4022600590000010600704a04001300b0004558b0062f1bb1061b3e59b012dc303100ffff007dffec04bd071c022600390000010700a000e40136001300b0004558b0092f1bb1
091f3e59b014dc303100ffff0077ffec03f705e6022600590000010700a000820000001300b0004558b0072f1bb1071b3e59b014dc303100ffff007dffec04bd0794022600390000010700a20146016a
000c00b0002fb016dcb01bd03031ffff0077ffec03f7065e022600590000010700a200e40034000c00b0062fb016dcb01bd03031ffff007dffec04bd0735022600390000010700a5013a0136001600b0
004558b0102f1bb1101f3e59b013dcb017d03031ffff0077ffec042e05ff022600590000010700a500d80000000c00b0062fb013dcb015d030310001007dfe8904bd05b0001f0057b21c202111123900
b0004558b0182f1bb1181f3e59b0004558b0132f1bb1130f3e59b0004558b00e2f1bb10e173e59b2041318111239b209030a2b5821d81bf459b01310b21c010a2b5821d81bf459b01810b01fd0303101
1114060706061514333237170623222635343720003511331114163320111104bd857e3d4f472c2e15495c5f7436ff00fedbfc9490012405b0fc3298e43d29593744178e2c6e5b5545010ceb03cdfc32
929a013403c600010077fe5203f7043a001f0066b21a202111123900b0004558b0172f1bb1171b3e59b0004558b0122f1bb1120f3e59b0004558b01f2f1bb11f0f3e59b0004558b00a2f1bb10a113e59
b205030a2b5821d81bf459b01f10b00fd0b00f2fb01210b21a010a2b5821d81bf459b01710b01dd03031210606151433323717062322263534372706232226351133111433323711331103e2574a472c
2e15495c5f7492056bc5b0b5f3abb13ef3385e3144178e2c6e5b8c61627ecec302bdfd46ce7f0309fbc6ffff0030000006e507370226003b00000107009d01a80136001300b0004558b00c2f1bb10c1f
3e59b00fdc303100ffff0021000005cc06010226005b00000107009d010a0000001300b0004558b00b2f1bb10b1b3e59b011dc303100ffff0007000004d607370226003d00000107009d008801360013
00b0004558b0012f1bb1011f3e59b00bdc303100ffff000cfe4b03d606010226005d00000106009d1700001300b0004558b00f2f1bb10f1b3e59b014dc303100ffff0007000004d607020226003d0000
0107006a00b30136001600b0004558b0082f1bb1081f3e59b010dcb019d03031ffff00500000048c07360226003e00000107007501830136001300b0004558b0072f1bb1071f3e59b00cdc303100ffff
0052000003c006000226005e000001070075011b0000001300b0004558b0072f1bb1071b3e59b00cdc303100ffff00500000048c07140226003e0000010700a1016a013f001300b0004558b0072f1bb1
071f3e59b012dc303100ffff0052000003c005de0226005e0000010700a101020009001300b0004558b0072f1bb1071b3e59b012dc303100ffff00500000048c07370226003e00000107009e009b0136
000900b0072fb00edc303100ffff0052000003c006010226005e00000106009e3300000900b0072fb00edc303100fffffff60000075707420226008100000107007502bb0142001300b0004558b0062f
1bb1061f3e59b015dc303100ffff0048ffec068406010226008600000107007502710001000900b0172fb03fdc303100ffff0069ffa1052207800226008300000107007501e00180001300b0004558b0
102f1bb1101f3e59b02cdc303100ffff004fff77043d05fe022600890000010700750130fffe001300b0004558b0042f1bb1041b3e59b028dc303100ffffffa60000042a048d022601bd0000010701de
ff16ff6e004600b21f170171b26f170171b2ff170171b20f170172b6af17bf17cf170372b2ff170172b25f170172b6bf17cf17df170371b23f170171b4df17ef17025db41f172f17025d3031ffffffa6
0000042a048d022601bd0000010701deff16ff6e004600b21f170171b26f170171b2ff170171b20f170172b6af17bf17cf170372b2ff170172b25f170172b6bf17cf17df170371b23f170171b4df17ef
17025db41f172f17025d3031ffff002400000416048d022601cd0000010601de32be000800b2000b015d3031ffff000900000494061e022601ba00000107004400c7001e001300b0004558b0042f1bb1
041d3e59b00cdc303100ffff000900000494061e022601ba0000010700750166001e001300b0004558b0052f1bb1051d3e59b00ddc303100ffff000900000494061f022601ba00000106009d671e0013
00b0004558b0042f1bb1041d3e59b00fdc303100ffff0009000004940614022601ba0000010600a4691f000900b0042fb016dc303100ffff00090000049405ea022601ba00000107006a0092001e0016
00b0004558b0042f1bb1041d3e59b012dcb01bd03031ffff000900000494067c022601ba0000010700a200fc0052001600b0004558b0042f1bb1041d3e59b010dcb018d03031ffff0009000004940699
022601ba0000000701df01020004ffff004ffe410443049d022601bc000000070079016b0000ffff0076000003b5061e022601be0000010700440096001e001300b0004558b0062f1bb1061d3e59b00d
dc303100ffff0076000003b5061e022601be0000010700750135001e001300b0004558b0072f1bb1071d3e59b00edc303100ffff0076000003b5061f022601be00000106009d361e001300b0004558b0
062f1bb1061d3e59b010dc303100ffff0076000003b505ea022601be00000106006a611e001600b0004558b0062f1bb1061d3e59b013dcb01cd03031ffffffa60000017e061e022601c2000001070044
ff75001e001300b0004558b0022f1bb1021d3e59b005dc303100ffff00830000025b061e022601c2000001060075131e001300b0004558b0032f1bb1031d3e59b006dc303100ffffffa900000258061f
022601c200000107009dff15001e001300b0004558b0022f1bb1021d3e59b008dc303100ffffff9d0000026305ea022601c200000107006aff40001e001600b0004558b0022f1bb1021d3e59b00bdcb0
14d03031ffff0076000004670614022601c70000010700a40088001f000900b0052fb015dc303100ffff004ffff0046f061e022601c800000107004400d5001e001300b0004558b00b2f1bb10b1d3e59
b01edc303100ffff004ffff0046f061e022601c80000010700750174001e000900b00b2fb01fdc303100ffff004ffff0046f061f022601c800000106009d751e001300b0004558b00b2f1bb10b1d3e59
b021dc303100ffff004ffff0046f0614022601c80000010600a4771f000900b00b2fb028dc303100ffff004ffff0046f05ea022601c800000107006a00a0001e001600b0004558b00b2f1bb10b1d3e59
b024dcb02dd03031ffff0067fff0041e061e022601ce00000107004400b5001e001300b0004558b0082f1bb1081d3e59b011dc303100ffff0067fff0041e061e022601ce0000010700750154001e0013
00b0004558b00f2f1bb10f1d3e59b012dc303100ffff0067fff0041e061f022601ce00000106009d551e001300b0004558b0082f1bb1081d3e59b014dc303100ffff0067fff0041e05ea022601ce0000
0107006a0080001e001600b0004558b0082f1bb1081d3e59b017dcb020d03031ffff000500000436061e022601d2000001070075012d001e001300b0004558b0012f1bb1011d3e59b00bdc303100ffff
00090000049405d2022601ba0000010600706222001300b0004558b0042f1bb1041d3e59b00cdc303100ffff0009000004940604022601ba0000010700a0009a001e001300b0004558b0042f1bb1041d
3e59b00edc30310000020009fe520494048d001600190071b2191a1b111239b01910b016d000b0004558b0002f1bb1001d3e59b0004558b0142f1bb1140f3e59b0004558b0012f1bb1010f3e59b00045
58b00c2f1bb10c113e59b207030a2b5821d81bf459b00110b011d0b2171400111239b0172fb213010a2b5821d81bf459b219001411123930310101230606151433323717062322263534372721072301
03210302bf01d536574a472c2e15495c5f749d59fe1e5ff501d73c0154aa048dfb73385e3144178e2c6e5b9261ebf9048dfd2501ba00ffff004ffff00443061e022601bc0000010700750163001e0009
00b00b2fb01edc303100ffff004ffff00443061f022601bc00000106009d641e001300b0004558b00b2f1bb10b1d3e59b020dc303100ffff004ffff0044305fc022601bc0000010700a1014a00270013
00b0004558b00b2f1bb10b1d3e59b024dc303100ffff004ffff00443061f022601bc00000106009e7b1e000900b00b2fb020dc303100ffff006a0000042a061f022601bd00000106009ef81e000900b0
012fb018dc303100ffff0076000003b505d2022601be0000010600703122001300b0004558b0062f1bb1061d3e59b00ddc303100ffff0076000003b50604022601be0000010600a0691e001300b00045
58b0062f1bb1061d3e59b00fdc303100ffff0076000003b505fc022601be0000010700a1011c0027001300b0004558b0062f1bb1061d3e59b014dc30310000010076fe5203b5048d001b0080b2111c1d
11123900b0004558b0162f1bb1161d3e59b0004558b00f2f1bb10f113e59b0004558b0042f1bb1040f3e59b0004558b0142f1bb1140f3e59b21b1604111239b01b2fb200010a2b5821d81bf459b01410
b202010a2b5821d81bf459b003d0b00f10b20a030a2b5821d81bf459b01610b218010a2b5821d81bf459303101211121152306061514333237170623222635343721112115211121035ffe0a024c5e57
4a472c2e15495c5f7487fdfb033cfdb701f601f8fecac2385e3144178e2c6e5b865f048dc4fef200ffff0076000003b5061f022601be00000106009e4d1e001300b0004558b0062f1bb1061d3e59b011
dc303100ffff0054fff00448061f022601c000000106009d681e001300b0004558b00a2f1bb10a1d3e59b021dc303100ffff0054fff004480604022601c00000010700a0009b001e001300b0004558b0
0a2f1bb10a1d3e59b020dc303100ffff0054fff0044805fc022601c00000010700a1014e0027001300b0004558b00a2f1bb10a1d3e59b025dc303100ffff0054fdf90448049d022601c00000000701a2
016afe92ffff007600000468061f022601c100000106009d7b1e001300b0004558b0072f1bb1071d3e59b010dc303100ffffff910000026e0614022601c20000010700a4ff17001f000900b0022fb00f
dc303100ffffff970000026e05d2022601c2000001070070ff100022001300b0004558b0022f1bb1021d3e59b005dc303100ffffffbd000002430604022601c20000010700a0ff48001e001300b00045
58b0022f1bb1021d3e59b007dc303100ffff0015fe52018d048d022601c20000000600a3ec00ffff007c0000018205fc022601c20000010600a1fb27001300b0004558b0022f1bb1021d3e59b00cdc30
3100ffff0024fff00437061f022601c300000107009d00f4001e001300b0004558b0002f1bb1001d3e59b013dc303100ffff0076fdf90468048d022601c40000000701a20112fe92ffff007600000394
061e022601c50000010600750a1e001300b0004558b0052f1bb1051d3e59b008dc303100ffff0076fdf90394048d022601c50000000701a20110fe92ffff0076000003940490022601c50000010701a2
0195038a001000b0004558b00a2f1bb10a1d3e593031ffff007600000394048d022601c50000000700a10172fd46ffff007600000467061e022601c70000010700750185001e001300b0004558b0082f
1bb1081d3e59b00cdc303100ffff0076fdf90467048d022601c70000000701a20178fe92ffff007600000467061f022601c700000107009e009d001e001300b0004558b0062f1bb1061d3e59b00fdc30
3100ffff004ffff0046f05d2022601c80000010600707022000900b00b2fb01ddc303100ffff004ffff0046f0604022601c80000010700a000a8001e001300b0004558b00b2f1bb10b1d3e59b020dc30
3100ffff004ffff0046f061d022601c80000010700a500fe001e000c00b00b2fb01fdcb021d03031ffff007600000439061e022601cb0000010700750117001e000900b0042fb019dc303100ffff0076
fdf90439048d022601cb0000000701a20118fe92ffff007600000439061f022601cb00000106009e2f1e000900b0042fb01bdc303100ffff003efff003ef061e022601cc0000010700750141001e0009
00b0092fb028dc303100ffff003efff003ef061f022601cc00000106009d421e001300b0004558b0092f1bb1091d3e59b02adc303100ffff003efe4103ef049d022601cc000000070079014f0000ffff
003efff003ef061f022601cc00000106009e591e000900b0092fb02adc303100ffff0024fdf90416048d022601cd0000000701a20125fe92ffff002400000416061f022601cd00000106009e471e0013
00b0004558b0062f1bb1061d3e59b00ddc303100ffff0024fe470416048d022601cd00000007007901390006ffff0067fff0041e0614022601ce0000010600a4571f001300b0004558b00f2f1bb10f1d
3e59b013dc303100ffff0067fff0041e05d2022601ce0000010600705022000900b0002fb010dc303100ffff0067fff0041e0604022601ce0000010700a00088001e001300b0004558b0082f1bb1081d
3e59b013dc303100ffff0067fff0041e067c022601ce0000010700a200ea0052000c00b0002fb015dcb01ad03031ffff0067fff00434061d022601ce0000010700a500de001e000c00b0002fb012dcb0
14d0303100010067fe82041e048d001e0061b21b1f2011123900b0004558b0172f1bb1171d3e59b0004558b0002f1bb1001d3e59b0004558b00d2f1bb10d173e59b0004558b0122f1bb1120f3e59b204
1200111239b00d10b208030a2b5821d81bf459b01210b21b010a2b5821d81bf459303101110606070615143332371706232226353437262627113311141633323711041e017d777f472c2e15495c5f74
40cdf202f17e6ce504048dfcfc81bd32565a44178e2c6e5b5d4906d6bb0305fd007368d40307ffff0028000005e5061f022601d000000107009d0119001e001300b0004558b0012f1bb1011d3e59b00f
dc303100ffff000500000436061f022601d200000106009d2e1e001300b0004558b0082f1bb1081d3e59b00ddc303100ffff00050000043605ea022601d200000106006a591e001600b0004558b0082f
1bb1081d3e59b010dcb019d03031ffff0041000003f3061e022601d30000010700750130001e001300b0004558b0082f1bb1081d3e59b00cdc303100ffff0041000003f305fc022601d30000010700a1
01170027001300b0004558b0072f1bb1071d3e59b012dc303100ffff0041000003f3061f022601d300000106009e481e001300b0004558b0072f1bb1071d3e59b00fdc303100ffff0012000005420641
022600250000000600adbf00ffffff4a000004b00641002600296400000700adfe840000ffffff530000057c06410026002c6400000700adfe8d0000ffffff560000020306430026002d6400000700ad
fe900002ffffffa7ffec05320641002600331400000700adfee10000fffffee10000053a06410026003d6400000700adfe1b0000ffffffb2000004f10641002600b91400000700adfeec0000ffffff87
fff402da069a022600c20000010700aeff20ffeb001c00b0004558b00c2f1bb10c1b3e59b018dcb010d0b01810b021d03031ffff00120000054205b0020600250000ffff0094000004a305b002060026
0000ffff00940000044c05b0020600290000ffff00500000048c05b00206003e0000ffff00940000051805b00206002c0000ffff00a30000019f05b00206002d0000ffff00940000051805b00206002f
0000ffff00940000066a05b0020600310000ffff00940000051705b0020600320000ffff0066ffec051e05c4020600330000ffff0094000004d405b0020600340000ffff002d000004b005b002060038
0000ffff0007000004d605b00206003d0000ffff0029000004e905b00206003c0000ffffffbf0000028507090226002d00000107006aff62013d001600b0004558b0022f1bb1021f3e59b00bdcb014d0
3031ffff0007000004d607020226003d00000107006a00b30136001600b0004558b0082f1bb1081f3e59b010dcb019d03031ffff0056ffeb04790641022600ba0000010700ad01500000000900b0132f
b024dc303100ffff0060ffec040c0641022600be0000010700ad01190000000900b0092fb02adc303100ffff007efe6104060641022600c00000010700ad01230000000900b0032fb014dc303100ffff
00a9fff40261062c022600c20000010600ad0feb000900b0002fb00fdc303100ffff0080ffeb040806a2022600ca0000010600ae1df3001c00b0004558b0002f1bb1001b3e59b01edcb015d0b01e10b0
27d03031ffff008e0000046b043a0206008d0000ffff004fffec043d044e020600530000ffff0092fe60041f043a020600760000ffff0016000003da043a0206005a0000ffff001f000003e8043a0206
005c0000ffffffccfff4029205b7022600c200000107006aff6fffeb001600b0004558b00c2f1bb10c1b3e59b014dcb01dd03031ffff0080ffeb040805bf022600ca00000106006a6cf3001600b00045
58b0002f1bb1001b3e59b01adcb023d03031ffff004fffec043d0641022600530000010700ad01220000000900b0042fb01ddc303100ffff0080ffeb04080634022600ca0000010700ad010dfff30009
00b0002fb015dc303100ffff0066ffec062d0632022600cd0000010700ad022cfff1000900b0002fb023dc303100ffff00940000044c07090226002900000107006a00b3013d001600b0004558b0062f
1bb1061f3e59b013dcb01cd03031ffff009b00000437073d022600b00000010700750182013d001300b0004558b0042f1bb1041f3e59b008dc3031000001004affec048a05c400270063b21128291112
3900b0004558b0092f1bb1091f3e59b0004558b01d2f1bb11d0f3e59b2021d09111239b20e091d111239b00910b211010a2b5821d81bf459b00210b217010a2b5821d81bf459b2221d09111239b01d10
b225010a2b5821d81bf459303101342624272635342433321616152334262322061514160416161514042322242635331416333236038d87fea068c7011fe598ee88fc8f857c89940154ce60fee9ef9e
fef793fda4998485017760686a417dc9b0e470cf7e72816a5f506b6581a770b6d775ce897c886b00ffff00a30000019f05b00206002d0000ffffffbf0000028507090226002d00000107006aff62013d
001600b0004558b0022f1bb1021f3e59b00bdcb014d03031ffff002dffec03e405b00206002e0000ffff009b0000053005b0020601e30000ffff00940000051807360226002f000001070075016e0136
001300b0004558b0052f1bb1051f3e59b00fdc303100ffff0039ffeb04dd0723022600dd0000010700a000d9013d001300b0004558b00f2f1bb10f1f3e59b013dc303100ffff00120000054205b00206
00250000ffff0094000004a305b0020600260000ffff009b0000043705b0020600b00000ffff00940000044c05b0020600290000ffff00940000050d0723022600db0000010700a0011d013d001300b0
004558b0082f1bb1081f3e59b00ddc303100ffff00940000066a05b0020600310000ffff00940000051805b00206002c0000ffff0066ffec051e05c4020600330000ffff009b0000051405b0020600b5
0000ffff0094000004d405b0020600340000ffff0066ffec04eb05c4020600270000ffff002d000004b005b0020600380000ffff0029000004e905b00206003c0000ffff005affec03fb044e02060045
0000ffff0053ffec040b044e020600490000ffff00860000041205d9022600ef0000010700a00097fff3001300b0004558b0082f1bb1081b3e59b00ddc303100ffff004fffec043d044e020600530000
ffff007cfe600430044e0206005400000001004fffec03f5044e001c004bb2001d1e11123900b0004558b00f2f1bb10f1b3e59b0004558b0082f1bb1080f3e59b200010a2b5821d81bf459b203080f11
1239b2130f08111239b00f10b216010a2b5821d81bf459303125323637330e0223220011353400333216172326262322060715141602395b7804e50476ca75e3fef60108e4c1f306e504775c7680017f
ae6a4e65af660126010319f70129e1b75d78abae27b0ad00ffff000cfe4b03d6043a0206005d0000ffff001f000003e8043a0206005c0000ffff0053ffec040b05cc0226004900000106006a6c000016
00b0004558b0082f1bb1081b3e59b025dcb02ed03031ffff00850000034d05f3022600eb00000107007500c2fff3001300b0004558b0042f1bb1041b3e59b008dc303100ffff004bffec03ca044e0206
00570000ffff007d0000019005d50206004d0000ffffffab0000027105c50226008c00000107006aff4efff9001600b0004558b0022f1bb1021b3e59b00bdcb014d03031ffffffb5fe4b018505d50206
004e0000ffff008f0000046505f2022600f00000010700750144fff2001300b0004558b0042f1bb1041b3e59b00fdc303100ffff000cfe4b03d605e60226005d0000010600a04a00001300b0004558b0
0f2f1bb10f1b3e59b013dc303100ffff0030000006e507360226003b00000107004402080136001300b0004558b00b2f1bb10b1f3e59b00edc303100ffff0021000005cc06000226005b000001070044
016a0000001300b0004558b00b2f1bb10b1b3e59b00edc303100ffff0030000006e507360226003b00000107007502a70136001300b0004558b00c2f1bb10c1f3e59b00fdc303100ffff0021000005cc
06000226005b00000107007502090000001300b0004558b00c2f1bb10c1b3e59b00fdc303100ffff0030000006e507020226003b00000107006a01d30136000c00b0012fb016dcb00dd03031ffff0021
000005cc05cc0226005b00000107006a01350000000c00b0012fb016dcb00dd03031ffff0007000004d607360226003d00000107004400e80136001300b0004558b0082f1bb1081f3e59b00adc303100
ffff000cfe4b03d606000226005d0000010600447700000900b0012fb010dc303100ffff005203fc010b06000306000b0000001600b0004558b0042f1bb104213e59b001d0b0012f3031ffff006503f4
02400600030600060000002c00b0004558b0092f1bb109213e59b0004558b0042f1bb104213e59b00910b006d0b0062fb001d0b0012f3031ffff008ffff203c805b00026000500000007000502250000
ffffffb1fe4b027305df0226009b00000107009eff3fffde000900b0002fb011dc303100ffff00330400016506000206016d0000ffff00940000066a07360226003100000107007502900136001300b0
004558b0022f1bb1021f3e59b011dc303100ffff007c0000067906000226005100000107007502a00000000900b0032fb020dc303100ffff0012fe6d054205b0022600250000000700a6017a0003ffff
005afe7103fb044e022600450000000700a600ad0007ffff00940000044c073d0226002900000107004400e8013d001300b0004558b0062f1bb1061f3e59b00ddc303100ffff00940000050d073d0226
00db000001070044014a013d001300b0004558b0082f1bb1081f3e59b00bdc303100ffff0053ffec040b06000226004900000107004400a10000001300b0004558b0082f1bb1081b3e59b01fdc303100
ffff00860000041205f3022600ef00000107004400c4fff3001300b0004558b0082f1bb1081b3e59b00bdc303100ffff00440000055c05b0020600b80000ffff004ffe22057e043a020600cc0000ffff
0010000004f306fc022601180000010700ab0449010e001600b0004558b00f2f1bb10f1f3e59b011dcb015d03031fffffff10000041805d0022601190000010700ab03e5ffe2001600b0004558b0112f
1bb1111b3e59b013dcb017d03031ffff004ffe4b0864044e0026005300000007005d048e0000ffff0066fe4b095c05c40026003300000007005d05860000ffff0049fe3a047f05c3022600da00000007
01b00192ffa0ffff004dfe3b03c4044d022600ee0000000701b00139ffa1ffff0066fe3e04eb05c4022600270000000701b001d6ffa4ffff004ffe3e03f5044e022600470000000701b0014affa4ffff
0007000004d605b00206003d0000ffff0020fe5f03f5043a020600bc0000ffff00a30000019f05b00206002d0000ffff00160000079b0723022600d90000010700a0021d013d001300b0004558b00d2f
1bb10d1f3e59b019dc303100ffff001e0000065c05d9022600ed0000010700a00187fff3001300b0004558b00d2f1bb10d1b3e59b019dc303100ffff00a30000019f05b00206002d0000ffff00120000
0542071c022600250000010700a000f60136001300b0004558b0042f1bb1041f3e59b00edc303100ffff005affec03fb05e6022600450000010700a000800000001300b0004558b0172f1bb1171b3e59
b02ddc303100ffff00120000054207020226002500000107006a00ee0136001600b0004558b0042f1bb1041f3e59b012dcb01bd03031ffff005affec03fb05cc0226004500000106006a7800001600b0
004558b0172f1bb1171b3e59b031dcb03ad03031fffffff60000075705b0020600810000ffff0048ffec06840450020600860000ffff00940000044c0723022600290000010700a000bb013d001300b0
004558b0062f1bb1061f3e59b00fdc303100ffff0053ffec040b05e6022600490000010600a07400001300b0004558b0082f1bb1081b3e59b021dc303100ffff0051ffeb051e06db0226014500000107
006a00c2010f001600b0004558b0002f1bb1001f3e59b026dcb02fd03031ffff0059ffec03f8044f0206009c0000ffff0059ffec03f805cd0226009c00000106006a6901001600b0004558b0002f1bb1
001b3e59b026dcb02fd03031ffff00160000079b0709022600d900000107006a0215013d001600b0004558b00d2f1bb10d1f3e59b01ddcb026d03031ffff001e0000065c05bf022600ed00000107006a
017ffff3001600b0004558b00d2f1bb10d1b3e59b01ddcb026d03031ffff0049ffed047f0717022600da00000107006a00a3014b001600b0004558b00b2f1bb10b1f3e59b031dcb03ad03031ffff004d
ffec03c405cc022600ee00000106006a4e00001600b0004558b0252f1bb1251b3e59b02fdcb038d03031ffff00940000050d06f1022600db00000107007000e50141001300b0004558b0082f1bb1081f
3e59b00bdc303100ffff00860000041205a7022600ef0000010600705ff7001300b0004558b0072f1bb1071b3e59b00bdc303100ffff00940000050d0709022600db00000107006a0115013d001600b0
004558b0082f1bb1081f3e59b011dcb01ad03031ffff00860000041205bf022600ef00000107006a008ffff3001600b0004558b0082f1bb1081b3e59b011dcb01ad03031ffff0066ffec051e07020226
003300000107006a01050136001600b0004558b00c2f1bb10c1f3e59b026dcb02fd03031ffff004fffec043d05cc0226005300000107006a00810000001600b0004558b0042f1bb1041b3e59b022dcb0
2bd03031ffff005fffec051705c4020601160000ffff004fffec043d044e020601170000ffff005fffec051707060226011600000107006a0113013a001600b0004558b00c2f1bb10c1f3e59b026dcb0
2fd03031ffff004fffec043d05cc0226011700000106006a7300001600b0004558b0042f1bb1041b3e59b025dcb02ed03031ffff006bffec04f10718022600e600000107006a00e3014c001600b00045
58b0132f1bb1131f3e59b027dcb030d03031ffff0051ffec03e805cc022600fe00000106006a5900001600b0004558b0082f1bb1081b3e59b028dcb031d03031ffff0039ffeb04dd06f1022600dd0000
0107007000a10141000900b0012fb010dc303100ffff000cfe4b03d605b40226005d0000010600701204000900b0012fb010dc303100ffff0039ffeb04dd0709022600dd00000107006a00d1013d0016
00b0004558b00f2f1bb10f1f3e59b017dcb020d03031ffff000cfe4b03d605cc0226005d00000106006a4200001600b0004558b00f2f1bb10f1b3e59b017dcb020d03031ffff0039ffeb04dd073c0226
00dd0000010700a5012f013d001600b0004558b00f2f1bb10f1f3e59b016dcb012d03031ffff000cfe4b03f605ff0226005d0000010700a500a00000001600b0004558b00f2f1bb10f1b3e59b016dcb0
12d03031ffff008e000004ee0709022600e000000107006a010f013d001600b0004558b00a2f1bb10a1f3e59b019dcb022d03031ffff005f000003e005bf022600f800000106006a67f3001600b00045
58b0092f1bb1091b3e59b019dcb022d03031ffff009b00000658070a002600e50b000027002d04b900000107006a01c2013e001600b0004558b00b2f1bb10b1f3e59b020dcb029d03031ffff008f0000
05c905bf002600fd00000027008c044700000107006a0174fff3001600b0004558b00b2f1bb10b1b3e59b01fdcb028d03031ffff0029fe4b055105b00226003c0000000701af03c30000ffff001ffe4b
0456043a0226005c0000000701af02c80000ffff004fffec04030600020600480000ffff002dfe4b05fd05b0022600dc0000000701af046f0000ffff0021fe4b0507043a022600f10000000701af0379
0000ffff0012fe97054205b0022600250000000700ac050d0003ffff005afe9b03fb044e022600450000000700ac04400007ffff00120000054207bb022600250000010700aa0505013c000900b0042f
b00bdc303100ffff005affec03fb0685022600450000010700aa048f0006000900b0172fb02adc303100ffff00120000054a07b1022600250000010701b700bf0121001700b0004558b0052f1bb1051f
3e59b10e09f4b014d0303100ffff005affec04d4067c022600450000010601b749ec000c00b0172fb02cdcb031d03031ffff00100000054207ae022600250000010701b600c4012b001700b0004558b0
042f1bb1041f3e59b10e09f4b013d0303100ffffff9affec03fb0679022600450000010601b64ef6000c00b0172fb02adcb031d03031ffff00120000054207de022600250000010701b500c30113000c
00b0042fb00bdcb012d03031ffff005affec045706a9022600450000010601b54dde000c00b0172fb02adcb031d03031ffff00120000054207d6022600250000010701b400c40105000c00b0042fb00b
dcb012d03031ffff005affec03fb06a1022600450000010601b44ed0000c00b0172fb02adcb031d03031ffff0012fe97054207370226002500000027009d00c30136000700ac050d0003ffff005afe9b
03fb06010226004500000026009d4d00000700ac04400007ffff00120000054207ae022600250000010701b300ef0130000c00b0042fb00edcb019d03031ffff005affec03fb06790226004500000106
01b379fb000c00b0172fb02ddcb038d03031ffff00120000054207ae022600250000010701b800ef0130000c00b0042fb00edcb019d03031ffff005affec03fb0679022600450000010601b879fb000c
00b0172fb02ddcb038d03031ffff001200000542083e022600250000010701b200ee0136000c00b0042fb00edcb019d03031ffff005affec03fb0708022600450000010601b27800000c00b0172fb02d
dcb038d03031ffff0012000005420818022600250000010701b100f1013c000c00b0042fb014dcb018d03031ffff005affec03fb06e2022600450000010601b17b06000c00b0172fb033dcb037d03031
ffff0012fe970542071c022600250000002700a000f60136000700ac050d0003ffff005afe9b03fb05e6022600450000002700a000800000000700ac04400007ffff0094fe9e044c05b0022600290000
000700ac04cb000affff0053fe94040b044e022600490000000700ac048f0000ffff00940000044c07c2022600290000010700aa04ca0143000900b0062fb00cdc303100ffff0053ffec040b06850226
00490000010700aa04830006000900b0082fb01edc303100ffff00940000044c0733022600290000010700a4008a013e000900b0062fb017dc303100ffff0053ffec040b05f6022600490000010600a4
4301000900b0082fb029dc303100ffff00940000050f07b8022600290000010701b700840128001700b0004558b0072f1bb1071f3e59b10f09f4b015d0303100ffff0053ffec04c8067c022600490000
010601b73dec000c00b0082fb020dcb025d03031ffffffd50000044c07b5022600290000010701b600890132001700b0004558b0062f1bb1061f3e59b10f09f4b014d0303100ffffff8effec040b0679
022600490000010601b642f6000c00b0082fb01edcb025d03031ffff00940000049207e5022600290000010701b50088011a000c00b0062fb00cdcb013d03031ffff0053ffec044b06a9022600490000
010601b541de000c00b0082fb01edcb025d03031ffff00940000044c07dd022600290000010701b40089010c000c00b0062fb00cdcb013d03031ffff0053ffec040b06a1022600490000010601b442d0
000c00b0082fb01edcb025d03031ffff0094fe9e044c073e0226002900000027009d0088013d000700ac04cb000affff0053fe94040b06010226004900000026009d4100000700ac048f0000ffff00a3
0000021107c20226002d0000010700aa03780143000900b0022fb004dc303100ffff008f000001fd067e0226008c0000010700aa0364ffff000900b0022fb004dc303100ffff0094fe9a01a705b00226
002d0000000700ac03780006ffff0078fe9e019005d50226004d0000000700ac035c000affff0066fe94051e05c4022600330000000700ac051d0000ffff004ffe92043d044e022600530000000700ac
049dfffeffff0066ffec051e07bb022600330000010700aa051c013c000900b0142fb01fdc303100ffff004fffec043d0685022600530000010700aa04980006000900b0042fb01bdc303100ffff0066
ffec056107b1022600330000010701b700d60121000c00b0142fb021dcb026d03031ffff004fffec04dd067c022600530000010601b752ec000c00b0042fb01ddcb022d03031ffff0027ffec051e07ae
022600330000010701b600db012b000c00b0142fb01fdcb026d03031ffffffa3ffec043d0679022600530000010601b657f6000c00b0042fb01bdcb022d03031ffff0066ffec051e07de022600330000
010701b500da0113000c00b0142fb01fdcb026d03031ffff004fffec046006a9022600530000010601b556de000c00b0042fb01bdcb022d03031ffff0066ffec051e07d6022600330000010701b400db
0105000c00b0142fb01fdcb026d03031ffff004fffec043d06a1022600530000010601b457d0000c00b0042fb01bdcb022d03031ffff0066fe94051e07370226003300000027009d00da0136000700ac
051d0000ffff004ffe92043d06010226005300000026009d5600000700ac049dfffeffff0058ffec05aa07330226009700000007007501d30133ffff004fffec04bb0600022600980000010700750158
0000000900b0092fb025dc303100ffff0058ffec05aa07330226009700000007004401340133ffff004fffec04bb06000226009800000107004400b90000000900b0092fb023dc303100ffff0058ffec
05aa07b8022600970000000700aa05160139ffff004fffec04bb0685022600980000010700aa049b0006000900b0092fb023dc303100ffff0058ffec05aa0729022600970000000700a400d60134ffff
004fffec04bb05f6022600980000010600a45b01000900b0092fb02edc303100ffff0058fe9405aa062e022600970000000700ac05060000ffff004ffe8b04bb04a8022600980000000700ac049afff7
ffff007dfe9404bd05b0022600390000000700ac04f20000ffff0077fe9403f7043a022600590000000700ac04410000ffff007dffec04bd07bb022600390000010700aa04f3013c000900b0002fb011
dc303100ffff0077ffec03f70685022600590000010700aa04910006000900b0062fb011dc303100ffff007dffec063d07420226009900000107007501d70142000900b0042fb01bdc303100ffff0077
ffec052805ec0226009a0000010700750157ffec000900b0002fb01cdc303100ffff007dffec063d07420226009900000107004401380142000900b0042fb019dc303100ffff0077ffec052805ec0226
009a00000107004400b8ffec000900b0002fb01adc303100ffff007dffec063d07c7022600990000010700aa051a0148000900b0042fb019dc303100ffff0077ffec052806710226009a0000010700aa
049afff2000900b0002fb01adc303100ffff007dffec063d0738022600990000010700a400da0143000900b0042fb024dc303100ffff0077ffec052805e20226009a0000010600a45aed000900b0002f
b025dc303100ffff007dfe8b063d0601022600990000000700ac0519fff7ffff0077fe94052804930226009a0000000700ac04450000ffff0007fea404d605b00226003d0000000700ac04c60010ffff
000cfe0f03d6043a0226005d0000000700ac0546ff7bffff0007000004d607bb0226003d0000010700aa04ca013c000900b0012fb009dc303100ffff000cfe4b03d606850226005d0000010700aa0459
0006000900b0012fb010dc303100ffff0007000004d6072c0226003d0000010700a4008a0137000900b0012fb014dc303100ffff000cfe4b03d605f60226005d0000010600a41901000900b0012fb01b
dc3031000002004fffec04b2060000160021008cb21f2223111239b01f10b010d000b0132fb0004558b00c2f1bb10c1b3e59b0004558b0062f1bb1060f3e59b0004558b0022f1bb1020f3e59b22f1301
5db20f13015db2160213111239b0162fb200070a2b5821d81bf459b2040c06111239b20e0c06111239b00fd0b01610b011d0b00610b21a010a2b5821d81bf459b00c10b21f010a2b5821d81bf4593031
0123112327062322021134123332173523353335331533011416333237112623220604b2afdc0c6db6beebe8c3ac6afbfbf3affc907f7595454395768004c9fb37708401320107fa012f78f3aa8d8dfc
9da5b98501ce82bbffff004ffeae04b20600002600480000002701de01850242010700430099ff6d001200b22f1c015db21f1c0171b29f1c015d3031ffff009bfe9a057e05b0022601e30000000701b0
042f0000ffff008ffe9a04c2043a022600f00000000701b003730000ffff0094fe9a05db05b00226002c0000000701b0048c0000ffff0086fe9a04d5043a022600f30000000701b003860000ffff002d
fe9a04b005b0022600380000000701b0024d0000ffff0023fe9a03d0043a022600f50000000701b001e60000ffff0029fe9a052205b00226003c0000000701b003d30000ffff001ffe9a0427043a0226
005c0000000701b002d80000ffff008efe9a05ad05b0022600e00000000701b0045e0000ffff005ffe9a04a4043b022600f80000000701b003550000ffff008efe9a04ee05b0022600e00000000701b0
02cf0000ffff005ffe9a03e0043b022600f80000000701b001c60000ffff009bfe9a043705b0022600b00000000701b001070000ffff0085fe9a034d043a022600eb0000000701b000ec0000ffff0016
fe9a080505b0022600d90000000701b006b60000ffff001efe9a06b4043a022600ed0000000701b005650000ffff0016fe4305bc05c40226013f0000000701b002edffa9ffffffcbfe46048b044e0226
01400000000701b001f5ffacffff0079000003f806000206004c00000002ffd0000004c105b00013001c006eb2001d1e111239b016d000b0004558b0102f1bb1101f3e59b0004558b00a2f1bb10a0f3e
59b213100a111239b0132fb200070a2b5821d81bf459b202100a111239b0022fb00010b00cd0b01310b00ed0b00210b214010a2b5821d81bf459b00a10b215010a2b5821d81bf4593031012315213216
1615140407211123353335331533031121323635342627026de0012aa0ee7cfeebeffdd3c0c0fde0e00129808f8c7c0447c46eca85ccf8020447aabfbffdc7fe128b736e80020002ffd0000004c105b0
0013001c006eb2001d1e111239b016d000b0004558b0102f1bb1101f3e59b0004558b00a2f1bb10a0f3e59b213100a111239b0132fb200070a2b5821d81bf459b202100a111239b0022fb00010b00cd0
b01310b00ed0b00210b214010a2b5821d81bf459b00a10b215010a2b5821d81bf45930310123152132161615140407211123353335331533031121323635342627026de0012aa0ee7cfeebeffdd3c0c0
fde0e00129808f8c7c0447c46eca85ccf8020447aabfbffdc7fe128b736e80020001fff00000043705b0000d004900b0004558b0082f1bb1081f3e59b0004558b0022f1bb1020f3e59b20d0802111239
b00d2fb200070a2b5821d81bf459b004d0b00d10b006d0b00810b20a010a2b5821d81bf45930310123112311233533112115211133028df6fcabab039cfd60f6029ffd61029faa0267ccfe650001ffe2
0000034d043a000d004900b0004558b0082f1bb1081b3e59b0004558b0022f1bb1020f3e59b20d0802111239b00d2fb200070a2b5821d81bf459b004d0b00d10b006d0b00810b20a010a2b5821d81bf4
5930310121112311233533112115211521027ffef8f2a3a302c8fe2a010801d1fe2f01d1aa01bfc4fb000001ffe30000054405b00014007400b0004558b0082f1bb1081f3e59b0004558b0102f1bb110
1f3e59b0004558b0022f1bb1020f3e59b0004558b0132f1bb1130f3e59b20e0802111239b00e2fb201010a2b5821d81bf459b2070802111239b0072fb204010a2b5821d81bf459b00710b00ad0b00410
b00cd0b212010e11123930310123112311233533353315331523153301210101210257acfcccccfcd5d58b01ac0136fe0c0220fed00270fd90043faac7c7aaf30264fd47fd090001ffae000004490600
0014007400b0004558b0082f1bb108213e59b0004558b0102f1bb1101b3e59b0004558b0022f1bb1020f3e59b0004558b0132f1bb1130f3e59b20e1002111239b00e2fb201010a2b5821d81bf459b207
0810111239b0072fb204070a2b5821d81bf459b00710b00ad0b00410b00cd0b212010e111239303101231123112335333533153315231133012101012101f66ff2e7e7f2c4c469010f011cfe9f018ffe
e601d9fe2704bbaa9b9baafde1019efe11fdb500ffff0094fe7e05dd0723022600db0000002700a0011d013d010700100480ffc6001300b0004558b0082f1bb1081f3e59b00ddc303100ffff0086fe7e
04e405d9022600ef0000002700a00097fff3010700100387ffc6001300b0004558b0082f1bb1081b3e59b00ddc303100ffff0094fe7e05e905b00226002c000000070010048cffc6ffff0086fe7e04e3
043a022600f30000000700100386ffc6ffff0094fe7e073205b00226003100000007001005d5ffc6ffff008ffe7e0641043a022600f200000007001004e4ffc6ffff002dfe7e05dc05b0022600dc0000
00070010047fffc6ffff0021fe7e04e6043a022600f10000000700100389ffc600010007000004d605b0000e0056b20a0f1011123900b0004558b0082f1bb1081f3e59b0004558b00b2f1bb10b1f3e59
b0004558b0022f1bb1020f3e59b2060208111239b0062fb205070a2b5821d81bf459b001d0b20a0802111239b00610b00ed0303101231123112335330121010121013303c3d5feca7afe670119014f01
4f0118fe67860204fdfc0204aa0302fd4e02b2fcfe0000010020fe5f03f5043a000e0063b20a0f1011123900b0004558b0082f1bb1081b3e59b0004558b00b2f1bb10b1b3e59b0004558b0022f1bb102
113e59b0004558b0002f1bb1000f3e59b0004558b0042f1bb1040f3e59b206070a2b5821d81bf459b20a0b00111239b00dd0b00ed030310523112311233533013313133301330360dcf3cea2febbfbf3
ecfbfebcaf01fe6001a0aa0391fd0102fffc6f0000010029000004e905b00011006300b0004558b00b2f1bb10b1f3e59b0004558b00e2f1bb10e1f3e59b0004558b0022f1bb1020f3e59b0004558b005
2f1bb1050f3e59b2110b02111239b0112fb200070a2b5821d81bf459b2040b02111239b007d0b01110b009d0b20d0b02111239303101230121010121012335330121010121013303db870195fed9fec7
fec6feda01968173fe820124013201320124fe83790295fd6b0216fdea0295aa0271fdf2020efd8f0001001f000003e8043a0011006300b0004558b00b2f1bb10b1b3e59b0004558b00e2f1bb10e1b3e
59b0004558b0022f1bb1020f3e59b0004558b0052f1bb1050f3e59b2110e02111239b0112fb200070a2b5821d81bf459b2040e02111239b007d0b01110b009d0b20d0e02111239303101230121030321
01233533012113132101330357950126fef4d8d7fef201258a82feef010ccace010efeee8c01d7fe290172fe8e01d7aa01b9fe9c0164fe47ffff0060ffec040c044d020600be0000ffff000200000431
05b00226002a0000000701deff72fe69ffff0081026d05d1033100460197850066664000ffff00510000044005c4020600160000ffff004fffec041505c4020600170000ffff00340000045805b00206
00180000ffff0081ffec043a05b0020600190000ffff005dfffa041205c40006001d0000ffff007dffec043605c4000600141400ffff006affec04f0074b0226002b00000107007501bd014b000900b0
0b2fb021dc303100ffff0052fe56040c06000226004b000001070075013f0000000900b0032fb027dc303100ffff009400000517073602260032000001070044014c0136001300b0004558b0062f1bb1
061f3e59b00bdc303100ffff0079000003f806000226005200000107004400b30000001300b0004558b0002f1bb1001b3e59b012dc303100ffff0012000005420721022600250000010700ab04770133
001600b0004558b0042f1bb1041f3e59b00cdcb010d03031ffff000dffec03fb05ec022600450000010700ab0401fffe001600b0004558b0172f1bb1171b3e59b02bdcb02fd03031ffff00480000044c
0728022600290000010700ab043c013a001600b0004558b0062f1bb1061f3e59b00ddcb011d03031ffff0001ffec040b05ec022600490000010700ab03f5fffe001600b0004558b0082f1bb1081b3e59
b01fdcb023d03031fffffef60000021e07280226002d0000010700ab02ea013a001600b0004558b0022f1bb1021f3e59b005dcb009d03031fffffee20000020a05e40226008c0000010700ab02d6fff6
001600b0004558b0022f1bb1021b3e59b005dcb009d03031ffff0066ffec051e0721022600330000010700ab048e0133001600b0004558b00c2f1bb10c1f3e59b020dcb024d03031ffff0016ffec043d
05ec022600530000010700ab040afffe001600b0004558b0042f1bb1041b3e59b01cdcb020d03031ffff0032000004de0721022600360000010700ab04260133001600b0004558b0042f1bb1041f3e59
b019dcb01dd03031ffffff6e000002b405ec022600560000010700ab0362fffe001600b0004558b0072f1bb1071b3e59b00fdcb013d03031ffff0071ffec04bd0721022600390000010700ab04650133
001600b0004558b0092f1bb1091f3e59b012dcb016d03031ffff000fffec03f705ec022600590000010700ab0403fffe001600b0004558b0072f1bb1071b3e59b012dcb016d03031fffffeac00000502
0641002600cf6400000700adfde60000ffff0094fe9e04a305b0022600260000000700ac04b9000affff007cfe8b04320600022600460000000700ac04cbfff7ffff0094fe9e04d205b0022600280000
000700ac0494000affff004ffe9404030600022600480000000700ac04b40000ffff0094fdf904d205b0022600280000000701a20148fe92ffff004ffdf904030600022600480000000701a20168fe92
ffff0094fe9e051805b00226002c0000000700ac0526000affff0079fe9e03f806000226004c0000000700ac04a1000affff00940000051807360226002f000001070075016e0136000900b0042fb00f
dc303100ffff007d00000436073d0226004f000001070075016b013d000900b0042fb00fdc303100ffff0094fedf051805b00226002f0000000700ac04e9004bffff007dfeca043606000226004f0000
000700ac04790036ffff0094fe9e042605b0022600300000000700ac04b9000affff0078fe9e018b0600022600500000000700ac035c000affff0094fe9e066a05b0022600310000000700ac05d6000a
ffff007cfe9e0679044e022600510000000700ac05d9000affff0094fe9a051705b0022600320000000700ac05280006ffff0079fe9e03f8044e022600520000000700ac048d000affff0094000004d4
07420226003400000107007501720142000900b0032fb016dc303100ffff007cfe60043005f702260054000001070075019dfff7000900b00c2fb01ddc303100ffff0094fe9e04de05b0022600360000
000700ac04ba000affff0072fe9e02b4044e022600560000000700ac0356000affff004afe94048a05c4022600370000000700ac04d50000ffff004bfe8b03ca044e022600570000000700ac047cfff7
ffff002dfe9704b005b0022600380000000700ac04c30003ffff0008fe9402720541022600580000000700ac04140000ffff00120000051d07380226003a0000010700a400b00143000900b0012fb012
dc303100ffff0016000003da05ed0226005a0000010600a418f8000900b0012fb012dc303100ffff0012fe9e051d05b00226003a0000000700ac04ef000affff0016fe9e03da043a0226005a00000007
00ac0457000affff0030fe9e06e505b00226003b0000000700ac05e6000affff0021fe9e05cc043a0226005b0000000700ac054e000affff0050fe9e048c05b00226003e0000000700ac04c1000affff
0052fe9e03c0043a0226005e0000000700ac0463000afffffe1cffec056405d70026003346000007015afdb50000ffff000900000494051e022601ba0000000700adff76feddffffff2a000003f10521
002601be3c00000700adfe64fee0ffffff37000004a4051c002601c13c00000700adfe71fedbffffff39000001b30521002601c23c00000700adfe73fee0ffffff93fff00479051e002601c80a000007
00adfecdfeddfffffee800000472051e002601d23c00000700adfe22feddffffffa40000048e051e002601f30a00000700adfedefeddffff000900000494048d020601ba0000ffff00760000040a048d
020601bb0000ffff0076000003b5048d020601be0000ffff0041000003f3048d020601d30000ffff007600000468048d020601c10000ffff008500000177048d020601c20000ffff007600000468048d
020601c40000ffff00760000058f048d020601c60000ffff004ffff0046f049d020601c80000ffff00760000042c048d020601c90000ffff002400000416048d020601cd0000ffff000500000436048d
020601d20000ffff00150000044a048d020601d10000ffffff9d0000026305ea022601c200000107006aff40001e001600b0004558b0022f1bb1021d3e59b00bdcb014d03031ffff00050000043605ea
022601d200000106006a591e001600b0004558b0082f1bb1081d3e59b010dcb019d03031ffff0076000003b505ea022601be00000106006a611e001600b0004558b0062f1bb1061d3e59b013dcb01cd0
3031ffff007600000397061e022601ea0000010700750123001e000900b0042fb008dc303100ffff003efff003ef049d020601cc0000ffff008500000177048d020601c20000ffffff9d0000026305ea
022601c200000107006aff40001e001600b0004558b0022f1bb1021d3e59b00bdcb014d03031ffff0024fff00364048d020601c30000ffff007600000468061e022601c40000010700750117001e0009
00b0042fb00fdc303100ffff001fffec04390604022602010000010600a07a1e001300b0004558b00f2f1bb10f1d3e59b013dc303100ffff000900000494048d020601ba0000ffff00760000040a048d
020601bb0000ffff007600000397048d020601ea0000ffff0076000003b5048d020601be0000ffff00760000046e0604022601fe0000010700a000ba001e001300b0004558b0082f1bb1081d3e59b00d
dc303100ffff00760000058f048d020601c60000ffff007600000468048d020601c10000ffff004ffff0046f049d020601c80000ffff007600000462048d020601ef0000ffff00760000042c048d0206
01c90000ffff004ffff00443049d020601bc0000ffff002400000416048d020601cd0000ffff00150000044a048d020601d1000000010042fe3903e7049d002800a4b227292a11123900b0172fb00045
58b00a2f1bb10a1d3e59b0004558b0192f1bb1190f3e59b00a10b203010a2b5821d81bf459b2060a19111239b227190a111239b0272fb25f270172b23f270171b2cf270171b2ff270171b20f270172b4
6f277f270271b4af27bf27025db28f270172b2bf270172b224010a2b5821d81bf459b2102427111239b01910b016d0b21d190a111239b01910b21f010a2b5821d81bf459303101342623220615233436
3332161514060716161514060711231126263533163332363534272335333602e2706b5b66f3f3c3d8f46e5d6f6ebbacf39bb0f30bca7774e0949ac70343464f463c94b3a7965b8a2724915b86ae18fe
4101c218ac87935748a603b0040000010076fe9a052c048d000f00a8b203101111123900b0004558b00c2f1bb10c1d3e59b0004558b0092f1bb1091d3e59b0004558b0012f1bb101173e59b0004558b0
062f1bb1060f3e59b0004558b0032f1bb1030f3e59b20a0609111239b00a2fb4af0abf0a025db23f0a0171b2cf0a0171b23f0a0172b2ff0a0171b20f0a0172b46f0a7f0a0271b4df0aef0a025db41f0a
2f0a025db25f0a0172b205010a2b5821d81bf459b00310b20e070a2b5821d81bf459303101231123112111231133112111331133052cf3c4fdf4f3f3020cf3c4fe9a016601dbfe25048dfe1101effc28
0001004ffe430443049d001e005eb21b1f2011123900b0004558b00e2f1bb10e1d3e59b0004558b0042f1bb104113e59b0004558b0032f1bb1030f3e59b006d0b2120e03111239b00e10b215010a2b58
21d81bf459b00310b21b010a2b5821d81bf459b21e030e11123930310106060711231126022735343636333204172326262320111514163332363704420cc6a9f3b5cf017eec9cd6010414f30c7d72fe
ed8687787c0d01849fd01bfe4901b924011fdd4fa9ff8adac27069fe8e48b9b56270ffff000500000436048d020601d20000ffff000afe3a05a804a3022602170000000701b002e6ffa0ffff00760000
046e05d2022601fe00000107007000820022000900b0002fb00adc303100ffff001fffec043905d2022602010000010600704222000900b0022fb010dc303100ffff00500000054d048d020601f10000
ffff0012fe55054205b0022600250000000700a301820003ffff005afe5903fb044e022600450000000700a300b50007ffff0094fe5c044c05b0022600290000000700a30140000affff0053fe52040b
044e022600490000000700a301040000ffff0078fe9e018b043a0226008c0000000700ac035c000a0000000f00ba0003000104090000005e00000003000104090001001a005e0003000104090002000e
00780003000104090003001a005e0003000104090004001a005e0003000104090005002c00860003000104090006001a00b20003000104090007004000cc0003000104090009000c010c000300010409
000b00140118000300010409000c0026012c000300010409000d005c0152000300010409000e005401ae0003000104090010000c02020003000104090011000c020e0043006f00700079007200690067
006800740020003200300031003100200047006f006f0067006c006500200049006e0063002e00200041006c006c0020005200690067006800740073002000520065007300650072007600650064002e
0052006f0062006f0074006f0020004d0065006400690075006d0052006500670075006c0061007200560065007200730069006f006e00200032002e003000300031003100350032003b002000320030
003100340052006f0062006f0074006f002d004d0065006400690075006d0052006f0062006f0074006f00200069007300200061002000740072006100640065006d00610072006b0020006f00660020
0047006f006f0067006c0065002e0047006f006f0067006c00650047006f006f0067006c0065002e0063006f006d00430068007200690073007400690061006e00200052006f00620065007200740073
006f006e004c006900630065006e00730065006400200075006e006400650072002000740068006500200041007000610063006800650020004c006900630065006e00730065002c0020005600650072
00730069006f006e00200032002e00300068007400740070003a002f002f007700770077002e006100700061006300680065002e006f00720067002f006c006900630065006e007300650073002f004c
004900430045004e00530045002d0032002e00300052006f0062006f0074006f004d0065006400690075006d0003000000000000ff6a0064000000000000000000000000000000000000000000010002
00080002ffff000f00010000000a005c00ac000444464c54001a6379726c00286772656b00366c61746e0044000400000000ffff000200000004000400000000ffff000200010005000400000000ffff
000200020006000400000000ffff000200030007000863707370003263707370003863707370003e6370737000446b65726e004a6b65726e004a6b65726e004a6b65726e004a00000001000100000001
00030000000100020000000100000000000100040005000c000c000c000c01de00010000000100080001000a000500240048000100de000800250026002700280029002a002b002c002d002e002f0030
003100320033003400350036003700380039003a003b003c003d003e00650067009200b000b100b200b300b400b500b600b700b800b900d100d200d300d400d500d600d700d800d900da00db00dc00dd
00de00df00e000e100e200e300e400e500e600e700e8012c013001320138013a013c013e013f01450146017f0185018a018d024602470249024b024c024d024e024f0250025102520253025402550256
025702580259025a025b025c025d025e025f0260026102620263026402650282028402860288028a028c028e02900292029402960298029a029c029e02a002a202a402a602a802aa02ac02ae02b102b3
02b502b702b902bb02bd02bf02c102c402c602c802ca02cc02ce02d002d202d402d802da02dc02de02e002e202e402e602e802ea02ec02ee02f002f102f302f50352035303540355035603570358035a
035b035c035d035e035f0360036103630364036503660367036803690379037a037b037c037d037e037f0380038103820383038403850386038703880389038a038b038c038d038e03ba03bc03be03d3
03d903df0448044a044e04560458045d0469000200000002000a3bba0001036c0004000001b106b2369e369e06dc07323740364c36ca3b8a37d807383ade3ade381e3a8c35ea3ade3ade3b8a36560a72
0af4383e381e36a4367839323b00362a0b5e37b636dc37ee0ba00cca0cd43996399637f836dc36180dca38240e2c399038240e4636dc0e8839ca37403b8a37400f020ffc10fa11d812763824127c3996
153a1714182618401846184c1a461a4c1a821ab41b321ca81e5a20183ade214e22e03932252e3ade3ade36f63ade3ade25f8279239a02870293229c02a1e2af839282b8239902c4c2c762ddc36dc3062
30a031d2339036dc325432da3304335a3390374037f836a4382433b636dc39ca39283a8c3a8c3928369e33e0369e369e369e355235783582358c35aa35bc35ce35e036ca3b8a3b8a3b8a3b8a383e3740
37403740374037403740374036ca37d837d837d837d83ade3ade3ade3ade3ade3b8a3b8a3b8a3b8a3b8a381e381e381e381e3b0037b637b637b637b637b637b637b637ee37ee37ee37ee399637f837f8
37f837f837f838243824374037b6374037b6374037b636ca36ca36ca36ca3b8a37d837ee37d837ee37d837ee37d837ee37d837ee3ade39963ade3ade3ade3ade3ade381e3a8c35ea35ea35ea35ea3ade
39963ade39963ade399639963b8a37f83b8a37f83b8a37f8361836183618383e383e383e381e381e381e381e381e381e36783b0038243b00362a362a362a374037d83ade3ade3b8a3b003740364c37d8
362a3ade3ade3a8c3ade3ade3b8a3656383e3b0039323ade3b00399637f8382437f837d839ca3ade3ade381e3a8c3a8c36f63740364c39ca37d83ade3ade3b8a365636ca383e393237b637ee37f836dc
3824399037ee392838243678367836783b003824369e369e369e3ade3996374037b637d837ee36a4382436ca3b0038243ade393239903ade374037b6374037b637d837ee37ee37ee393239903b8a37f8
37f836dc36f6382436f6382436f6382439323990374037b6374037b6374037b6374037b6374037b6374037b6374037b6374037b6374037b6374037b6374037b6374037b637d837ee37d837ee37d837ee
37d837ee37d837ee37d837ee37d837ee37d837ee3ade3ade3b8a37f83b8a37f83b8a37f83b8a37f83b8a37f83b8a37f83b8a37f837f8381e381e3b0038243b0038243b0038243a8c3ade383e39323990
39ca392839323990399639a039ca3a8c3ade3ade3b003b8a0002008b000400040000000600060001000b000c00020013001300040025002a0005002c0036000b0038003f001600450046001e0049004a
0020004c004c0022004f004f0023005100540024005600560028005800580029005a005d002a005f005f002e008a008a002f009c009c003000b000b4003100b600b8003600ba00ba003900bc00bc003a
00bf00c0003b00c200c2003d00c400c4003e00c600cd003f00d100d1004700d300dd004800df00df005300e100e3005400e500ee005700f000f0006100f500f7006200fa00fb006500fd00ff00670102
0104006a01090109006d010c010c006e01170119006f012101210072012b012d0073013001300076013201320077014901490078016c016d0079016f0171007b01ba01ba007e01bd01bd007f01c401c5
008001c801c8008201ca01cb008301cd01cd0085022802280086022a022b008702460247008902490249008b024b026c008c026e027100ae0276027b00b20280028800b8028a028a00c1028c028c00c2
028e028e00c30290029000c40292029b00c502a402a600cf02a802a800d202aa02aa00d302ac02ac00d402ae02ae00d502b102b100d602b302b300d702b502b500d802b702b700d902b902b900da02bb
02bb00db02bd02c900dc02cb02cb00e902cd02cd00ea02cf02cf00eb02da02da00ec02dc02dc00ed02de02de00ee02e002e000ef02e202e200f002e402e400f102e602e600f202e802e800f302ea02ea
00f402ec02ec00f502ee02f100f602f302f300fa02f502f500fb0352035700fc035a03690102036c036c01120370037001130372037201140376037601150379037a0116037c03850118038703890122
038b0390012503920393012b03950398012d039e039f013103a103a1013303a303a3013403a503a8013503ab03b0013903b203b2013f03b603b7014003bc03bc014203be03c7014303ca03cb014d03cd
03d0014f03d703d8015303dc03dc015503de03e4015603e903ea015d03ee0416015f041804180188041a04270189042f042f019704320432019804340434019904400445019a0448044801a0044a044a
01a1044c044c01a2044e044f01a30454045701a5045a045a01a9045c045d01aa045f045f01ac0463046301ad0465046501ae0469046901af04a904a901b0000a0038ffc400d1ffc400d5ffc40132ffc4
013affc402daffc402dcffc402deffc4038dffc4044cffc40015003a0014003b0026003d0016011800140265001602ec002602ee001602f00016035700160366001603690016039f002603a1002603a3
002603a5001603b6001403be00160440001604420016044400160469001600010013ff0800ce0010feee0012feee0025ff40002eff30003800140045ffde0047ffeb0048ffeb0049ffeb004bffeb0053
ffeb0055ffeb0056ffe60059ffea005affe8005dffe80093ffeb0098ffeb009affea00b1ff4000b3ff4000baffeb00bcffe800c7ffeb00c8ffeb00caffea00d1001400d5001400f6ffeb0102ffeb010c
ff400117ffeb0119ffe8011dffeb0121ffeb013200140139ffeb013a0014014bffeb014cffeb0156ffeb016efeee0172feee0176feee0177feee01baffc0024bff40024cff40024dff40024eff40024f
ff400250ff400251ff400266ffde0267ffde0268ffde0269ffde026affde026bffde026cffde026dffeb026effeb026fffeb0270ffeb0271ffeb0277ffeb0278ffeb0279ffeb027affeb027bffeb027c
ffea027dffea027effea027fffea0280ffe80281ffe80282ff400283ffde0284ff400285ffde0286ff400287ffde0289ffeb028bffeb028dffeb028fffeb0291ffeb0293ffeb0295ffeb0297ffeb0299
ffeb029bffeb029dffeb029fffeb02a1ffeb02a3ffeb02b1ff3002c5ffeb02c7ffeb02c9ffeb02da001402dc001402de001402e1ffea02e3ffea02e5ffea02e7ffea02e9ffea02ebffea02efffe80352
ff40035aff40036affeb036effea0370ffeb0372ffe80375ffea0376ffeb0377ffea037eff300382ff40038d0014038fffde0390ffeb0392ffeb0394ffeb0395ffe80397ffeb039effe803a6ffe803ae
ff4003afffde03b2ffeb03b7ffe803b8ffeb03bdffeb03bfffe803c4ff4003c5ffde03c6ff4003c7ffde03cbffeb03cdffeb03ceffeb03d8ffeb03daffeb03dcffeb03e0ffe803e2ffe803e4ffe803eb
ffeb03eeff4003efffde03f0ff4003f1ffde03f2ff4003f3ffde03f4ff4003f5ffde03f6ff4003f7ffde03f8ff4003f9ffde03faff4003fbffde03fcff4003fdffde03feff4003ffffde0400ff400401
ffde0402ff400403ffde0404ff400405ffde0407ffeb0409ffeb040bffeb040dffeb040fffeb0411ffeb0413ffeb0415ffeb041bffeb041dffeb041fffeb0421ffeb0423ffeb0425ffeb0427ffeb0429
ffeb042bffeb042dffeb042fffeb0431ffeb0433ffea0435ffea0437ffea0439ffea043bffea043dffea043fffea0441ffe80443ffe80445ffe8044c001400200038ffdf003affe4003bffec003dffdd
00d1ffdf00d5ffdf0118ffe40132ffdf013affdf01ba000e0265ffdd02daffdf02dcffdf02deffdf02ecffec02eeffdd02f0ffdd0357ffdd0366ffdd0369ffdd038dffdf039fffec03a1ffec03a3ffec
03a5ffdd03b6ffe403beffdd0440ffdd0442ffdd0444ffdd044cffdf0469ffdd001a0038ffce003affed003dffd000d1ffce00d5ffce0118ffed0132ffce013affce0265ffd002daffce02dcffce02de
ffce02eeffd002f0ffd00357ffd00366ffd00369ffd0038dffce03a5ffd003b6ffed03beffd00440ffd00442ffd00444ffd0044cffce0469ffd00010002effee0039ffee0261ffee0262ffee0263ffee
0264ffee02b1ffee02e0ffee02e2ffee02e4ffee02e6ffee02e8ffee02eaffee037effee0432ffee0434ffee004a00060010000b0010000d0014004100120047ffe80048ffe80049ffe8004bffe80055
ffe8006100130093ffe80098ffe800baffe800c7ffe800c8ffe800f6ffe80102ffe8011dffe80121ffe80139ffe8014bffe8014cffe80156ffe8016c0010016d0010016f00100170001001710010026d
ffe8026effe8026fffe80270ffe80271ffe80289ffe8028bffe8028dffe8028fffe80291ffe80293ffe80295ffe80297ffe80299ffe8029bffe8029dffe8029fffe802a1ffe802a3ffe8036affe80390
ffe80394ffe80397ffe803a7001003a8001003ab001003b2ffe803b8ffe803bdffe803cbffe803cdffe803ceffe803daffe803ebffe80407ffe80409ffe8040bffe8040dffe8040fffe80411ffe80413
ffe80415ffe80429ffe8042bffe8042dffe80431ffe8000200f5ffd6016dff98003d0047ffec0048ffec0049ffec004bffec0055ffec0093ffec0098ffec00baffec00c7ffec00c8ffec00f6ffec0102
ffec011dffec0121ffec0139ffec014bffec014cffec0156ffec026dffec026effec026fffec0270ffec0271ffec0289ffec028bffec028dffec028fffec0291ffec0293ffec0295ffec0297ffec0299
ffec029bffec029dffec029fffec02a1ffec02a3ffec036affec0390ffec0394ffec0397ffec03b2ffec03b8ffec03bdffec03cbffec03cdffec03ceffec03daffec03ebffec0407ffec0409ffec040b
ffec040dffec040fffec0411ffec0413ffec0415ffec0429ffec042bffec042dffec0431ffec00180053ffe20117ffe2016d00180277ffe20278ffe20279ffe2027affe2027bffe202c5ffe202c7ffe2
02c9ffe20370ffe20376ffe20392ffe203d8ffe203dcffe2041bffe2041dffe2041fffe20421ffe20423ffe20425ffe20427ffe2042fffe200060010ff840012ff84016eff840172ff840176ff840177
ff840010002effec0039ffec0261ffec0262ffec0263ffec0264ffec02b1ffec02e0ffec02e2ffec02e4ffec02e6ffec02e8ffec02eaffec037effec0432ffec0434ffec001e0006fff2000bfff2005a
fff3005dfff300bcfff300f5fff50119fff3016cfff2016dfff2016ffff20170fff20171fff20280fff30281fff302effff30372fff30395fff3039efff303a6fff303a7fff203a8fff203abfff203b7
fff303bffff303e0fff303e2fff303e4fff30441fff30443fff30445fff3003e0027fff3002bfff30033fff30035fff30083fff30092fff30097fff300b2fff300c3000d00d2fff30107fff30116fff3
011afff3011cfff3011efff30120fff30138fff30155fff30228fff30229fff3022bfff3022cfff30252fff3025cfff3025dfff3025efff3025ffff30260fff30288fff3028afff3028cfff3028efff3
029cfff3029efff302a0fff302a2fff302c4fff302c6fff302c8fff302f9fff30356fff30363fff30389fff3038cfff303b9fff303bcfff303d7fff303d9fff303dbfff3041afff3041cfff3041efff3
0420fff30422fff30424fff30426fff30428fff3042afff3042cfff3042efff30430fff304a9fff3003f0027ffe6002bffe60033ffe60035ffe60083ffe60092ffe60097ffe600b2ffe600b7ffc200c3
001000d2ffe60107ffe60116ffe6011affe6011cffe6011effe60120ffe60138ffe60155ffe60228ffe60229ffe6022bffe6022cffe60252ffe6025cffe6025dffe6025effe6025fffe60260ffe60288
ffe6028affe6028cffe6028effe6029cffe6029effe602a0ffe602a2ffe602c4ffe602c6ffe602c8ffe602f9ffe60356ffe60363ffe60389ffe6038cffe603b9ffe603bcffe603d7ffe603d9ffe603db
ffe6041affe6041cffe6041effe60420ffe60422ffe60424ffe60426ffe60428ffe6042affe6042cffe6042effe60430ffe604a9ffe600370025ffe4003cffd2003dffd300b1ffe400b3ffe400c3ffe2
00d9ffd2010cffe4024bffe4024cffe4024dffe4024effe4024fffe40250ffe40251ffe40265ffd30282ffe40284ffe40286ffe402eeffd302f0ffd30352ffe40357ffd3035affe40366ffd30367ffd2
0369ffd30382ffe4038effd203a5ffd303aeffe403beffd303c1ffd203c4ffe403c6ffe403cfffd203e9ffd203eeffe403f0ffe403f2ffe403f4ffe403f6ffe403f8ffe403faffe403fcffe403feffe4
0400ffe40402ffe40404ffe40440ffd30442ffd30444ffd3044effd20456ffd20469ffd300270010ff460012ff460025ffcd00b1ffcd00b3ffcd00c6fff2010cffcd016eff460172ff460176ff460177
ff46024bffcd024cffcd024dffcd024effcd024fffcd0250ffcd0251ffcd0282ffcd0284ffcd0286ffcd0352ffcd035affcd0382ffcd03aeffcd03c4ffcd03c6ffcd03eeffcd03f0ffcd03f2ffcd03f4
ffcd03f6ffcd03f8ffcd03faffcd03fcffcd03feffcd0400ffcd0402ffcd0404ffcd000100c3000e00af0047ffdc0048ffdc0049ffdc004bffdc0051ffc10052ffc10053ffd60054ffc10055ffdc0059
ffdd005affe1005dffe10093ffdc0098ffdc009affdd00baffdc00bcffe100beffe600c0ffc100c1ffeb00c2ffe900c4fff000c5ffe700c7ffdc00c8ffdc00c9ffe300caffdd00cbffce00ccffd400cd
ffdb00ebffc100efffc100f0ffc100f2ffc100f3ffc100f4ffc100f6ffdc00f7ffc100f9ffc100faffc100fdffc100ffffc10102ffdc0104ffc10117ffd60119ffe1011dffdc0121ffdc0135ffc10139
ffdc0144ffc10149ffc1014bffdc014cffdc0156ffdc026dffdc026effdc026fffdc0270ffdc0271ffdc0276ffc10277ffd60278ffd60279ffd6027affd6027bffd6027cffdd027dffdd027effdd027f
ffdd0280ffe10281ffe10289ffdc028bffdc028dffdc028fffdc0291ffdc0293ffdc0295ffdc0297ffdc0299ffdc029bffdc029dffdc029fffdc02a1ffdc02a3ffdc02beffc102c0ffc102c2ffc102c3
ffc102c5ffd602c7ffd602c9ffd602e1ffdd02e3ffdd02e5ffdd02e7ffdd02e9ffdd02ebffdd02efffe1036affdc036cffc1036effdd0370ffd60372ffe10375ffdd0376ffd60377ffdd0390ffdc0391
ffc10392ffd60393ffc10394ffdc0395ffe10397ffdc0398ffc1039dffc1039effe103a6ffe103adffc103b2ffdc03b3ffc103b7ffe103b8ffdc03bdffdc03bfffe103cbffdc03cdffdc03ceffdc03d4
ffc103d6ffc103d8ffd603daffdc03dcffd603e0ffe103e2ffe103e4ffe103e8ffc103ebffdc0407ffdc0409ffdc040bffdc040dffdc040fffdc0411ffdc0413ffdc0415ffdc041bffd6041dffd6041f
ffd60421ffd60423ffd60425ffd60427ffd60429ffdc042bffdc042dffdc042fffd60431ffdc0433ffdd0435ffdd0437ffdd0439ffdd043bffdd043dffdd043fffdd0441ffe10443ffe10445ffe10449
ffc1044bffc10455ffc10462ffc10464ffc10466ffc100760006ffda000bffda0047fff00048fff00049fff0004bfff00055fff00059ffef005affdc005dffdc0093fff00098fff0009affef00bafff0
00bcffdc00c1ffec00c3000f00c5ffea00c7fff000c8fff000c9ffce00caffef00cbffe700f6fff00102fff00119ffdc011dfff00121fff00139fff0014bfff0014cfff00156fff0016cffda016dffda
016fffda0170ffda0171ffda026dfff0026efff0026ffff00270fff00271fff0027cffef027dffef027effef027fffef0280ffdc0281ffdc0289fff0028bfff0028dfff0028ffff00291fff00293fff0
0295fff00297fff00299fff0029bfff0029dfff0029ffff002a1fff002a3fff002e1ffef02e3ffef02e5ffef02e7ffef02e9ffef02ebffef02efffdc036afff0036effef0372ffdc0375ffef0377ffef
0390fff00394fff00395ffdc0397fff0039effdc03a6ffdc03a7ffda03a8ffda03abffda03b2fff003b7ffdc03b8fff003bdfff003bfffdc03cbfff003cdfff003cefff003dafff003e0ffdc03e2ffdc
03e4ffdc03ebfff00407fff00409fff0040bfff0040dfff0040ffff00411fff00413fff00415fff00429fff0042bfff0042dfff00431fff00433ffef0435ffef0437ffef0439ffef043bffef043dffef
043fffef0441ffdc0443ffdc0445ffdc00440010000c0012000c0047ffe70048ffe70049ffe7004bffe70055ffe70093ffe70098ffe700baffe700c3000f00c7ffe700c8ffe700f6ffe70102ffe7011d
ffe70121ffe70139ffe7014bffe7014cffe70156ffe7016e000c0172000c0176000c0177000c026dffe7026effe7026fffe70270ffe70271ffe70289ffe7028bffe7028dffe7028fffe70291ffe70293
ffe70295ffe70297ffe70299ffe7029bffe7029dffe7029fffe702a1ffe702a3ffe7036affe70390ffe70394ffe70397ffe703b2ffe703b8ffe703bdffe703cbffe703cdffe703ceffe703daffe703eb
ffe70407ffe70409ffe7040bffe7040dffe7040fffe70411ffe70413ffe70415ffe70429ffe7042bffe7042dffe70431ffe7000600c9ffea00ecffee00f5ffd500fdffed0133ffec0158ffec000100f5
ffc0000100c90020007e0006000c000b000c0047ffe80048ffe80049ffe8004a000c004bffe80053ffea0055ffe8005a000b005d000b0093ffe80098ffe800baffe800bc000b00c3ff9000c5000b00c7
ffe800c8ffe800c9000c00f6ffe80102ffe80117ffea0119000b011dffe80121ffe80139ffe8014bffe8014cffe80156ffe8016c000c016d000c016f000c0170000c0171000c01baffbf01bcffee01c0
ffec01c8ffed01caffec01ccfff501cd000e01cf000d01d2000d026dffe8026effe8026fffe80270ffe80271ffe80277ffea0278ffea0279ffea027affea027bffea0280000b0281000b0289ffe8028b
ffe8028dffe8028fffe80291ffe80293ffe80295ffe80297ffe80299ffe8029bffe8029dffe8029fffe802a1ffe802a3ffe802c5ffea02c7ffea02c9ffea02ef000b036affe80370ffea0372000b0376
ffea0390ffe80392ffea0394ffe80395000b0397ffe8039e000b03a6000b03a7000c03a8000c03ab000c03b2ffe803b7000b03b8ffe803bdffe803bf000b03cbffe803cdffe803ceffe803d8ffea03da
ffe803dcffea03e0000b03e2000b03e4000b03ebffe80407ffe80409ffe8040bffe8040dffe8040fffe80411ffe80413ffe80415ffe8041bffea041dffea041fffea0421ffea0423ffea0425ffea0427
ffea0429ffe8042bffe8042dffe8042fffea0431ffe80441000b0443000b0445000b000100f5ffe2000d005cffed005effed00edffed00f5ffc002f2ffed02f4ffed02f6ffed0396ffed03c2ffed03d0
ffed03eaffed044fffed0457ffed000c005cfff2005efff200edfff202f2fff202f4fff202f6fff20396fff203c2fff203d0fff203eafff2044ffff20457fff2001f005afff4005cfff2005dfff4005e
fff300bcfff400edfff20119fff40280fff40281fff402effff402f2fff302f4fff302f6fff30372fff40395fff40396fff2039efff403a6fff403b7fff403bffff403c2fff203d0fff203e0fff403e2
fff403e4fff403eafff20441fff40443fff40445fff4044ffff20457fff2005d0006ffca000bffca0038ffd2003affd4003cfff4003dffd3005affe6005cffef005dffe600bcffe600d1ffd200d5ffd2
00d9fff400ddffed00e0ffe100e5ffd400edffef00f5ffc900fdffd10108ffe50118ffd40119ffe6011fffe30132ffd20133ffc4013affd2013cffe1014dffd4014efff5014fffe70157ff640158ffc9
016cffca016dffca016fffca0170ffca0171ffca0265ffd30280ffe60281ffe602daffd202dcffd202deffd202eeffd302efffe602f0ffd30357ffd30366ffd30367fff40369ffd30372ffe60381ffed
038dffd2038efff40395ffe60396ffef039effe603a5ffd303a6ffe603a7ffca03a8ffca03abffca03b6ffd403b7ffe603beffd303bfffe603c1fff403c2ffef03cffff403d0ffef03dfffed03e0ffe6
03e1ffed03e2ffe603e3ffed03e4ffe603e5ffe103e9fff403eaffef0440ffd30441ffe60442ffd30443ffe60444ffd30445ffe6044cffd2044efff4044fffef0450ffe10452ffe10456fff40457ffef
0469ffd3006c0006ffc0000bffc00038ff9d003affc7003cfff0003dffab0051ffd20052ffd20054ffd200c0ffd200d1ff9d00d3fff500d5ff9d00d9fff000dcfff500ddffea00e0ffe500e5ffc100eb
ffd200efffd200f0ffd200f2ffd200f3ffd200f4ffd200f5ffcd00f7ffd200f9ffd200faffd200fdffd200ffffd20104ffd20118ffc70132ff9d0133ffcc0135ffd2013aff9d013cffe5013fffdf0144
ffd20149ffd2014dffce014fffea0151fff50157ff9e0158ffce016cffc0016dffc0016fffc00170ffc00171ffc00265ffab0276ffd202beffd202c0ffd202c2ffd202c3ffd202daff9d02dcff9d02de
ff9d02eeffab02f0ffab0357ffab0366ffab0367fff00369ffab036cffd20381ffea038dff9d038efff00391ffd20393ffd20398ffd2039dffd203a5ffab03a7ffc003a8ffc003abffc003adffd203b3
ffd203b6ffc703beffab03c1fff003cffff003d4ffd203d6ffd203dfffea03e1ffea03e3ffea03e5ffe503e8ffd203e9fff003ecfff50440ffab0442ffab0444ffab0449ffd2044bffd2044cff9d044e
fff00450ffe50452ffe50455ffd20456fff00462ffd20464ffd20466ffd20467fff50469ffab006f0006ffb1000bffb10038ff9e003affc5003cfff2003dffa80051ffcf0052ffcf0054ffcf005cffef
00c0ffcf00d1ff9e00d5ff9e00d9fff200ddffec00e0ffe100e5ffc200ebffcf00edffef00efffcf00f0ffcf00f2ffcf00f3ffcf00f4ffcf00f5ffc600f7ffcf00f9ffcf00faffcf00fdffcf00ffffcf
0104ffcf0118ffc50132ff9e0133ffc00135ffcf013aff9e013cffe1013fffdf0144ffcf0149ffcf014dffcd014fffe80157ff9f0158ffc6016cffb1016dffb1016fffb10170ffb10171ffb10265ffa8
0276ffcf02beffcf02c0ffcf02c2ffcf02c3ffcf02daff9e02dcff9e02deff9e02eeffa802f0ffa80357ffa80366ffa80367fff20369ffa8036cffcf0381ffec038dff9e038efff20391ffcf0393ffcf
0396ffef0398ffcf039dffcf03a5ffa803a7ffb103a8ffb103abffb103adffcf03b3ffcf03b6ffc503beffa803c1fff203c2ffef03cffff203d0ffef03d4ffcf03d6ffcf03dfffec03e1ffec03e3ffec
03e5ffe103e8ffcf03e9fff203eaffef0440ffa80442ffa80444ffa80449ffcf044bffcf044cff9e044efff2044fffef0450ffe10452ffe10455ffcf0456fff20457ffef0462ffcf0464ffcf0466ffcf
0469ffa8004d0038ffbe0051ffe10052ffe10054ffe1005affef005dffef00bcffef00c0ffe100d1ffbe00d5ffbe00e5ffc900ebffe100efffe100f0ffe100f2ffe100f3ffe100f4ffe100f5ffdf00f7
ffe100f9ffe100faffe100fdffe100ffffe10104ffe10108ffed0119ffef011fffeb0132ffbe0133ffdf0135ffe1013affbe013fffe90144ffe10149ffe1014efff50158ffe00276ffe10280ffef0281
ffef02beffe102c0ffe102c2ffe102c3ffe102daffbe02dcffbe02deffbe02efffef036cffe10372ffef038dffbe0391ffe10393ffe10395ffef0398ffe1039dffe1039effef03a6ffef03adffe103b3
ffe103b7ffef03bfffef03d4ffe103d6ffe103e0ffef03e2ffef03e4ffef03e8ffe10441ffef0443ffef0445ffef0449ffe1044bffe1044cffbe0455ffe10462ffe10464ffe10466ffe100640038ffe6
003affe7003cfff2003dffe70051ffd60052ffd60054ffd6005cfff100c0ffd600d1ffe600d5ffe600d9fff200ddffee00e0ffe800e5ffe600ebffd600edfff100efffd600f0ffd600f2ffd600f3ffd6
00f4ffd600f5ffd000f7ffd600f9ffd600faffd600fdffd600ffffd60104ffd60118ffe70132ffe60133ffce0135ffd6013affe6013cffe80144ffd60149ffd6014dffe7014fffed0157ffe60158ffd0
0265ffe70276ffd602beffd602c0ffd602c2ffd602c3ffd602daffe602dcffe602deffe602eeffe702f0ffe70357ffe70366ffe70367fff20369ffe7036cffd60381ffee038dffe6038efff20391ffd6
0393ffd60396fff10398ffd6039dffd603a5ffe703adffd603b3ffd603b6ffe703beffe703c1fff203c2fff103cffff203d0fff103d4ffd603d6ffd603dfffee03e1ffee03e3ffee03e5ffe803e8ffd6
03e9fff203eafff10440ffe70442ffe70444ffe70449ffd6044bffd6044cffe6044efff2044ffff10450ffe80452ffe80455ffd60456fff20457fff10462ffd60464ffd60466ffd60469ffe700930025
00100027ffe8002bffe80033ffe80035ffe80038ffe0003affe0003dffdf0083ffe80092ffe80097ffe800b1001000b2ffe800b3001000d1ffe000d2ffe800d3001000d5ffe000d8001400dc001000e0
ffe100e5ffe000ec001300f1001000f8ffe0010300100107ffe8010c00100116ffe80118ffe0011affe8011cffe8011effe80120ffe80132ffe00138ffe8013affe0013cffe1013dffe00140ffe10145
ffe9014dffdf014fffde015100100155ffe80157ffdf0159fff20228ffe80229ffe8022bffe8022cffe8024b0010024c0010024d0010024e0010024f001002500010025100100252ffe8025cffe8025d
ffe8025effe8025fffe80260ffe80265ffdf0282001002840010028600100288ffe8028affe8028cffe8028effe8029cffe8029effe802a0ffe802a2ffe802c4ffe802c6ffe802c8ffe802daffe002dc
ffe002deffe002eeffdf02f0ffdf02f9ffe8035200100356ffe80357ffdf035a00100363ffe80366ffdf0369ffdf038200100389ffe8038cffe8038dffe003a5ffdf03ae001003b6ffe003b9ffe803bc
ffe803beffdf03c4001003c6001003d7ffe803d9ffe803dbffe803e5ffe103e6ffe003ec001003ed001003ee001003f0001003f2001003f4001003f6001003f8001003fa001003fc001003fe00100400
00100402001004040010041affe8041cffe8041effe80420ffe80422ffe80424ffe80426ffe80428ffe8042affe8042cffe8042effe80430ffe80440ffdf0442ffdf0444ffdf044cffe00450ffe10451
ffe00452ffe10453ffe004670010046800100469ffdf04a9ffe80032001bfff20038fff1003afff4003cfff4003dfff000d1fff100d3fff500d5fff100d9fff400dcfff500ddfff300e5fff10118fff4
0132fff1013afff1014dfff2014ffff20151fff50157fff20265fff002dafff102dcfff102defff102eefff002f0fff00357fff00366fff00367fff40369fff00381fff3038dfff1038efff403a5fff0
03b6fff403befff003c1fff403cffff403dffff303e1fff303e3fff303e9fff403ecfff50440fff00442fff00444fff0044cfff1044efff40456fff40467fff50469fff000660025000f0038ffe6003a
ffe6003c000e003dffe600b1000f00b3000f00d1ffe600d3000e00d5ffe600d8001300d9000e00dc000e00dd000b00e0ffe500e5ffe600e6fff400ec001200f1000f00f5ffe700f8ffe800fdffe70103
000f010c000f0118ffe60132ffe60133ffe7013affe6013cffe5013dffe8014dffe6014fffe60151000e0157ffe60158ffe7024b000f024c000f024d000f024e000f024f000f0250000f0251000f0265
ffe60282000f0284000f0286000f02daffe602dcffe602deffe602eeffe602f0ffe60352000f0357ffe6035a000f0366ffe60367000e0369ffe60381000b0382000f038dffe6038e000e03a5ffe603ae
000f03b6ffe603beffe603c1000e03c4000f03c6000f03cf000e03df000b03e1000b03e3000b03e5ffe503e6ffe803e9000e03ec000e03ed000f03ee000f03f0000f03f2000f03f4000f03f6000f03f8
000f03fa000f03fc000f03fe000f0400000f0402000f0404000f0440ffe60442ffe60444ffe6044cffe6044e000e0450ffe50451ffe80452ffe50453ffe80456000e0467000e0468000f0469ffe60037
0006ffbf000bffbf0038ff9f003affc9003dffad00d1ff9f00d5ff9f00ddffec00e0ffe600e5ffc400f5ffcd00fdffd50118ffc90132ff9f0133ffcc013aff9f013cffe6013fffdf014dffd1014fffec
0157ffa10158ffcf016cffbf016dffbf016fffbf0170ffbf0171ffbf0265ffad02daff9f02dcff9f02deff9f02eeffad02f0ffad0357ffad0366ffad0369ffad0381ffec038dff9f03a5ffad03a7ffbf
03a8ffbf03abffbf03b6ffc903beffad03dfffec03e1ffec03e3ffec03e5ffe60440ffad0442ffad0444ffad044cff9f0450ffe60452ffe60469ffad00300038ffe3003cffe5003dffe400d1ffe300d3
ffe500d5ffe300d8ffe200d9ffe500dcffe500ddffe900f1ffea0103ffea0132ffe3013affe30151ffe50157ffe40265ffe402daffe302dcffe302deffe302eeffe402f0ffe40357ffe40366ffe40367
ffe50369ffe40381ffe9038dffe3038effe503a5ffe403beffe403c1ffe503cfffe503dfffe903e1ffe903e3ffe903e9ffe503ecffe503edffea0440ffe40442ffe40444ffe4044cffe3044effe50456
ffe50467ffe50468ffea0469ffe400230038ffe2003cffe400d1ffe200d3ffe400d5ffe200d8ffe100d9ffe400dcffe400ddffe900ecffe400f1ffeb0103ffeb0132ffe2013affe20151ffe402daffe2
02dcffe202deffe20367ffe40381ffe9038dffe2038effe403c1ffe403cfffe403dfffe903e1ffe903e3ffe903e9ffe403ecffe403edffeb044cffe2044effe40456ffe40467ffe40468ffeb00170038
ffeb003dfff300d1ffeb00d5ffeb0132ffeb013affeb0265fff302daffeb02dcffeb02deffeb02eefff302f0fff30357fff30366fff30369fff3038dffeb03a5fff303befff30440fff30442fff30444
fff3044cffeb0469fff300360051ffef0052ffef0054ffef005cfff000c0ffef00ebffef00ecffee00edfff000efffef00f0ffef00f2ffef00f3ffef00f4ffef00f5ffee00f7ffef00f9ffef00faffef
00fdffef00ffffef0104ffef0108fff4011ffff10133ffef0135ffef0144ffef0149ffef0158ffef0276ffef02beffef02c0ffef02c2ffef02c3ffef036cffef0391ffef0393ffef0396fff00398ffef
039dffef03adffef03b3ffef03c2fff003d0fff003d4ffef03d6ffef03e8ffef03eafff00449ffef044bffef044ffff00455ffef0457fff00462ffef0464ffef0466ffef00220006fff2000bfff2005a
fff5005dfff500bcfff500f5fff400fdfff40108fff50119fff50133fff50158fff5016cfff2016dfff2016ffff20170fff20171fff20280fff50281fff502effff50372fff50395fff5039efff503a6
fff503a7fff203a8fff203abfff203b7fff503bffff503e0fff503e2fff503e4fff50441fff50443fff50445fff500320051ffee0052ffee0054ffee00c0ffee00ebffee00ec001400efffee00f0ffee
00f2ffee00f3ffee00f4ffee00f5ffed00f7ffee00f8ffed00f9ffee00faffee00fbffd000fdffee00ffffee0104ffee0133ffed0135ffee013dffed0144ffee0149ffee0158ffed0276ffee02beffee
02c0ffee02c2ffee02c3ffee036cffee0391ffee0393ffee0398ffee039dffee03adffee03b3ffee03d4ffee03d6ffee03e6ffed03e8ffee0449ffee044bffee0451ffed0453ffed0455ffee0462ffee
0464ffee0466ffee000a0006fff5000bfff5016cfff5016dfff5016ffff50170fff50171fff503a7fff503a8fff503abfff500590047fff00048fff00049fff0004bfff00053ffc70055fff00093fff0
0098fff000bafff000c7fff000c8fff000f6fff00102fff00117ffc7011bffeb011dfff00121fff00139fff0014bfff0014cfff00156fff001bcffeb01c0ffe901c8ffeb01caffeb026dfff0026efff0
026ffff00270fff00271fff00277ffc70278ffc70279ffc7027affc7027bffc70289fff0028bfff0028dfff0028ffff00291fff00293fff00295fff00297fff00299fff0029bfff0029dfff0029ffff0
02a1fff002a3fff002c5ffc702c7ffc702c9ffc7036afff00370ffc70376ffc70390fff00392ffc70394fff00397fff003b2fff003b8fff003bdfff003cbfff003cdfff003cefff003d8ffc703dafff0
03dcffc703ebfff00407fff00409fff0040bfff0040dfff0040ffff00411fff00413fff00415fff0041bffc7041dffc7041fffc70421ffc70423ffc70425ffc70427ffc70429fff0042bfff0042dfff0
042fffc70431fff000a10006000d000b000d0045fff00047ffc00048ffc00049ffc0004a000d004bffc00053ffe20055ffc0005a000b005d000b0093ffc00098ffc000baffc000bc000b00c6ffd600c7
ffc000c8ffc000cbffd500ecffc800f1ffd700f6ffc00102ffc00103ffd70117ffe20119000b011bffec011dffc0011f000c0121ffc00139ffc0014bffc0014cffc0014e000b0150000b0156ffc0016c
000d016d000d016f000d0170000d0171000d01baffbf01bcffee01c0ffec01c8ffed01caffec01ccfff501cd000e01cf000d01d2000d0266fff00267fff00268fff00269fff0026afff0026bfff0026c
fff0026dffc0026effc0026fffc00270ffc00271ffc00277ffe20278ffe20279ffe2027affe2027bffe20280000b0281000b0283fff00285fff00287fff00289ffc0028bffc0028dffc0028fffc00291
ffc00293ffc00295ffc00297ffc00299ffc0029bffc0029dffc0029fffc002a1ffc002a3ffc002c5ffe202c7ffe202c9ffe202ef000b036affc00370ffe20372000b0376ffe2038ffff00390ffc00392
ffe20394ffc00395000b0397ffc0039e000b03a6000b03a7000d03a8000d03ab000d03affff003b2ffc003b7000b03b8ffc003bdffc003bf000b03c5fff003c7fff003cbffc003cdffc003ceffc003d8
ffe203daffc003dcffe203e0000b03e2000b03e4000b03ebffc003edffd703effff003f1fff003f3fff003f5fff003f7fff003f9fff003fbfff003fdfff003fffff00401fff00403fff00405fff00407
ffc00409ffc0040bffc0040dffc0040fffc00411ffc00413ffc00415ffc0041bffe2041dffe2041fffe20421ffe20423ffe20425ffe20427ffe20429ffc0042bffc0042dffc0042fffe20431ffc00441
000b0443000b0445000b0468ffd7000f00ec001400f1001000f5fff000f8fff000fdfff001000016010300100133ffe6013dffdc0158fff003e6fff003ed00100451fff00453fff004680010004c0047
ffee0048ffee0049ffee004bffee0055ffee0093ffee0098ffee00baffee00c7ffee00c8ffee00ec001200f1000e00f5ffe300f6ffee00f8ffe300fbffb800fdffe30102ffee0103000e011dffee0121
ffee0133ffba0139ffee013dffd9014bffee014cffee0156ffee0158ffe3026dffee026effee026fffee0270ffee0271ffee0289ffee028bffee028dffee028fffee0291ffee0293ffee0295ffee0297
ffee0299ffee029bffee029dffee029fffee02a1ffee02a3ffee036affee0390ffee0394ffee0397ffee03b2ffee03b8ffee03bdffee03cbffee03cdffee03ceffee03daffee03e6ffe303ebffee03ed
000e0407ffee0409ffee040bffee040dffee040fffee0411ffee0413ffee0415ffee0429ffee042bffee042dffee0431ffee0451ffe30453ffe30468000e0020005affc0005dffc000bcffc000f5ff80
00f8ffee00fdfff00108ffdb0119ffc0011fffdc0133ff47013dffee014e00070150fff40158ff7f0280ffc00281ffc002efffc00372ffc00395ffc0039effc003a6ffc003b7ffc003bfffc003e0ffc0
03e2ffc003e4ffc003e6ffee0441ffc00443ffc00445ffc00451ffee0453ffee0021005afff4005cfff0005dfff400bcfff400ecffef00edfff000f1fff300fdffee0103fff30119fff40280fff40281
fff402effff40372fff40395fff40396fff0039efff403a6fff403b7fff403bffff403c2fff003d0fff003e0fff403e2fff403e4fff403eafff003edfff30441fff40443fff40445fff4044ffff00457
fff00468fff3000a0006ffd6000bffd6016cffd6016dffd6016fffd60170ffd60171ffd603a7ffd603a8ffd603abffd60015005cffe000edffe000f5ff7600f8ffc200fdffd30108ffd9011fffdb0133
ff1e013dffed014efff00150fff20158ff560396ffe003c2ffe003d0ffe003e6ffc203eaffe0044fffe00451ffc20453ffc20457ffe0000d00f5ff6400f8ffd200fdffd90108ffd9011fffdb0133ff1e
013dffed014efff00150fff20158ff5603e6ffd20451ffd20453ffd2000900f5ff6a00fdffc60108ffd9011fffdb0133ff1e013dffed014efff00150fff20158ff56000a0006ffd7000bffd7016cffd7
016dffd7016fffd70170ffd70171ffd703a7ffd703a8ffd703abffd7005c0047ff980048ff980049ff98004bff980053ff700055ff980057ff18005b000b0093ff980098ff9800baff9800c7ff9800c8
ff9800f6ff980102ff980117ff70011dff980121ff980139ff98014bff98014cff980156ff98026dff98026eff98026fff980270ff980271ff980277ff700278ff700279ff70027aff70027bff700289
ff98028bff98028dff98028fff980291ff980293ff980295ff980297ff980299ff98029bff98029dff98029fff9802a1ff9802a3ff9802c5ff7002c7ff7002c9ff7002d1ff1802d3ff1802d5ff1802d7
ff1802d9ff18036aff980370ff700376ff700390ff980392ff700394ff980397ff980399ff1803b2ff9803b8ff9803bdff9803cbff9803cdff9803ceff9803d8ff7003daff9803dcff7003ebff980407
ff980409ff98040bff98040dff98040fff980411ff980413ff980415ff98041bff70041dff70041fff700421ff700423ff700425ff700427ff700429ff98042bff98042dff98042fff700431ff980009
01bcfff201c0fff201c8fff201cafff201cdffc001ceffec01cfffc701d0ffd801d2ffbf000201cfffee01d0fff5000201c8ffeb01caffeb000701c8ffef01cafff001cdffbb01ceffec01cfffb701d0
ffd501d2ffb4000401cdffee01cffff101d1ffec01d2ffea000401cdffe901cfffeb01d0fff101d2ffe5000401cdfff201cffff101d0fff501d2ffee000201cf000d01d2000d000b005bffcc01ba0013
01bcfff301c0fff101c8fff201cafff201cdffbd01ceffee01cfffb801d0ffd701d2ffb70004004a001400580032005b0011016d00100008005bffe500b7ffcb00ccffe401ba000d01bcffed01c0ffeb
01c8ffec01caffec00020110000b0157ffe600080058000e0081fed700c3ff9800c6ffc700d8ff1200ecff52014affcf01baff800009000d000f0041000c0056ffeb0061000e01baffcb01bcffe901c0
ffe701c8ffe701caffe70001005b000b0009000d0014004100110056ffe20061001301baffb401bcffd901c0ffd901c8ffd901caffd90004000dffe60041fff40061ffef0140ffed000600c9ffea00ec
ffee00f5ffd600fdffed0133ffec0158ffec001200d8ffae00e5001200eaffe000ecffad00eeffd600fcffdf0100ffd20106ffe0011bffce012bffdd012dffe20131ffe00137ffe0013dffe90140ffda
014affbd0154ffdf01570011001d0023ffaf0058ffef005bffdf0099ffee00b7ffe500b8ffd100c3001100c9ffc800d8001300e5ffc500f5ffca00fdffd00133ff81013cff65013dff85013fff660140
ffdd0145fff2014dffb1014fffca0157ffa90158ffc801c0fff501c8fff501cdffc701cefff101cfffcd01d0ffdd01d2ffc4000800f5fff000fdfff00108fff1011ffff30133fff1014efff30150fff3
0158fff10005004affee005bffea01cffff001d0ffed01d2fff0000200f5fff5016dffc0000900c9ffea00ecffb800f5ffe20108fff0011ffff10133ffeb014efff50158ffec016dff90000101baffeb
0006004a000d00c5000b00c6ffea00c9000c00ecffc8011bfff1003a0004ffc40056ffbf005bffd1006dff6c007cff6e0081ff430086ffac0089ffa100b7ffb800beff7e00c2ff7b00c5ff9b00c6ff79
00c9ffb200cbff7e00ccff7d00cdff7c00d8ffaf00e5000f00e9ffe400eaffa000ecff7400eeff8000f5ffb200fcff7d00fdffb200feff800100ff79010100280106ff7d0108ff7f011bff66011fffda
012bff81012dff980131ff7d0133ffb30137ffa0013dff7c013fff9a0140ff6c0145ffe6014aff6b014eff920150ffad0154ff7b0157000f0158ff910159fff201baffaf01bcffb901c0ffb901c8ffb9
01caffb901ccffbc01cdfff101d0fff101d1ffed000200ecff68011bffee001700b7ffd400c1ffed00c3001100c9ffe000cbffe700ccffe500cdffee00d8001200e9ffe900f5ffd70133ffd7013dffd3
013fffd60140ffc50145ffe7014d000d014f000c0158ffd60159fff201bcffe901c0ffe701c8ffe701caffe90001011bfff1000200f5ffd6016dff88000a00e5ffc300f5ffcf00fdffd40133ffce013c
ffe7013fffdf014dffd1014fffec0157ffa00158ffd100300056ff7e005bff9d006dfef1007cfef40081feab0086ff5e0089ff4b00b7ff7200beff0f00c2ff0a00c5ff4100c6ff0700c9ff6800cbff0f
00ccff0e00cdff0c00d8ff6300e5000500e9ffbd00eaff4900ecfefe00eeff1300f5ff6800fcff0e00fdff6800feff130100ff07010100300106ff0e0108ff11011bfee7011fffac012bff15012dff3c
0131ff0e0133ff6a0137ff49013dff0c013fff3f0140fef10145ffc0014afeef014eff310150ff5f0154ff0a015700050158ff300159ffd50014005bffc100b7ffc500c9ffb400e9ffd700f5ffb900fd
ffe90108ffb2011bffd2011fffc80133ffa0013dffc50145ffe4014effcc0150ffcc0158ffcb0159ffef01bcffe801c0ffe601c8ffe701caffe7000800d8001500ec0015013cffe4013dffe5013fffe4
014dffe3014fffe20157ffe40022000affe2000d0014000effcf00410012004affea0056ffd80058ffea00610013006dffae007cffcd0081ffa00086ffc10089ffc000b7ffd000bbffea00beffc600bf
000d00c1ffe900c2ffd600c5ffe800c6ffba00c9ffe900cbffcb00ccffda00cdffc70175ffd301baffab01bcffcd01c0ffcb01c8ffcb01caffcb01cdfff301d0fff301d1ffef00090081ffdf00b4fff3
00b6fff000c3ffea00d8ffdf00e5ffe00157ffe001baffed01d1fff50002078a000400000a5e12360021001d0000ffdbff88ffceffc5ffecffa5ffa40000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000fee3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000ff88000000000000ffd0fff40000ffebff88ffefffb3ffd9ff6afff5ffce000c0011ffc90012ffdf00000000000000000000000000000000000000000000000000000000000000000000ffe50000
ffe80000ffc90000000000000000000000000000fff30000000000000000000000000000000000000000000000000000000000000000ffe3000000000000000000000000000000000000000000000000
000000000000000000000000000000000000ffeb000000000000000000000000ffab0000ffea0000ffd5000000000000ffe10000000000000000ff86ffeaffe900000000000000000000000000000000
ffed0000ffed0000000000140000000000000000ffefffe60000000000000000000000000000000000000000000000000000000000000012000000000000000000000000ffe3000000000000ffe40000
000000000011ffe40011ffe50000000000110000000000000000000000000000ffea00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000ffe60000ffe50000ffe100000000000000000000ffe9ffd80000000000000000ffa30000000000000000ff5c0000000000000000fee0001300000000000000000000
ffc0ff33ffe8ff32ffa3fee9fff2ff85000000000000000000000000000000000000000000000000000000000000ff4efff5fff30000fff300000000000000000000000000000000000f0000ff6f0000
ffa700000000fe6cffcdffdc0000ff480000000000000000ff88ff58ffa7ffa7ff30ffb4ffe4001000000010000f0010ffbfffaeffc4ffcb0000ff7eff7c0000fefe00000000fef0ff28fff0ffb30000
0000ffb5ffd2ffd40000ffd20000fff300000000000000000000ffe4fff5000000000000000000000000ff2900000000ff6300000000000000000000ffd5ffdfffe10000ffe100000000000e00000000
00000000ffed0000000000000000000000000000ff7100000000ffc40000000000000000000000000000ffe60000ffeb0000ffe700000000000e00000000ffebffe10000001100000011ffd100000000
00000000ff6400000000000000000000ff6affc1ffbfffd8ffbfffc6ffe30011ffa0001200110012ffd9ffecffe200000000000000000000ff19000d0000ff68ffa0fff0ffe900000000000d0000ffeb
0000ffeb0000ffe600000000000000000000ffedffe500000000000000000000000000000000000000000000000000000000ffef0000000000000000000000000000000000000000000000000000fff1
00000000000000000000000000000000000000000000000000000000ffe30000000000000000000000000000000000000000000000000000fff5fff100000000fff20000000000000000000000000000
0000fff10000fff50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fff20000000000000000000000000000
000000000000000000000000fff300000000000000000000000000000000000000000000000000000000ffb0000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000ffa80000000000000000000000000000000000000000000000000000fff1fff000000000fff000000000000000000000000000000000ffeb00000010
0000ffe2ffed0000ffdc000000000000000000000000000000000012000000000000000000000000ff530000000000000000000000000000000f0000fff1fff30000fff1000000000000000000000000
000000000000000000000000ffd700000000ff590000000000000000000000000000000000000000ffec0000ffd800000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000fff00000fff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff33ff5fff55ff55
ff66ff6bffbd00070000000700050007ff7eff61ff86ff920000ff0fff0c0000fe3600000000fe1e0000ffd1ff6a0000ffc00000000000000000000000000000ff9f0000ffc80000ffad000000000000
0000ffe700000000ffeb000000000000000000000000ffc900000000ffa5ffafffbdffaeffbdffd2ffe900120000000000000012000000000000ffca0000ffbbffe90000fe7700000000ff3900000000
00000000000000000000ffec0000ffec000000000000000000000000000000000000000000000000ffd80000000000000000000000000000000000000000ff7900000000000000000000000000000000
00000000000000000000ffb50000000000000000ffe30000000000000000000000000000ffeb00020078000600060000000b000b0001001000100002001200120003002500290004002c003400090038
003e001200450047001900490049001c004c004c001d00510054001e005600560022005a005a0023005c005e0024008a008a002700b000b3002800bc00bc002c00c000c0002d00c600c6002e00d300d4
002f00d600d6003100d900d9003200db00dd003300df00df003600e100e1003700e300e3003800e500e5003900eb00eb003a00ed00ed003b00f600f6003c00fb00fb003d00fd00fe003e010301040040
010901090042010c010c0043011701190044012b012d004701300130004a01320132004b01490149004c016c0172004d017601770054022802280056022a022b005702460247005902490249005b024b
0271005c0276027b00830280029000890292029b009a02a402a600a402a802a800a702aa02aa00a802ac02ac00a902ae02ae00aa02b102b100ab02b302b300ac02b502b500ad02b702b700ae02b902b9
00af02bb02bb00b002bd02c900b102cb02cb00be02cd02cd00bf02cf02cf00c002da02da00c102dc02dc00c202de02de00c302e002e000c402e202e200c502e402e400c602e602e600c702e802e800c8
02ea02ea00c902ec02ec00ca02ee02f600cb0352035700d4035a036900da036c036c00ea0370037000eb0372037200ec0376037600ed0379037a00ee037c038500f00387038900fa038b039000fd0392
03980103039e039f010a03a103a1010c03a303a3010d03a503a8010e03ab03b0011203b203b2011803b603b7011903bc03c7011b03ca03cb012703cd03d0012903d703d8012d03dc03dc012f03de03e4
013003e903ea013703ee04160139041804180162041a04270163042f042f017104320432017204340434017304400445017404480448017a044a044a017b044c044c017c044e044f017d04540457017f
045a045a0183045c045d0184045f045f018604630463018704650465018804690469018904a904a9018a0002014e00100010000100120012000100250025000200260026000300270027000400280028
0005002900290006002c002d0007002e002e0008002f002f000900300030000a00310032000700330033000500340034000b00380038000c003900390008003a003a000d003b003b000e003c003c000f
003d003d0010003e003e0011004500450012004600460013004700470014004900490015004c004c0016005100520016005300530017005400540013005600560018005a005a0019005c005c001a005d
005d0019005e005e001b008a008a001300b000b0001c00b100b1000200b200b2000500b300b3000200bc00bc001900c000c0001600c600c6001300d300d4001d00d600d6000700d900d9000f00db00dc
000700dd00dd001e00df00df000700e100e1000700e300e3001d00e500e5001d00eb00eb001f00ed00ed001a00f600f6001300fb00fb002000fd00fd002000fe00fe0013010301040020010901090020
010c010c000201170117001701180118000d011901190019012b012b0013012c012c001c012d012d001f01300130000901320132000901490149001f016e016e00010172017200010176017700010228
02280004022a022b000502460247000502490249000c024b025100020252025200040253025600060257025b0007025c026000050261026400080265026500100266026c0012026d026d0014026e0271
00150276027600160277027b0017028002810019028202820002028302830012028402840002028502850012028602860002028702870012028802880004028902890014028a028a0004028b028b0014
028c028c0004028d028d0014028e028e0004028f028f0014029002900005029202920006029302930015029402940006029502950015029602960006029702970015029802980006029902990015029a
029a0006029b029b001502a402a4000702a502a5001602a602a6000702a802a8000702aa02aa000702ac02ac000702ae02ae000702b102b1000802b302b3000902b502b5000a02b702b7000a02b902b9
000a02bb02bb000a02bd02bd000702be02be001602bf02bf000702c002c0001602c102c1000702c202c3001602c402c4000502c502c5001702c602c6000502c702c7001702c802c8000502c902c90017
02cb02cb001802cd02cd001802cf02cf001802da02da000c02dc02dc000c02de02de000c02e002e0000802e202e2000802e402e4000802e602e6000802e802e8000802ea02ea000802ec02ec000e02ee
02ee001002ef02ef001902f002f0001002f102f1001102f202f2001b02f302f3001102f402f4001b02f502f5001102f602f6001b03520352000203530353000603540355000703560356000503570357
0010035a035a0002035b035b0003035c035c0006035d035d0011035e035f000703600360000903610362000703630363000503640364000b03650365000c03660366001003670367000f036803680007
036903690010036c036c0016037003700017037203720019037603760017037903790006037a037a001c037c037d0007037e037e0008037f0380000903810381001e0382038200020383038300030384
0384001c038503850006038703880007038903890005038b038b000b038c038c0004038d038d000c038e038e000f038f038f001203900390001503920392001703930393001303940394001403950395
001903960396001a03970397001503980398001f039e039e0019039f039f000e03a103a1000e03a303a3000e03a503a5001003a603a6001903ac03ac000703ad03ad001603ae03ae000203af03af0012
03b003b0000603b203b2001503b603b6000d03b703b7001903bc03bc000403bd03bd001403be03be001003bf03bf001903c003c0000703c103c1000f03c203c2001a03c303c3000703c403c4000203c5
03c5001203c603c6000203c703c7001203ca03ca000603cb03cb001503cd03ce001503cf03cf000f03d003d0001a03d703d7000503d803d8001703dc03dc001703de03de001303df03df001e03e003e0
001903e103e1001e03e203e2001903e303e3001e03e403e4001903e903e9000f03ea03ea001a03ee03ee000203ef03ef001203f003f0000203f103f1001203f203f2000203f303f3001203f403f40002
03f503f5001203f603f6000203f703f7001203f803f8000203f903f9001203fa03fa000203fb03fb001203fc03fc000203fd03fd001203fe03fe000203ff03ff00120400040000020401040100120402
04020002040304030012040404040002040504050012040604060006040704070015040804080006040904090015040a040a0006040b040b0015040c040c0006040d040d0015040e040e0006040f040f
0015041004100006041104110015041204120006041304130015041404140006041504150015041604160007041804180007041a041a0005041b041b0017041c041c0005041d041d0017041e041e0005
041f041f0017042004200005042104210017042204220005042304230017042404240005042504250017042604260005042704270017042f042f00170432043200080434043400080440044000100441
04410019044204420010044304430019044404440010044504450019044804480009044a044a0007044c044c000c044e044e000f044f044f001a04540454001c04550455001f04560456000f04570457
001a045a045a0016045c045c001d045d045d001c045f045f000904630463000704650465000704690469001004a904a900050002016d000600060001000b000b00010010001000160011001100190012
00120016002500250002002700270008002b002b0008002e002e001a00330033000800350035000800370037001b00380038000900390039000a003a003a000b003b003b000c003c003c0017003d003d
000d003e003e0018004500450003004700490004004b004b000400510052000500530053000600540054000500550055000400570057000700590059000e005a005a000f005c005c001c005d005d000f
005e005e0010008300830008009200920008009300930004009700970008009800980004009a009a000e00b100b1000200b200b2000800b300b3000200ba00ba000400bc00bc000f00c000c0000500c7
00c8000400ca00ca000e00d100d1000900d200d2000800d300d3001100d500d5000900d900d9001700dc00dc001100dd00dd001500e000e0001200eb00eb000500ed00ed001c00ef00f0000500f100f1
001300f200f4000500f600f6000400f700f7000500f800f8001400f900fa000500fd00fd000500ff00ff0005010201020004010301030013010401040005010701070008010c010c0002011601160008
01170117000601180118000b01190119000f011a011a0008011c011c0008011d011d0004011e011e0008012001200008012101210004013201320009013501350005013801380008013901390004013a
013a0009014401440005014901490005014b014c00040151015100110155015500080156015600040169016a0019016c016d0001016e016e0016016f0171000101720172001601760177001602280229
0008022b022c0008024502450019024b02510002025202520008025c0260000802610264000a02650265000d0266026c0003026d027100040276027600050277027b0006027c027f000e02800281000f
028202820002028302830003028402840002028502850003028602860002028702870003028802880008028902890004028a028a0008028b028b0004028c028c0008028d028d0004028e028e0008028f
028f0004029102910004029302930004029502950004029702970004029902990004029b029b0004029c029c0008029d029d0004029e029e0008029f029f000402a002a0000802a102a1000402a202a2
000802a302a3000402b102b1001a02be02be000502c002c0000502c202c3000502c402c4000802c502c5000602c602c6000802c702c7000602c802c8000802c902c9000602d002d0001b02d102d10007
02d202d2001b02d302d3000702d402d4001b02d502d5000702d602d6001b02d702d7000702d802d8001b02d902d9000702da02da000902dc02dc000902de02de000902e002e0000a02e102e1000e02e2
02e2000a02e302e3000e02e402e4000a02e502e5000e02e602e6000a02e702e7000e02e802e8000a02e902e9000e02ea02ea000a02eb02eb000e02ec02ec000c02ee02ee000d02ef02ef000f02f002f0
000d02f102f1001802f202f2001002f302f3001802f402f4001002f502f5001802f602f6001002f902f9000803520352000203560356000803570357000d035a035a0002035d035d0018036303630008
03660366000d03670367001703690369000d036a036a0004036c036c0005036e036e000e03700370000603720372000f03750375000e03760376000603770377000e037e037e001a0381038100150382
03820002038903890008038c038c0008038d038d0009038e038e0017038f038f000303900390000403910391000503920392000603930393000503940394000403950395000f03960396001c03970397
0004039803980005039903990007039d039d0005039e039e000f039f039f000c03a103a1000c03a303a3000c03a503a5000d03a603a6000f03a703a8000103ab03ab000103ad03ad000503ae03ae0002
03af03af000303b203b2000403b303b3000503b603b6000b03b703b7000f03b803b8000403b903b9000803bc03bc000803bd03bd000403be03be000d03bf03bf000f03c103c1001703c203c2001c03c4
03c4000203c503c5000303c603c6000203c703c7000303cb03cb000403cd03ce000403cf03cf001703d003d0001c03d403d4000503d603d6000503d703d7000803d803d8000603d903d9000803da03da
000403db03db000803dc03dc000603df03df001503e003e0000f03e103e1001503e203e2000f03e303e3001503e403e4000f03e503e5001203e603e6001403e803e8000503e903e9001703ea03ea001c
03eb03eb000403ec03ec001103ed03ed001303ee03ee000203ef03ef000303f003f0000203f103f1000303f203f2000203f303f3000303f403f4000203f503f5000303f603f6000203f703f7000303f8
03f8000203f903f9000303fa03fa000203fb03fb000303fc03fc000203fd03fd000303fe03fe000203ff03ff000304000400000204010401000304020402000204030403000304040404000204050405
0003040704070004040904090004040b040b0004040d040d0004040f040f0004041104110004041304130004041504150004041a041a0008041b041b0006041c041c0008041d041d0006041e041e0008
041f041f0006042004200008042104210006042204220008042304230006042404240008042504250006042604260008042704270006042804280008042904290004042a042a0008042b042b0004042c
042c0008042d042d0004042e042e0008042f042f000604300430000804310431000404320432000a04330433000e04340434000a04350435000e04370437000e04390439000e043b043b000e043d043d
000e043f043f000e04400440000d04410441000f04420442000d04430443000f04440444000d04450445000f044904490005044b044b0005044c044c0009044e044e0017044f044f001c045004500012
04510451001404520452001204530453001404550455000504560456001704570457001c04620462000504640464000504660466000504670467001104680468001304690469000d046f046f001904a9
04a9000800010000000a02060810000444464c54001a6379726c00486772656b00766c61746e00a4000400000000ffff00120000000a0014001e002800340041004b0055005f00690073007d00870091
009b00a500af000400000000ffff00120001000b0015001f002900350042004c00560060006a0074007e00880092009c00a600b0000400000000ffff00120002000c00160020002a00360043004d0057
0061006b0075007f00890093009d00a700b100280006415a4520005443525420007e4d4f4c2000a84e41562000d4524f4d20010054555220012c0000ffff00130003000d00170021002b003200370044
004e00580062006c00760080008a0094009e00a800b20000ffff00120004000e00180022002c00380045004f00590063006d00770081008b0095009f00a900b30000ffff00120005000f00190023002d
003900460050005a0064006e00780082008c009600a000aa00b40000ffff001300060010001a0024002e003a003e00470051005b0065006f00790083008d009700a100ab00b50000ffff001300070011
001b0025002f003b003f00480052005c00660070007a0084008e009800a200ac00b60000ffff001300080012001c00260030003c004000490053005d00670071007b0085008f009900a300ad00b70000
ffff001300090013001d002700310033003d004a0054005e00680072007c00860090009a00a400ae00b800b963327363045863327363045e63327363046463327363046a63327363046a63327363046a
63327363046a63327363046a63327363046a63327363046a63636d70047063636d70047063636d70047063636d70047063636d70047063636d70047063636d70047063636d70047063636d7004706363
6d700470646c69670478646c6967047e646c69670484646c6967048a646c6967048a646c6967048a646c6967048a646c6967048a646c6967048a646c6967048a646e6f6d0490646e6f6d0496646e6f6d
049c646e6f6d04a2646e6f6d04a2646e6f6d04a2646e6f6d04a2646e6f6d04a2646e6f6d04a2646e6f6d04a26672616304a86672616304a86672616304a86672616304a86672616304a86672616304a8
6672616304a86672616304a86672616304a86672616304a86c69676104b26c69676104ba6c6e756d04c06c6e756d04c66c6e756d04cc6c6e756d04d26c6e756d04d26c6e756d04d26c6e756d04d26c6e
756d04d26c6e756d04d26c6e756d04d26c6f636c04d86c6f636c04de6c6f636c04e46e756d7204ea6e756d7204f06e756d7204f66e756d7204fc6e756d7204fc6e756d7204fc6e756d7204fc6e756d72
04fc6e756d7204fc6e756d7204fc6f6e756d05026f6e756d05086f6e756d050e6f6e756d05146f6e756d05146f6e756d05146f6e756d05146f6e756d05146f6e756d05146f6e756d0514706e756d051a
706e756d0520706e756d0526706e756d052c706e756d052c706e756d052c706e756d052c706e756d052c706e756d052c706e756d052c736d63700532736d63700538736d6370053e736d63700544736d
63700544736d63700544736d63700544736d63700544736d63700544736d6370054473733031054a73733031055073733031055673733031055c73733031055c73733031055c73733031055c73733031
055c73733031055c73733031055c73733032056273733032056873733032056e73733032057473733032057473733032057473733032057473733032057473733032057473733032057473733033057a
73733033058073733033058673733033058c73733033058c73733033058c73733033058c73733033058c73733033058c73733033058c73733034059273733034059873733034059e7373303405a47373
303405a47373303405a47373303405a47373303405a47373303405a47373303405a47373303505aa7373303505b07373303505b67373303505bc7373303505bc7373303505bc7373303505bc73733035
05bc7373303505bc7373303505bc7373303605c27373303605c87373303605ce7373303605d47373303605d47373303605d47373303605d47373303605d47373303605d47373303605d47373303705da
7373303705e07373303705e67373303705ec7373303705ec7373303705ec7373303705ec7373303705ec7373303705ec7373303705ec746e756d05f2746e756d05f8746e756d05fe746e756d0604746e
756d0604746e756d0604746e756d0604746e756d0604746e756d0604746e756d0604000000010001000000010003000000010002000000010000000000020008000900000001000e0000000100100000
0001000f00000001000d00000001004300000001004500000001004400000001004200000003003f00400041000000020011001200000001001200000001003c00000001003e00000001003d00000001
003b00000001000a00000001000c00000001000b00000001004700000001004900000001004800000001004600000001003000000001003200000001003100000001002f00000001003800000001003a
00000001003900000001003700000001000500000001000700000001000600000001000400000001001400000001001600000001001500000001001300000001001800000001001a0000000100190000
0001001700000001001c00000001001e00000001001d00000001001b00000001002000000001002200000001002100000001001f00000001002400000001002600000001002500000001002300000001
002800000001002a00000001002900000001002700000001002c00000001002e00000001002d00000001002b000000010034000000010036000000010035000000010033004b00980098009800980426
042604260426071407c00e500e500e660e880e880e880e880ebe0ee40f120f120f120f120f260f260f260f260f3a0f3a0f3a0f3a0f4e0f4e0f4e0f4e0f600f600f600f600f7a0f7a0f7a0f7a0fbc0fbc
0fbc0fbc0fda0fda0fda0fda0ff80ff80ff80ff8102a102a102a102a105c105c105c105c108e10a210da10cc10cc10cc10cc10da10da10da10da11060001000000010008000201c400df01e701ba01bb
01bc01bd01be01bf01c001c101c201c301c401c501c601c701c801c901ca01cb01cc01cd01ce01cf01d001d101d201d301e801e90243023b01ea01eb01ec01ed01ee01ef01f001f101f201f301f401f5
01f601f701f801f901fa01fb01fc01fd01fe0200020104dc02020203020402050206020702080209020a020b022f020f02100211021402150216021702180219021b021c021e021d02fb02fc02fd02fe
02ff0300030103020303030403050306030703080309030a030b030c030d030e030f0310031103120313031403150316031703180319031a031b031c031d031e031f0320032103220323032403250326
032703280329032a032b032c032d032e032f0330033103320333033403350336033703380339033a033b033c033d033e033f0340034103420343034503440346034703480349034a034b034c034d034e
034f0350035104aa04ab04ac04ad04ae04af04b004b104b204b304b404b504b604b704b804b904ba04bb04bc04bd04be04bf04c004c104c204c304c404c501ff04c604c704c804c904ca04cb04cc04cd
04ce04cf04d004d104d204d304d404d504d704d804da021a04db020e04d60213020d04d9020c0212000100df000800250026002700280029002a002b002c002d002e002f003000310032003300340035
0036003700380039003a003b003c003d003e006500670085009200b000b100b200b300b400b500b600b700b800b900d100d200d300d400d500d600d700d800d900da00db00dc00dd00de00df00e000e1
00e200e300e400e500e600e700e8012c013001320138013a013c013e013f01450146017f0185018a018d024602470249024b024c024d024e024f0250025102520253025402550256025702580259025a
025b025c025d025e025f0260026102620263026402650282028402860288028a028c028e02900292029402960298029a029c029e02a002a202a402a602a802aa02ac02ae02b102b302b502b702b902bb
02bd02bf02c102c402c602c802ca02cc02ce02d002d202d402d802da02dc02de02e002e202e402e602e802ea02ec02ee02f002f102f302f50352035303540355035603570358035a035b035c035d035e
035f0360036103630364036503660367036803690379037a037b037c037d037e037f0380038103820383038403850386038703880389038a038b038c038d038e03ba03bc03be03d303d903df0448044a
044e04560458045d046900010000000100080002017400b701ba01bb01bc01bd01be01bf01c001c101c201c301c401c501c601c701c801c901ca01cb01cc01cd01ce01cf01d001d101d201d302fc032f
023b01fa04c904ca01fb01fc01fd01fe01ff020004cd04ce04d004d304dc02020203020402050206020702080209020a020b01f401f501f601f701f801f9022f020f02100211021402150217021902fd
02fe02ff0300030103020303030403050306030703080309030a030b030c030d030e030f031003110312031303140315031603170318034e0319031a031b031c031d031e031f03200321032203230324
03250326032703280329032a032b032c032d032e0330033103320333033403350336033703380339033a033b033c033d033e033f0340034103420343034503440346034703480349034a034b034c034d
034f0350035104c804cb04cc04cf04d104d2020104d404c004c104c204c304c404c504c604c704d504d704d8021804da021a04db02fb020e04d60213020d04d90216020c0212000100b7004500460047
00480049004a004b004c004d004e004f0050005100520053005400550056005700580059005a005b005c005d005e0087008c009300e900ea00eb00ec00ed00ee00ef00f000f100f200f300f400f500f6
00f700f800f900fa00fb00fc00fd00fe00ff0100010101020103010401050106012d013101330139013b013d01400147024a0266026702680269026a026b026c026d026e026f02700271027202730274
02750276027702780279027a027b027c027d027e027f028002810283028502870289028b028d028f02910293029502970299029b029d029f02a102a302a502a702a902ab02ad02b202b402b602b802ba
02bc02be02c002c202c502c702c902cb02cd02cf02d102d302d502d902db02dd02df02e102e302e502e702e902eb02ed02ef02f202f402f6038f0390039103920393039403950396039703980399039a
039b039c039d039e03bb03bd03bf03cd03d403da03e004460449044b044f04570459045a045e046a0006000000060012002a0042005a0072008a00030000000100120001009000010000004a00010001
004d00030000000100120001007800010000004a00010001004e00030000000100120001006000010000004a0001000102ad00030000000100120001004800010000004a00010001039a000300000001
00120001003000010000004a00010001039c00030000000100120001001800010000004a0001000104190002000100a700ab000000040000000100080001061e0036007200a400ae00b800ca00fc010e
0118014a0164017e019001ba01ec01f60218023202440276028802a202cc02de0310031a0324033603680372037c038603a003ba03cc03f6042804320454046e048004b204c404de0508051a0524052e
05380542056c059605c005ea06140006000e0014001a00200026002c024b000200a7024c000200a8024e000200a903f0000200aa047a000200ab03ee000200ac000100040487000200ac000100040288
000200a800020006000c0489000200ac048b000201a20006000e0014001a00200026002c0253000200a70254000200a8040a000200a90408000200aa047c000200ab0406000200ac00020006000c0476
000200a802a2000201a200010004048d000200ac0006000e0014001a00200026002c0257000200a70258000200a802a6000200a90416000200aa047e000200ab0418000200ac00030008000e0014048f
000200a80491000200ac02b3000201a200030008000e001402b5000200a80493000200ac02b7000201a200020006000c03ac000200a80495000200ac0005000c00120018001e00240478000200a702bd
000200a8025b000200a90497000200ac02bf000201a20006000e0014001a00200026002c025c000200a7025d000200a8025f000200a9041c000200aa0480000200ab041a000200ac0001000404990002
00a80004000a00100016001c02ca000200a80482000200ab049b000200ac02cc000201a200030008000e001402d0000200a8049d000200ac02d6000201a200020006000c049f000200ac02da000201a2
0006000e0014001a00200026002c0261000200a70262000200a802e0000200a90434000200aa0484000200ab0432000200ac00020006000c04a1000200a904a3000200ac00030008000e0014039f0002
00a703a1000200a804a5000200ac0005000c00120018001e002403a5000200a70265000200a80444000200a90442000200aa0440000200ac00020006000c02f1000200a804a7000200ac0006000e0014
001a00200026002c0266000200a70267000200a80269000200a903f1000200aa047b000200ab03ef000200ac000100040488000200ac000100040289000200a800020006000c048a000200ac048c0002
01a20006000e0014001a00200026002c026e000200a7026f000200a8040b000200a90409000200aa047d000200ab0407000200ac000100040477000200a800010004048e000200ac0001000404190002
00ac00030008000e00140490000200a80492000200ac02b4000201a200030008000e001402b6000200a80494000200ac02b8000201a200020006000c03ad000200a80496000200ac0005000c00120018
001e00240479000200a702be000200a80276000200a90498000200ac02c0000201a20006000e0014001a00200026002c0277000200a70278000200a8027a000200a9041d000200aa0481000200ab041b
000200ac00010004049a000200a80004000a00100016001c02cb000200a80483000200ab049c000200ac02cd000201a200030008000e001402d1000200a8049e000200ac02d7000201a200020006000c
04a0000200ac02db000201a20006000e0014001a00200026002c027c000200a7027d000200a802e1000200a90435000200aa0485000200ab0433000200ac00020006000c04a2000200a904a4000200ac
00030008000e001403a0000200a703a2000200a804a6000200ac0005000c00120018001e002403a6000200a70280000200a80445000200a90443000200aa0441000200ac00020006000c02f2000200a8
04a8000200ac0001000402f7000200a80001000402f9000200a80001000402f8000200a80001000402fa000200a80005000c00120018001e00240272000200a70273000200a802a7000200a904170002
00aa047f000200ab0005000c00120018001e0024042a000200a70428000200a8042e000200a9042c000200aa0430000200ac0005000c00120018001e0024042b000200a70429000200a8042f000200a9
042d000200aa0431000200ac0005000c00120018001e00240438000200a70436000200a8043c000200a9043a000200aa043e000200ac0005000c00120018001e00240439000200a70437000200a8043d
000200a9043b000200aa043f000200ac000100040486000200a800020011002500290000002b002d0005002f003400080036003b000e003d003e0014004500490016004b004d001b004f0054001e0056
005b0024005d005e002a00810081002c00830083002d00860086002e00890089002f008c008c00300097009a003100cf00cf003500010000000100080001000600020001000202d402d5000100000001
00080002000e000404dd04de04df04e00001000402860287029802990004000000010008000100260002000a001c00020006000c01a30002004a01a8000200580001000401a90002005800010002004a
00570004000000010008000100440002000a00140001000401a40002004d0001000401a60002004d00040000000100080001001e0002000a00140001000401a5000200500001000401a7000200500001
0002004a01a3000100000001000800010006019500010001004b00010000000100080001000601270001000100ba00010000000100080001000601ac00010001003600010000000100080002001c0002
01e301e400010000000100080002000a000201e501e600010002002f004f00010000000100080002001e000c0228022a0229022b022c021f0220022101ae0223022402250001000c00270028002b0033
0035004600470048004b00530054005500010000000100080002000c0003022602270227000100030049004b01ae0001000000010008000200660008023d022d022e023002310239023a023c00010000
00010008000200160008001b00150016001700180019001d00140001000801ad0222047004710472047304740475000100000001000800020016000804750222047004710472047301ad047400010008
001400150016001700180019001b001d000100000001000800020016000800150016001700180019001b001d001400010008022d022e023002310239023a023c023d0001000000010008000100060169
000100010013000600000001000800030001001200010052000000010000004a00020002017c017c000001d401dd000100010000000100080001002801c000010000000100080002001a000a0232007a
00730074023302340235023602370238000200010014001d0000000100000001000800020026001001d401d501d601d701d801d901da01db01dc01dd0240023e02410242023f04e10001001000140015
0016001700180019001a001b001c001d004d004e02ad039a039c0419
]],
}
end
__FILES['imgui_internal.lua'] = function()
-- dear imgui, v1.93.0 WIP
-- (internal structures/api)
-- You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
-- Index of this file:
--
-- [SECTION] Context pointer
-- [SECTION] Macros
-- [SECTION] Generic helpers
-- [SECTION] ImDrawList support
-- [SECTION] Style support
-- [SECTION] Data types support
-- [SECTION] Widgets support: flags, enums, data structures
-- [SECTION] Popup support
-- [SECTION] Inputs support
-- [SECTION] Navigation support
-- [SECTION] Viewport support
-- [SECTION] Settings support
-- [SECTION] Localization support
-- [SECTION] Metrics, Debug tools
-- [SECTION] ImGuiContext (main imgui context)
-- [SECTION] ImGuiWindowTempData, ImGuiWindow
-- [SECTION] Tab bar, Tab item support
-- [SECTION] ImGui internal API
-- [SECTION] ImFontLoader
-- [SECTION] ImFontAtlas internal API
--
-- PORT: the order of declarations in this file does not strictly follow upstream's imgui_internal.h.
--- @meta
local type = type -- [Luau] builtin (FASTCALL); ImGui._GetTypeFunc() kept for compatibility
local rawget = rawget
local mathMin = math.min; local mathMax = math.max
local mathFloor = math.floor; local mathCeil = math.ceil
local bitNot = bit32.bnot
local bitAnd = bit32.band; local bitOr = bit32.bor
local bitLShift = bit32.lshift; local bitRShift = bit32.rshift
local t_sort = table.sort
--- @alias ImGuiTableColumnIdx ImS16
-- TODO: Implement actual ImString type
--- @alias ImString char[]|string
--- @alias ImStringBuffer char[]
-- NOTE: temporary solution
--- @param buf char[]
--- @param buf_begin int
--- @param buf_end int # exclusive
function ImGui._ByteArrayToString(buf, buf_begin, buf_end)
local str = {}
for i = buf_begin, buf_end - 1 do
table.insert(str, string.char(buf[i]))
end
return table.concat(str)
end
-- Helper: ImPool<>
-- Basic keyed storage for contiguous instances, slow/amortized insertion, O(1) indexable, O(Log N) queries by ID over a dense/hot buffer,
-- Indexes have the same lifetime as the associated object.
--- ImPool<T>: pool of objects keyed by ID. Public indices are 0-based like upstream (Buf is stored 1-based).
--- `ctor` creates a new element. Removed slots are recycled via FreeIdx (-1 = none; free slots chain through `_NextFree`).
local IM_POOL = {}
IM_POOL.__index = IM_POOL
function ImPool(ctor) return setmetatable({ Buf = {}, Map = {}, FreeIdx = 0, AliveCount = 0, BufSize = 0, _Ctor = ctor }, IM_POOL) end
function IM_POOL:GetByKey(key) local i = self.Map[key]; return i and self.Buf[i + 1] or nil end
function IM_POOL:GetByIndex(n) return self.Buf[n + 1] end
function IM_POOL:GetIndex(p) return p._PoolIdx end
function IM_POOL:Contains(p) return p ~= nil and self.Buf[(p._PoolIdx or -1) + 1] == p end
function IM_POOL:GetMapSize() return self.BufSize end
function IM_POOL:Clear() self.Buf = {}; self.Map = {}; self.FreeIdx = 0; self.AliveCount = 0; self.BufSize = 0 end
function IM_POOL:Add()
local idx
if self._Free and #self._Free > 0 then idx = table.remove(self._Free) else idx = self.BufSize; self.BufSize = self.BufSize + 1 end
local p = self._Ctor and self._Ctor() or {}
p._PoolIdx = idx
self.Buf[idx + 1] = p
self.AliveCount = self.AliveCount + 1
return p
end
function IM_POOL:GetOrAddByKey(key)
local i = self.Map[key]
if i then return self.Buf[i + 1] end
local p = self:Add(); self.Map[key] = p._PoolIdx; return p
end
function IM_POOL:Remove(key, p)
if type(key) == "table" then p = key; key = nil end
if key == nil then for k, i in pairs(self.Map) do if i == p._PoolIdx then key = k break end end end
if key ~= nil then self.Map[key] = nil end
self.Buf[p._PoolIdx + 1] = false
self._Free = self._Free or {}
table.insert(self._Free, p._PoolIdx)
self.AliveCount = self.AliveCount - 1
end
function IM_POOL:Reserve(n) end
-- NOTE: removed slots hold `false`; iterate with `for i = 0, pool:GetMapSize() - 1 do local t = pool:GetByIndex(i) if t then ... end end`
-------------------------------------------------------------------------------
-- [SECTION] Context pointer
-- See implementation of this variable in imgui.cpp for comments and details.
-------------------------------------------------------------------------------
--- @type ImGuiContext?
local GImGui -- Current implicit context pointer
-- Sets local `GImGui` in this file(imgui_internal.lua).
-- This is currently only used in main code `ImGui.SetCurrentContext()`
--- @param ctx ImGuiContext?
function ImGui._SetCurrentContext_Internal(ctx)
GImGui = ctx
end
local setmetatable = setmetatable
local MT = ImGui.GetMetatables()
-------------------------------------------------------------------------------
-- [SECTION] Macros
-------------------------------------------------------------------------------
IM_TABSIZE = 4
FLT_MIN = 1.1754943508223e-38
FLT_MAX = 3.4028234663853e+38
DBL_MAX = 1.7976931348623e+308
INT_MIN = -0x7fffffff - 1
INT_MAX = 0x7fffffff
UINT_MAX = 0x7fffffff * 2 + 1
NAV_WINDOWING_HIGHLIGHT_DELAY = 0.5 -- Time before the highlight and screen dimming starts fading in
NAV_ACTIVATE_HIGHLIGHT_TIMER = 0.10 -- Time to highlight an item activated by a shortcut.
NAV_ACTIVATE_INPUT_WITH_GAMEPAD_DELAY = 0.60 -- Time to hold activation button (e.g. FaceDown) to turn the activation into a text input.
IM_PI = math.pi
-------------------------------------------------------------------------------
-- [SECTION] Generic helpers
-- Note that the ImXXX helpers functions are lower-level than ImGui functions.
-- ImGui functions or the ImGui context are never called/used from other ImXXX functions.
-------------------------------------------------------------------------------
-- Helpers: Maths
-- - Wrapper for standard libs functions. (Note that imgui_demo.cpp does _not_ use them to keep the code easy to copy)
ImPow = math.pow -- DragBehaviorT/SliderBehaviorT uses ImPow with either float/double and need the precision
ImLog = math.log -- DragBehaviorT/SliderBehaviorT uses ImLog with either float/double and need the precision
ImAbs = math.abs
ImFabs = math.abs
ImFmod = function(a, b) if b < 0 then b = -b end; if a < 0 then return -(-a % b) else return a % b end; end
ImRound64 = function(val) return math.floor(val + 0.5) end -- FIXME: Positive values only
ImCeil = math.ceil
ImSin = math.sin
ImCos = math.cos
ImAcos = math.acos
ImAtan2 = math.atan2
ImSqrt = math.sqrt
--- @generic T: number
--- @param a T
--- @param b T
--- @param mn T
--- @param mx T
--- @return T
function ImAddClampOverflow(a, b, mn, mx)
if b < 0 and (a < mn - b) then return mn end
if b > 0 and (a > mx - b) then return mx end
return a + b
end
--- @generic T: number
--- @param a T
--- @param b T
--- @param mn T
--- @param mx T
--- @return T
function ImSubClampOverflow(a, b, mn, mx)
if b > 0 and (a < mn + b) then return mn end
if b < 0 and (a > mx + b) then return mx end
return a - b
end
-- - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support variety of types: signed/unsigned int/long long float/double
--- @overload fun(lhs: number, rhs: number): number
--- @overload fun(lhs: ImVec2, rhs: ImVec2): ImVec2
--- @overload fun(lhs: number, rhs: number): number
--- @overload fun(lhs: ImVec2, rhs: ImVec2): ImVec2
-- Helpers: Sorting
--- @param base table
--- @param count int
--- @param cmp_func fun(lhs, rhs): bool
ImStd.ImQsort = function(base, count, cmp_func) if count > 0 then t_sort(base, cmp_func) end end
--- @overload fun(v: number, mn: number, mx: number): number
--- @overload fun(v: ImVec2, mn: ImVec2, mx: ImVec2): ImVec2
--- @overload fun(a: number, b: number, t: number): number
--- @overload fun(a: ImVec2, b: ImVec2, t: number): ImVec2
--- @overload fun(a: ImVec2, b: ImVec2, t: ImVec2): ImVec2
--- @overload fun(v: float): float
--- @overload fun(v: ImVec2): ImVec2
ImTrunc64 = _Trunc
-- Consider using the bit-hack version (search for "0x1p120f").
--- @param f float
function ImCeilFast(f)
local i
if f >= 0 then i = math.floor(f) else i = math.ceil(f) end -- same as `ImTrunc()`
if f > i then return i + 1 end
return i
end
function IM_ROUNDUP_TO_EVEN(n) return (ImCeil((n) / 2) * 2) end
function ImRsqrt(x) return (1 / ImSqrt(x)) end
-- Converge to more precise solution using single step of Newton-Raphson method, repeating increases precision
function ImRsqrtPrecise(x) return (1 / ImSqrt(x)) end -- [Luau] non-SSE upstream variant
function ImIsTruncated(x) return x % 1 == 0 end -- (float)(int)x == x
-- Returns true if all parameters are whole numbers.
function ImIsTruncated4(x, y, z, w) return x % 1 == 0 and y % 1 == 0 and z % 1 == 0 and w % 1 == 0 end
function IM_TRUNC(VAL) return math.floor(VAL) end -- Positive values only!
function IM_ROUND(VAL) return math.floor(VAL + 0.5) end -- Positive values only!
-- Decent replacement for floorf() -- [Luau] upstream: (int)f truncates, -1 for negatives == math.floor
-- Helpers: Bit manipulation
--- @param v int
function ImIsPowerOfTwo(v)
return (v ~= 0) and (bitAnd(v, (v - 1)) == 0)
end
function ImUpperPowerOfTwo(v)
if v <= 0 then return 0 end
if v <= 1 then return 1 end
v = v - 1
v = bitOr(v, bitRShift(v, 1))
v = bitOr(v, bitRShift(v, 2))
v = bitOr(v, bitRShift(v, 4))
v = bitOr(v, bitRShift(v, 8))
v = bitOr(v, bitRShift(v, 16))
return v + 1
end
--- @param v unsigned_int
--- @return unsigned_int
function ImCountSetBits(v)
local count = 0
while v > 0 do
v = bitAnd(v, v - 1)
count = count + 1
end
return count
end
--- @param avg float
--- @param sample float
--- @param n int
--- @return float
function ImStd.ImExponentialMovingAverage(avg, sample, n)
avg = avg - avg / n
avg = avg + sample / n
return avg
end
-- Uses `string.match` internally
--- @param str string
function ImAtof(str)
return tonumber(string.match(str, "[+-]?%d*%.?%d+"))
end
--- @param s table # 1-based
--- @param c any
--- @param n int
function ImStd.memset(s, c, n)
for i = 1, n do s[i] = c end
end
do
local _memmove = function(dest, dest_start, src, src_start, count)
if count <= 0 then return end
-- more compact instead of writing separate if-else and for loops
local c = (dest == src and dest_start >= src_start)
for i = (c and count - 1 or 0), (c and 0 or count - 1), (c and -1 or 1) do
dest[dest_start + i] = src[src_start + i]
end
end
if table.move then
--- [GMod] Platform specific: the `table.move` polyfill in non-x86-64 currently uses `unpack` which will break if too many elements
-- Remove this check when the polyfill is fixed or removed
if not gmod or (gmod and BRANCH == "x86-64") then
_memmove = function(dest, dest_start, src, src_start, count)
table.move(src, src_start, src_start + count - 1, dest_start, dest)
end
end
end
ImStd.memmove = _memmove
end
--- @param str1 ImStringBuffer
--- @param str2 ImStringBuffer
function ImStd.strcmp(str1, str2)
local i = 1
while true do
local c1 = str1[i] or 0
local c2 = str2[i] or 0
if c1 ~= c2 then
return c1 - c2
end
if c1 == 0 then
return 0
end
i = i + 1
end
end
--- @param str1 ImStringBuffer
--- @param str2 ImStringBuffer
--- @param max_count int
function ImStd.strncmp(str1, str2, max_count)
for i = 1, max_count do
local c1 = str1[i] or 0
local c2 = str2[i] or 0
if c1 ~= c2 then
return c1 - c2
end
if c1 == 0 then
return 0
end
end
return 0
end
--- @param x float
--- @return float # Sign operator - returns -1, 0 or 1 based on sign of argument
function ImSign(x) if x < 0.0 then return -1.0 elseif x > 0.0 then return 1.0 end return 0.0 end
function ImLinearRemapClamp(s0, s1, d0, d1, x) return ImSaturate((x - s0) / (s1 - s0)) * (d1 - d0) + d0 end
--- @param c int # byte
--- @return int
function ImToUpper(c) if c >= 97 and c <= 122 then return bit32.band(c, bit32.bnot(32)) end return c end
IM_F32_TO_INT8_UNBOUND = function(val) return math.floor(val * 255.0 + (val >= 0 and 0.5 or -0.5)) end -- Unsaturated, for display purpose
IM_F32_TO_INT8_SAT = function(val) return math.floor(ImSaturate(val) * 255.0 + 0.5) end -- Saturated, always output 0..255
--- @param s ImString
--- @param i int
--- @return char
function ImStrByte(s, i)
if type(s) == "string" then
return string.byte(s, i, i)
else --- @cast s char[]
return s[i]
end
end
-- Stacked color modifier, backup of modified data so we can restore it
--- @class ImGuiColorMod
--- @field Col ImGuiCol
--- @field BackupValue ImVec4
--- @param col ImGuiCol
--- @param backup_value ImVec4
--- @return ImGuiColorMod
function ImGuiColorMod(col, backup_value)
local this = {
Col = col,
BackupValue = ImVec4()
}
ImVec4_Copy(this.BackupValue, backup_value)
return this
end
--- @param lhs ImVec2
--- @return float
function ImLengthSqr(lhs) return (lhs.x * lhs.x) + (lhs.y * lhs.y) end
--- @param v ImVec2
--- @param cos_a float
--- @param sin_a float
--- @return ImVec2
--- @nodiscard
function ImRotate(v, cos_a, sin_a)
return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a)
end
--- @param a ImVec2
--- @param b ImVec2
function ImDot(a, b)
return a.x * b.x + a.y * b.y
end
--- @param lhs ImVec2
--- @param rhs ImVec2
--- @nodiscard
function ImMul(lhs, rhs)
return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y)
end
--- @param a ImVec2
--- @param b ImVec2
--- @param c ImVec2
--- @return float
function ImTriangleArea(a, b, c) return ImFabs((a.x * (b.y - c.y)) + (b.x * (c.y - a.y)) + (c.x * (a.y - b.y))) * 0.5 end
--- @param a ImVec2
--- @param b ImVec2
--- @param c ImVec2
--- @return boolean
function ImTriangleIsClockwise(a, b, c) return ((b.x - a.x) * (c.y - b.y)) - ((c.x - b.x) * (b.y - a.y)) > 0.0 end
--- @param str char[]
--- @param begin? int
function ImStd.ImStrlen(str, begin)
if begin == nil then begin = 1 end
local l = #str
for i = begin, l do
if str[i] == 0 then
return i - begin
end
end
assert(false)
end
-- FIXME: should also accept count?
-- FIXME: avoid dynamic type checking?
-- string.find with patterns disabled
--- @param str string|char[]
--- @param s string|char
--- @param start_pos? int
--- @return int?
function ImMemchr(str, s, start_pos)
local start = start_pos or 1
if start < 1 then start = 1 end
if type(str) == "table" then
for i = start, #str do
if str[i] == 0 then break end
if str[i] == s then
return i
end
end
return nil
end
--- @cast str string
local pos = string.find(str, s, start, true)
return pos
end
IMGUI_FONT_SIZE_MAX = 512.0
IMGUI_FONT_SIZE_THRESHOLD_FOR_LOADADVANCEXONLYMODE = 128.0
IMGUI_WINDOW_HARD_MIN_SIZE = 4.0
-- ImDrawList: Lookup tables
IM_DRAWLIST_ARCFAST_TABLE_SIZE = 48 -- Number of samples in adaptive arc drawing lookup table.
IM_DRAWLIST_ARCFAST_SAMPLE_MAX = IM_DRAWLIST_ARCFAST_TABLE_SIZE -- Sample index _PathArcToFastEx() for 360 angle.
IM_DRAWLIST_TEX_LINES_WIDTH_MAX = 32 -- The maximum line width to bake anti-aliased textures for.
IM_DRAWLIST_TEX_LINES_DETAILED_WIDTH = 4 -- Calculate detailed textures for width [1..IM_DRAWLIST_TEX_LINES_DETAILED_WIDTH]
IM_DRAWLIST_TEX_LINES_SAMPLE_COUNT = 4 -- How many samples per integer thickness level.
IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX = 16
IM_DRAWLIST_TEX_CORNERS_THICKNESS_MAX = 4 -- 0: fill, 1-3: strokes thickness
IM_DRAWLIST_TEX_LINES_DETAILED_WIDTH_COUNT = ((IM_DRAWLIST_TEX_LINES_DETAILED_WIDTH - 1) * IM_DRAWLIST_TEX_LINES_SAMPLE_COUNT + 1)
-- Macros used by Recoverable Error handling
-- - Only dispatch error if _EXPR: evaluate as assert (similar to an assert macro).
-- - The message will always be a string literal, in order to increase likelihood of being display by an assert handler.
-- - See 'Demo->Configuration->Error Handling' and ImGuiIO definitions for details on error handling.
-- - Read https://github.com/ocornut/imgui/wiki/Error-Handling for details on error handling.
-- Upstream: `if (!(expr) && ErrorLog(msg)) IM_ASSERT(0)`. ErrorLog() returns io.ConfigErrorRecoveryEnableAssert.
function IM_ASSERT_USER_ERROR(_EXPR, _MSG) if not (_EXPR) or (_EXPR) == 0 then if not ImGui.ErrorLog or ImGui.ErrorLog(_MSG) then error(_MSG, 2) end end end -- Recoverable User Error
function IM_ASSERT_USER_ERROR_RET(_EXPR, _MSG) if not (_EXPR) or (_EXPR) == 0 then error(_MSG, 2) end end
-- Debug Logging for ShowDebugLogWindow(). This is designed for relatively rare events so please don't spam.
local function DebugLogIf(flag, fmt, ...)
local g = GImGui
if g and bitAnd(g.DebugLogFlags, flag) ~= 0 then ImGui.DebugLog(fmt, ...) end
end
function IMGUI_DEBUG_LOG_ACTIVEID(...) DebugLogIf(2 --[[ImGuiDebugLogFlags.EventActiveId]], ...) end
function IMGUI_DEBUG_LOG_POPUP(...) DebugLogIf(8 --[[ImGuiDebugLogFlags.EventPopup]], ...) end
function IMGUI_DEBUG_LOG_FONT(...) DebugLogIf(256 --[[ImGuiDebugLogFlags.EventFont]], ...) end -- Called from ImFontAtlas function which may operate without a context.
function IMGUI_DEBUG_LOG_VIEWPORT(...) DebugLogIf(2048 --[[ImGuiDebugLogFlags.EventViewport]], ...) end
function IMGUI_DEBUG_LOG_FOCUS(...) DebugLogIf(4 --[[ImGuiDebugLogFlags.EventFocus]], ...) end
function IMGUI_DEBUG_LOG_NAV(...) DebugLogIf(16 --[[ImGuiDebugLogFlags.EventNav]], ...) end
function IMGUI_DEBUG_LOG_IO(...) DebugLogIf(128 --[[ImGuiDebugLogFlags.EventIO]], ...) end
function IMGUI_DEBUG_LOG_INPUTROUTING(...) DebugLogIf(512 --[[ImGuiDebugLogFlags.EventInputRouting]], ...) end
function IMGUI_DEBUG_LOG_TABLE(...) DebugLogIf(4096 --[[ImGuiDebugLogFlags.EventTable]], ...) end
function IMGUI_DEBUG_LOG_DOCKING(...) DebugLogIf(1024 --[[ImGuiDebugLogFlags.EventDocking]], ...) end
function IMGUI_DEBUG_LOG_SELECTION(...) DebugLogIf(64 --[[ImGuiDebugLogFlags.EventSelection]], ...) end
function IMGUI_DEBUG_LOG_CLIPPER(...) DebugLogIf(32 --[[ImGuiDebugLogFlags.EventClipper]], ...) end
function IMGUI_DEBUG_LOG_ERROR(...) DebugLogIf(1 --[[ImGuiDebugLogFlags.EventError]], ...) end
--- @alias ImGuiSelectionUserData any
ImGuiSelectionUserData_Invalid = -1
-- Input function taking an 'ImGuiID owner_id' argument defaults to (ImGuiKeyOwner_Any == 0) aka don't test ownership, which matches legacy behavior.
ImGuiKeyOwner_Any = 0 -- Accept key that have an owner, UNLESS a call to SetKeyOwner() explicitly used ImGuiInputFlags.LockThisFrame or ImGuiInputFlags.LockUntilRelease.
ImGuiKeyOwner_NoOwner = 4294967295 -- Require key to have no owner.
-------------------------------------------------------------------------------
-- [SECTION] ImGui internal API
-- No guarantee of forward compatibility here!
-------------------------------------------------------------------------------
-- Inputs
-- FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions.
--- @param button ImGuiMouseButton
--- @return ImGuiKey
function ImGui.MouseButtonToKey(button) assert(button >= 0 and button < 5 --[[ImGuiMouseButton.COUNT]]) return 656 --[[ImGuiKey.MouseLeft]] + button end
--- @param key ImGuiKey
function ImGui.IsNamedKey(key)
return key >= 512 --[[ImGuiKey.NamedKey_BEGIN]] and key < 667 --[[ImGuiKey.NamedKey_END]]
end
--- @param key ImGuiKey
function ImGui.IsKeyboardKey(key)
return key >= 512 --[[ImGuiKey_Keyboard_BEGIN]] and key < 632 --[[ImGuiKey_Keyboard_END]]
end
--- @param key ImGuiKey
function ImGui.IsGamepadKey(key)
return key >= 632 --[[ImGuiKey_Gamepad_BEGIN]] and key < 656 --[[ImGuiKey_Gamepad_END]]
end
--- @param key ImGuiKey
function ImGui.IsMouseKey(key)
return key >= 656 --[[ImGuiKey_Mouse_BEGIN]] and key < 663 --[[ImGuiKey_Mouse_END]]
end
--- @param key ImGuiKey
function ImGui.IsAliasKey(key)
return key >= 656 --[[ImGuiKey_Aliases_BEGIN]] and key < 663 --[[ImGuiKey_Aliases_END]]
end
--- @param key ImGuiKey
function ImGui.IsLRModKey(key)
return key >= 527 --[[ImGuiKey.LeftCtrl]] and key <= 534 --[[ImGuiKey.RightSuper]]
end
--- @param key ImGuiKey
--- @return bool
function ImGui.IsNamedKeyOrMod(key)
return (key >= 512 --[[ImGuiKey.NamedKey_BEGIN]] and key < 667 --[[ImGuiKey.NamedKey_END]]) or key == 4096 --[[ImGuiMod_Ctrl]] or key == 8192 --[[ImGuiMod_Shift]] or key == 16384 --[[ImGuiMod_Alt]] or key == 32768 --[[ImGuiMod_Super]]
end
--- @param key ImGuiKey
--- @return ImGuiKey
function ImGui.ConvertSingleModFlagToKey(key)
if key == 4096 --[[ImGuiMod_Ctrl]] then
return 663 --[[ImGuiKey.ReservedForModCtrl]]
elseif key == 8192 --[[ImGuiMod_Shift]] then
return 664 --[[ImGuiKey.ReservedForModShift]]
elseif key == 16384 --[[ImGuiMod_Alt]] then
return 665 --[[ImGuiKey.ReservedForModAlt]]
elseif key == 32768 --[[ImGuiMod_Super]] then
return 666 --[[ImGuiKey.ReservedForModSuper]]
end
return key
end
-- [EXPERIMENTAL] Low-Level: Key/Input Ownership
-- - The idea is that instead of "eating" a given input, we can link to an owner id.
-- - Ownership is most often claimed as a result of reacting to a press/down event (but occasionally may be claimed ahead).
-- - Input queries can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_NoOwner (== -1) or a custom ID.
-- - Legacy input queries (without specifying an owner or _Any or _None) are equivalent to using ImGuiKeyOwner_Any (== 0).
-- - Input ownership is automatically released on the frame after a key is released. Therefore:
-- - for ownership registration happening as a result of a down/press event, the SetKeyOwner() call may be done once (common case).
-- - for ownership registration happening ahead of a down/press event, the SetKeyOwner() call needs to be made every frame (happens if e.g. claiming ownership on hover).
-- - SetItemKeyOwner() is a shortcut for common simple case. A custom widget will probably want to call SetKeyOwner() multiple times directly based on its interaction state.
-- - This is marked experimental because not all widgets are fully honoring the Set/Test idioms. We will need to move forward step by step.
-- Please open a GitHub Issue to submit your usage scenario or if there's a use case you need solved.
--- @param ctx ImGuiContext
--- @param key ImGuiKey
function ImGui.GetKeyOwnerData(ctx, key)
if bitAnd(key, 61440 --[[ImGuiMod_Mask_]]) ~= 0 then key = ImGui.ConvertSingleModFlagToKey(key) end
assert(ImGui.IsNamedKey(key))
return ctx.KeysOwnerData[key - 512 --[[ImGuiKey.NamedKey_BEGIN]]]
end
-- This extends ImGuiKeyData but only for named keys (legacy keys don't support the new features)
-- Stored in main context (1 per named key). In the future it might be merged into ImGuiKeyData.
--- @class ImGuiKeyOwnerData
--- @return ImGuiKeyOwnerData
--- @nodiscard
function ImGuiKeyOwnerData()
return {
OwnerCurr = 4294967295 --[[ImGuiKeyOwner_NoOwner]],
OwnerNext = 4294967295 --[[ImGuiKeyOwner_NoOwner]],
LockThisFrame = false, -- Reading this key requires explicit owner id (until end of frame). Set by ImGuiInputFlags.LockThisFrame.
LockUntilRelease = false -- Reading this key requires explicit owner id (until key is released). Set by ImGuiInputFlags.LockUntilRelease. When this is true LockThisFrame is always true as well.
}
end
--- @param size float
--- @return float
function ImGui.GetRoundedFontSize(size) return IM_ROUND(size) end
--- @param window ImGuiWindow
--- @param r ImRect
--- @return ImRect
--- @nodiscard
function ImGui.WindowRectAbsToRel(window, r) local off = window.DC.CursorStartPos return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y) end
--- @param window ImGuiWindow
--- @param r ImRect
--- @nodiscard
function ImGui.WindowRectRelToAbs(window, r) local off = window.DC.CursorStartPos return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y) end
--- @param dir ImGuiDir
--- @return bool
function ImGui.IsActiveIdUsingNavDir(dir)
local g = GImGui
return bit32.band(g.ActiveIdUsingNavDirMask, bit32.lshift(1, dir)) ~= 0
end
--- @param window ImGuiWindow
--- @param p ImVec2
--- @return ImVec2
--- @nodiscard
function ImGui.WindowPosAbsToRel(window, p) local off = window.DC.CursorStartPos return ImVec2(p.x - off.x, p.y - off.y) end
--- @param window ImGuiWindow
--- @param p ImVec2
--- @return ImVec2
--- @nodiscard
function ImGui.WindowPosRelToAbs(window, p) local off = window.DC.CursorStartPos return ImVec2(p.x + off.x, p.y + off.y) end
--- @param c char
--- @return bool # True if this character is a ' ' or '\t'
function ImCharIsBlankA(c) return c == 32 or c == 9 end
--- @param c char
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
ImGuiNavMoveFlags =
{
None = 0,
LoopX = bitLShift(1, 0), -- On failed request, restart from opposite side
LoopY = bitLShift(1, 1),
WrapX = bitLShift(1, 2), -- On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left)
WrapY = bitLShift(1, 3), -- This is not super useful but provided for completeness
AllowCurrentNavId = bitLShift(1, 4), -- Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place)
AlsoScoreVisibleSet = bitLShift(1, 5), -- Store alternate result in NavMoveResultLocalVisible that only comprise elements that are already fully visible (used by PageUp/PageDown)
ScrollToEdgeY = bitLShift(1, 6), -- Force scrolling to min/max (used by Home/End) -- FIXME-NAV: Aim to remove or reword as ImGuiScrollFlags
Forwarded = bitLShift(1, 7),
DebugNoResult = bitLShift(1, 8), -- Dummy scoring for debug purpose, don't apply result
FocusApi = bitLShift(1, 9), -- Requests from focus API can land/focus/activate items even if they are marked with _NoTabStop (see NavProcessItemForTabbingRequest() for details)
IsTabbing = bitLShift(1, 10), -- == Focus + Activate if item is Inputable + DontChangeNavHighlight
IsPageMove = bitLShift(1, 11), -- Identify a PageDown/PageUp request.
Activate = bitLShift(1, 12), -- Activate/select target item.
NoSelect = bitLShift(1, 13), -- Don't trigger selection by not setting g.NavJustMovedTo
NoSetNavCursorVisible = bitLShift(1, 14), -- Do not alter the nav cursor visible state
NoClearActiveId = bitLShift(1, 15), -- (Experimental) Do not clear active id when applying move result
}
ImGuiNavMoveFlags.WrapMask_ = bitOr(ImGuiNavMoveFlags.LoopX, ImGuiNavMoveFlags.LoopY, ImGuiNavMoveFlags.WrapX, ImGuiNavMoveFlags.WrapY)
--- @enum ImGuiNavLayer
ImGuiNavLayer =
{
Main = 0, -- Main scrolling layer
Menu = 1, -- Menu layer (access with Alt)
COUNT = 2
}
-------------------------------------------------------------------------------
-- [SECTION] Widgets support: flags, enums, data structures
-------------------------------------------------------------------------------
-- Extend ImGuiItemFlags
-- - input: PushItemFlag() manipulates g.CurrentItemFlags, g.NextItemData.ItemFlags, ItemAdd() calls may add extra flags too.
-- - output: stored in g.LastItemData.ItemFlags
-- Controlled by user
ImGuiItemFlags.ReadOnly = bitLShift(1, 11) -- false -- [ALPHA] Allow hovering interactions but underlying value is not changed.
ImGuiItemFlags.MixedValue = bitLShift(1, 12)
ImGuiItemFlags.NoWindowHoverableCheck = bitLShift(1, 13) -- false -- Disable hoverable check in ItemHoverable()
ImGuiItemFlags.AllowOverlap = bitLShift(1, 14) -- false -- Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame.
ImGuiItemFlags.NoNavDisableMouseHover = bitLShift(1, 15) -- false -- Nav keyboard/gamepad mode doesn't disable hover highlight (behave as if NavHighlightItemUnderNav==false).
ImGuiItemFlags.NoMarkEdited = bitLShift(1, 16) -- false -- Skip calling MarkItemEdited()
ImGuiItemFlags.NoFocus = bitLShift(1, 17) -- false -- [EXPERIMENTAL: Not very well specced] Clicking doesn't take focus. Automatically sets ImGuiButtonFlags.NoFocus + ImGuiButtonFlags.NoNavFocus in ButtonBehavior().
-- Controlled by widget code
ImGuiItemFlags.Inputable = bitLShift(1, 20) -- false -- [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature.
ImGuiItemFlags.HasSelectionUserData = bitLShift(1, 21) -- false -- Set by SetNextItemSelectionUserData()
ImGuiItemFlags.IsMultiSelect = bitLShift(1, 22) -- false -- Set by SetNextItemSelectionUserData()
ImGuiItemFlags.Default_ = bitOr(ImGuiItemFlags.AutoClosePopups, ImGuiItemFlags.LiveEditOnInputText) -- Please don't change, use PushItemFlag() instead.
-- Helpers: High-level text functions (DO NOT USE!!! THIS IS A MINIMAL SUBSET OF LARGER UPCOMING CHANGES)
--- @enum ImDrawTextFlags
ImDrawTextFlags =
{
None = 0,
CpuFineClip = bitLShift(1, 0), -- Must be == 1/true for legacy with 'bool cpu_fine_clip' arg to RenderText()
WrapKeepBlanks = bitLShift(1, 1),
StopOnNewLine = bitLShift(1, 2)
}
-- Flags for FocusWindow(). This is not called ImGuiFocusFlags to avoid confusion with public-facing ImGuiFocusedFlags.
-- FIXME: Once we finishing replacing more uses of GetTopMostPopupModal()+IsWindowWithinBeginStackOf()
-- and FindBlockingModal() with this, we may want to change the flag to be opt-out instead of opt-in.
--- @enum ImGuiFocusRequestFlags
ImGuiFocusRequestFlags =
{
None = 0,
RestoreFocusedChild = bitLShift(1, 0), -- Find last focused child (if any) and focus it instead.
UnlessBelowModal = bitLShift(1, 1) -- Do not set focus if the window is below a modal.
}
--- @enum ImGuiTextFlags
ImGuiTextFlags =
{
None = 0,
NoWidthForLargeClippedText = bitLShift(1, 0)
}
-- Character classification for word-wrapping logic
--- @enum ImWcharClass
ImWcharClass =
{
Blank = 0,
Punct = 1,
Other = 2
}
-- Helper: ImVec1 (1D vector)
-- (this odd construct is used to facilitate the transition between 1D and 2D, and the maintenance of some branches/patches)
--- @class ImVec1
--- @field x number
local IM_VEC1 = {}
IM_VEC1.__index = IM_VEC1
local function ImVec1(x) return setmetatable({x = x or 0}, IM_VEC1) end
function IM_VEC1:__tostring() return string.format("ImVec1(%g)", self.x) end
--- @param dest ImVec1
--- @param src ImVec1
function ImVec1_Copy(dest, src)
dest.x = src.x
end
-- Helper: ImRect (2D axis aligned bounding-box)
-- NB: we can't rely on ImVec2 math operators being available here!
--- @class ImRect
--- @field [1] ImVec2
--- @field [2] ImVec2
--- @field Min ImVec2 # Upper-left
--- @field Max ImVec2 # Lower-right
local IM_RECT = {}
setmetatable(IM_RECT, { __index = function(_, k) if k ~= "Min" and k ~= "Max" then error("ImRect: unknown key " .. tostring(k), 2) end end })
--- @param t ImRect
--- @param k string
IM_RECT.__index = IM_RECT -- [Luau] plain table: fast method lookup. Use r.Min / r.Max (numeric r[1]/r[2] are not supported)
-- [Luau] Min/Max are named fields, so `r.Min = v` assigns the reference: use ImVec2_Copy(r.Min, v) to keep copy semantics
IM_RECT.__newindex = function(t, k, v) assert(false, "ImRect: unknown field " .. tostring(k)) end
--- @return ImRect
--- @nodiscard
@native
function ImRect(a, b, c, d) if c and d then return setmetatable({ Min = ImVec2(a, b), Max = ImVec2(c, d) }, IM_RECT) end return setmetatable({ Min = ImVec2(a and a.x or 0, a and a.y or 0), Max = ImVec2(b and b.x or 0, b and b.y or 0) }, IM_RECT) end
function IM_RECT:__eq(other) return self.Min == other.Min and self.Max == other.Max end
function IM_RECT:__tostring() return string.format("ImRect(Min: %g,%g, Max: %g,%g)", self.Min.x, self.Min.y, self.Max.x, self.Max.y) end
--- @param p ImRect|ImVec2
@native
function IM_RECT:Contains(p)
if p.Min then
--- @cast p ImRect
return p.Min.x >= self.Min.x and p.Max.x <= self.Max.x and p.Min.y >= self.Min.y and p.Max.y <= self.Max.y
else
--- @cast p ImVec2
return p.x >= self.Min.x and p.y >= self.Min.y and p.x < self.Max.x and p.y < self.Max.y
end
end
--- @param p ImVec2
--- @param pad ImVec2
function IM_RECT:ContainsWithPad(p, pad)
return p.x >= self.Min.x - pad.x and p.y >= self.Min.y - pad.y and p.x < self.Max.x + pad.x and p.y < self.Max.y + pad.y
end
--- @param r ImRect
@native
function IM_RECT:Overlaps(r)
return self.Min.x <= r.Max.x and self.Max.x >= r.Min.x and self.Min.y <= r.Max.y and self.Max.y >= r.Min.y
end
--- @nodiscard
function IM_RECT:GetCenter() return ImVec2((self.Min.x + self.Max.x) * 0.5, (self.Min.y + self.Max.y) * 0.5) end
function IM_RECT:GetWidth() return self.Max.x - self.Min.x end
function IM_RECT:GetHeight() return self.Max.y - self.Min.y end
--- @nodiscard
@native
function IM_RECT:GetSize() return ImVec2(self.Max.x - self.Min.x, self.Max.y - self.Min.y) end
--- @nodiscard
function IM_RECT:GetTL() return ImVec2(self.Min.x, self.Min.y) end -- Top-left
--- @nodiscard
function IM_RECT:GetTR() return ImVec2(self.Max.x, self.Min.y) end -- Top-right
--- @nodiscard
function IM_RECT:GetBL() return ImVec2(self.Min.x, self.Max.y) end -- Bottom-left
--- @nodiscard
function IM_RECT:GetBR() return ImVec2(self.Max.x, self.Max.y) end -- Bottom-right
-- Simple version, may lead to an inverted rectangle, which is fine for Contains/Overlaps test but not for display.
--- @param r ImRect
@native
function IM_RECT:ClipWith(r)
-- [Luau] scalar math, no temporary ImVec2 (= ImMax(Min, r.Min) / ImMin(Max, r.Max))
local smin, smax, rmin, rmax = self.Min, self.Max, r.Min, r.Max
smin.x = mathMax(smin.x, rmin.x); smin.y = mathMax(smin.y, rmin.y)
smax.x = mathMin(smax.x, rmax.x); smax.y = mathMin(smax.y, rmax.y)
end
-- Full version, ensure both points are fully clipped.
--- @param r ImRect
@native
function IM_RECT:ClipWithFull(r)
-- [Luau] scalar math, no temporary ImVec2 (= ImClamp(Min/Max, r.Min, r.Max))
local smin, smax, rmin, rmax = self.Min, self.Max, r.Min, r.Max
local min_x, min_y, max_x, max_y = rmin.x, rmin.y, rmax.x, rmax.y
smin.x = mathMax(min_x, mathMin(smin.x, max_x)); smin.y = mathMax(min_y, mathMin(smin.y, max_y))
smax.x = mathMax(min_x, mathMin(smax.x, max_x)); smax.y = mathMax(min_y, mathMin(smax.y, max_y))
end
--- @param p ImRect|ImVec2
@native
function IM_RECT:Add(p)
if p.Min then
--- @cast p ImRect
if (self.Min.x > p.Min.x) then self.Min.x = p.Min.x end
if (self.Min.y > p.Min.y) then self.Min.y = p.Min.y end
if (self.Max.x < p.Max.x) then self.Max.x = p.Max.x end
if (self.Max.y < p.Max.y) then self.Max.y = p.Max.y end
else
--- @cast p ImVec2
if p.x < self.Min.x then self.Min.x = p.x end
if p.y < self.Min.y then self.Min.y = p.y end
if p.x > self.Max.x then self.Max.x = p.x end
if p.y > self.Max.y then self.Max.y = p.y end
end
end
function IM_RECT:AddX(x) if self.Min.x > x then self.Min.x = x end if self.Max.x < x then self.Max.x = x end end
function IM_RECT:AddY(y) if self.Min.y > y then self.Min.y = y end if self.Max.y < y then self.Max.y = y end end
--- @param amount float|ImVec2
@native
function IM_RECT:Expand(amount)
if type(amount) == "number" then
self.Min.x = self.Min.x - amount; self.Min.y = self.Min.y - amount
self.Max.x = self.Max.x + amount; self.Max.y = self.Max.y + amount
elseif type(amount) == "table" then
self.Min.x = self.Min.x - amount.x; self.Min.y = self.Min.y - amount.y
self.Max.x = self.Max.x + amount.x; self.Max.y = self.Max.y + amount.y
end
end
--- @nodiscard
function IM_RECT:ToVec4()
return ImVec4(self.Min.x, self.Min.y, self.Max.x, self.Max.y)
end
--- @param d ImVec2
@native
function IM_RECT:Translate(d)
self.Min.x = self.Min.x + d.x; self.Min.y = self.Min.y + d.y
self.Max.x = self.Max.x + d.x; self.Max.y = self.Max.y + d.y
end
--- @param dx float
function IM_RECT:TranslateX(dx)
self.Min.x = self.Min.x + dx; self.Max.x = self.Max.x + dx
end
--- @param dy float
function IM_RECT:TranslateY(dy)
self.Min.y = self.Min.y + dy; self.Max.y = self.Max.y + dy
end
function IM_RECT:IsInverted() return self.Min.x > self.Max.x or self.Min.y > self.Max.y end
function IM_RECT:GetArea() return (self.Max.x - self.Min.x) * (self.Max.y - self.Min.y) end
--- @nodiscard
function IM_RECT:AsVec4() return ImVec4(self.Min.x, self.Min.y, self.Max.x, self.Max.y) end
--- @param dest ImRect
--- @param src ImRect
@native
function ImRect_Copy(dest, src)
dest.Min.x = src.Min.x; dest.Min.y = src.Min.y
dest.Max.x = src.Max.x; dest.Max.y = src.Max.y
end
--- @param dest ImRect
--- @param src ImVec4
function ImRect_CopyFromV4(dest, src)
dest.Min.x = src.x; dest.Min.y = src.y
dest.Max.x = src.z; dest.Max.y = src.w
end
-- Helper: ImBitArray
-- Macro version of ImBitArrayTestBit(): ensure args have side-effect or are costly!
--- @param _ARRAY ImU32[]
--- @param _N int
function IM_BITARRAY_TESTBIT(_ARRAY, _N)
return bitAnd(_ARRAY[bitRShift(_N - 1, 5) + 1], bitLShift(1, bitAnd(_N - 1, 31))) ~= 0
end
-- Macro version of ImBitArrayClearBit(): ensure args have side-effect or are costly!
--- @param _ARRAY ImU32[]
--- @param _N int
function IM_BITARRAY_CLEARBIT(_ARRAY, _N)
local idx = bitRShift(_N - 1, 5) + 1
_ARRAY[idx] = bitAnd(_ARRAY[idx], bitNot(bitLShift(1, bitAnd(_N - 1, 31))))
end
--- @param arr ImU32[]
--- @param n int
function ImBitArraySetBit(arr, n)
local mask = bitLShift(1, bitAnd(n - 1, 31))
local idx = bitRShift(n - 1, 5) + 1
arr[idx] = bitOr(arr[idx], mask)
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
--- @alias ImBitArrayForNamedKeys ImBitArray
-- Helper: ImBitArray class (wrapper over ImBitArray functions)
-- Store 1-bit per value.
--- NOTE: This struct is expensive to create
--- @class ImBitArray<BITCOUNT, OFFSET>
--- @field Data ImU32[]
--- @param BITCOUNT int
--- @param OFFSET? int
--- @return ImBitArray
function ImBitArray(BITCOUNT, OFFSET)
if OFFSET == nil then OFFSET = 0 end
local this = { Data = {} }
local size = bitRShift(BITCOUNT + 31, 5)
this.ClearAllBits = function(self) local data = self.Data; for i = 1, size do data[i] = 0 end end
this.SetAllBits = function(self) local data = self.Data; for i = 1, size do data[i] = 0xFFFFFFFF end end
--- @param n int # 1-based
--- @return boolean
this.TestBit = function(self, n) n = n + OFFSET; assert(n >= 1 and n <= BITCOUNT); return IM_BITARRAY_TESTBIT(self.Data, n); end
--- @param n int # 1-based
this.SetBit = function(self, n) n = n + OFFSET; assert(n >= 1 and n <= BITCOUNT); ImBitArraySetBit(self.Data, n); end
--- @param n int # 1-based
this.ClearBit = function(self, n) n = n + OFFSET; 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; assert(n >= 1 and n <= BITCOUNT and n2 > n and n2 <= BITCOUNT + 1); ImBitArraySetBitRange(self.Data, n, n2); end
this:ClearAllBits()
return this
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) assert(n <= bitLShift(self.Storage.Size, 5)); return IM_BITARRAY_TESTBIT(self.Storage.Data, n) end
function MT.ImBitVector:SetBit(n) assert(n <= bitLShift(self.Storage.Size, 5)); ImBitArraySetBit(self.Storage.Data, n) end
function MT.ImBitVector:ClearBit(n) assert(n <= bitLShift(self.Storage.Size, 5)); ImBitArrayClearBit(self.Storage.Data, n) end
-- 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.
-- 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().
function MT.ImDrawList:PathClear() self._Path.Size = 0 end
--- @param pos ImVec2
@native
function MT.ImDrawList:PathLineTo(pos)
-- [Luau] inline self._Path:push_back(pos) (ImVec2_TCopy into the preallocated slot)
local path = self._Path
local n = path.Size + 1
if n > path.Capacity then path:push_back(pos) return end
local dest = path.Data[n]
dest.x = pos.x; dest.y = pos.y
path.Size = n
end
function MT.ImDrawList:PathLineToMergeDuplicate(pos)
local path_size = self._Path.Size
if path_size == 0 or self._Path.Data[path_size].x ~= pos.x or self._Path.Data[path_size].y ~= pos.y then
self._Path:push_back(pos)
end
end
--- @param col ImU32
--- @param flags? ImDrawFlags
@native
function MT.ImDrawList:PathFillConvex(col, flags)
self:AddConvexPolyFilled(self._Path.Data, self._Path.Size, col, flags)
self._Path.Size = 0
end
--- @param col ImU32
--- @param thickness? float
--- @param flags? ImDrawFlags
function MT.ImDrawList:PathStroke(col, thickness, flags)
if not thickness then thickness = 1.0 end
if not flags then flags = 0 end
self:_AddPolyline(self._Path.Data, self._Path.Size, col, thickness, flags, 3.4028234663853e+38 --[[FLT_MAX]])
self._Path.Size = 0
end
-- General polygon
-- - Only simple polygons are supported by filling functions (no self-intersections, no holes).
-- - Concave polygon fill is more expensive than convex one: it has O(N^2) complexity. Provided as a convenience for the user but not used by the main library.
--- @param points ImVec2[] # 1-based
--- @param num_points int
--- @param col ImU32
--- @param thickness float
--- @param flags? ImDrawFlags
function MT.ImDrawList:AddPolyline(points, num_points, col, thickness, flags)
if not flags then flags = 0 end
self:_AddPolyline(points, num_points, col, thickness, flags, 3.4028234663853e+38 --[[FLT_MAX]])
end
--- @param col ImU32
function ImGui.SetNextItemColorMarker(col)
local g = GImGui
g.NextItemData.HasFlags = bitOr(g.NextItemData.HasFlags, 32 --[[ImGuiNextItemDataFlags.HasColorMarker]])
g.NextItemData.ColorMarker = col
end
-------------------------------------------------------------------------------
-- [SECTION] ImDrawList support
-------------------------------------------------------------------------------
-- Data shared between all ImDrawList instances
-- Conceptually this could have been called e.g. ImDrawListSharedContext
-- Typically one ImGui context would create and maintain one of this.
-- You may want to create your own instance of you try to ImDrawList completely without ImGui. In that case, watch out for future changes to this structure.
--- @class ImDrawListSharedData
--- @field TexUvWhitePixel ImVec2 # UV of white pixel in the atlas (== FontAtlas->TexUvWhitePixel)
--- @field TexUvLines ImVec4[] # 0-based. UV of anti-aliased lines in the atlas (== FontAtlas->Builder->TexUvLines)
--- @field TexUvCorners ImVec4[] # 0-based. UV of rounded corner (== FontAtlas->Builder->TexUvCorners)
--- @field FontAtlas ImFontAtlas # Current font atlas
--- @field Font ImFont # Current font (used for simplified AddText overload)
--- @field FontSize float # Current font size (used for simplified AddText overload)
--- @field FontScale float # Current font scale (== FontSize / Font->FontSize)
--- @field CurveTessellationMaxError float # Tessellation tolerance when using PathBezierCurveTo()
--- @field CircleTessellationMaxError float # Number of circle segments to use per pixel of radius for AddCircle() etc
--- @field InitialDrawFlags ImDrawFlags # Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards)
--- @field ClipRectFullscreen? ImVec4 # Value for PushClipRectFullscreen()
--- @field TempBuffer ImVector<ImVec2> # Temporary write buffer
--- @field DrawLists ImVector<ImDrawList> # All draw lists associated to this ImDrawListSharedData
--- @field Context? ImGuiContext # [OPTIONAL] Link to Dear ImGui context. 99% of ImDrawList/ImFontAtlas can function without an ImGui context, but this facilitate handling one legacy edge case.
--- @field ArcFastVtx table<ImVec2> # 1-based table. Sample points on the quarter of the circle.
--- @field ArcFastRadiusCutoff float # Cutoff radius after which arc drawing will fallback to slower PathArcTo()
--- @field CircleSegmentCounts table<int> # 1-based table. Precomputed segment count for given radius before we calculate it dynamically (to avoid calculation overhead)
MT.ImDrawListSharedData = {}
MT.ImDrawListSharedData.__index = MT.ImDrawListSharedData
--- @return ImDrawListSharedData
--- @nodiscard
function ImDrawListSharedData()
local this = setmetatable({
TexUvWhitePixel = nil, -- UV of white pixel in the atlas (== FontAtlas->TexUvWhitePixel)
TexUvLines = nil, -- UV of anti-aliased lines in the atlas (== FontAtlas->Builder->TexUvLines)
TexUvCorners = nil, -- UV of rounded corner (== FontAtlas->Builder->TexUvCorners)
FontAtlas = nil, -- Current font atlas
Font = nil, -- Current font (used for simplified AddText overload)
FontSize = 0, -- Current font size (used for simplified AddText overload)
FontScale = 0, -- Current font scale (== FontSize / Font->FontSize)
CurveTessellationMaxError = 0, -- Tessellation tolerance when using PathBezierCurveTo()
CircleTessellationMaxError = 0, -- Number of circle segments to use per pixel of radius for AddCircle() etc
InitialDrawFlags = 0, -- Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards)
ClipRectFullscreen = nil, -- Value for PushClipRectFullscreen()
TempBuffer = ImVector(ImVec2), -- Temporary write buffer
DrawLists = ImVector(), -- All draw lists associated to this ImDrawListSharedData
-- Lookup tables
ArcFastVtx = {}, -- Sample points on the quarter of the circle.
ArcFastRadiusCutoff = nil, -- Cutoff radius after which arc drawing will fallback to slower PathArcTo()
CircleSegmentCounts = {}, -- Precomputed segment count for given radius before we calculate it dynamically (to avoid calculation overhead)
Context = nil -- [OPTIONAL] Link to Dear ImGui context. 99% of ImDrawList/ImFontAtlas can function without an ImGui context, but this facilitate handling one legacy edge case.
}, MT.ImDrawListSharedData)
for i = 1, 48 --[[IM_DRAWLIST_ARCFAST_TABLE_SIZE]] do
local a = ((i - 1) * 2 * 3.141592653589793 --[[IM_PI]]) / 48 --[[IM_DRAWLIST_ARCFAST_TABLE_SIZE]]
this.ArcFastVtx[i] = ImVec2(ImCos(a), ImSin(a))
end
return this
end
-- Internal storage for incrementally packing and building a ImFontAtlas
--- @class ImFontAtlasBuilder
--- @field PackContext stbrp_context
--- @field PackNodes ImVector<stbrp_node>
--- @field Rects ImVector<ImTextureRect>
--- @field RectsIndex ImVector<ImFontAtlasRectEntry>
--- @field TempBuffer ImVector<char>
--- @field RectsIndexFreeListStart int
--- @field RectsPackedCount int
--- @field RectsPackedSurface int
--- @field RectsDiscardedCount int
--- @field RectsDiscardedSurface int
--- @field FrameCount int
--- @field MaxRectSize ImVec2
--- @field MaxRectBounds ImVec2
--- @field LockDisableResize bool
--- @field PreloadedAllGlyphsRanges bool
--- @field BakedPool ImVector<ImFontBaked>
--- @field BakedMap table<ImGuiID, any> # LUA: No ImGuiStorage
--- @field BakedDiscardedCount int
--- @field PackIdMouseCursors ImFontAtlasRectId
--- @field PackIdLinesTexData ImFontAtlasRectId
--- @field PackIdLineFractTexData ImFontAtlasRectId
--- @field PackIdCornersTexData ImFontAtlasRectId
--- @field TexUvLines ImVec4[] # 0-based. UVs for baked anti-aliased lines (u0, u1, v, 1/thickness)
--- @field TexUvCorners ImVec4[] # 0-based. UVs for baked anti-aliased corners (0= fill, 1> stroke thickness)
MT.ImFontAtlasBuilder = {}
MT.ImFontAtlasBuilder.__index = MT.ImFontAtlasBuilder
--- @return ImFontAtlasBuilder
function ImFontAtlasBuilder()
--- @type ImFontAtlasBuilder
local this = setmetatable({}, MT.ImFontAtlasBuilder)
this.PackContext = nil -- struct stbrp_context_opaque { char data[80]; }; -- Actually 'stbrp_context' but we don't want to define this in the header file.
this.PackNodes = ImVector()
this.Rects = ImVector()
this.RectsIndex = ImVector() -- ImFontAtlasRectId -> index into Rects[]
this.TempBuffer = ImVector() -- Misc scratch buffer
this.RectsIndexFreeListStart = -1 -- First unused entry
this.RectsPackedCount = 0 -- Number of packed rectangles.
this.RectsPackedSurface = 0 -- Number of packed pixels. Used when compacting to heuristically find the ideal texture size.
this.RectsDiscardedCount = 0
this.RectsDiscardedSurface = 0
this.FrameCount = -1 -- Current frame count
this.MaxRectSize = ImVec2() -- Largest rectangle to pack (de-facto used as a "minimum texture size")
this.MaxRectBounds = ImVec2() -- Bottom-right most used pixels
this.LockDisableResize = false -- Disable resizing texture
this.PreloadedAllGlyphsRanges = false -- Set when missing ImGuiBackendFlags.RendererHasTextures features forces atlas to preload everything.
-- Cache of all ImFontBaked
this.BakedPool = ImVector() -- ImStableVector<ImFontBaked,32>
this.BakedMap = {} -- BakedId --> ImFontBaked
this.BakedDiscardedCount = 0
-- Custom rectangle identifiers
this.PackIdMouseCursors = -1 -- White pixel + mouse cursors. Also happen to be fallback in case of packing failure.
this.PackIdLinesTexData = -1
this.PackIdLineFractTexData = -1
this.PackIdCornersTexData = -1
-- Cached UV coordinates
this.TexUvLines = {} -- [IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1 + IM_DRAWLIST_TEX_LINES_DETAILED_WIDTH_COUNT] UVs for baked anti-aliased lines (u0, u1, v, 1/thickness) -- 0-based
this.TexUvCorners = {} -- [IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX * IM_DRAWLIST_TEX_CORNERS_THICKNESS_MAX] UVs for baked anti-aliased corners (0= fill, 1> stroke thickness) -- 0-based
for n = 0, IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 13 --[[IM_DRAWLIST_TEX_LINES_DETAILED_WIDTH_COUNT]] do this.TexUvLines[n] = ImVec4() end
for n = 0, 16 --[[IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX]] * 4 --[[IM_DRAWLIST_TEX_CORNERS_THICKNESS_MAX]] - 1 do this.TexUvCorners[n] = ImVec4() end
return this
end
--- @class ImFontStackData
--- @field Font ImFont
--- @field FontSizeBeforeScaling float # ~~ style.FontSizeBase
--- @field FontSizeAfterScaling float # ~~ g.FontSize
--- @return ImFontStackData
--- @param font ImFont
--- @param font_size_before_scaling float
--- @param font_size_after_scaling float
--- @nodiscard
function ImFontStackData(font, font_size_before_scaling, font_size_after_scaling)
return {
Font = font,
FontSizeBeforeScaling = font_size_before_scaling, -- ~~ style.FontSizeBase
FontSizeAfterScaling = font_size_after_scaling -- ~~ g.FontSize
}
end
-------------------------------------------------------------------------------
-- [SECTION] Style support
-------------------------------------------------------------------------------
--- `GetVarPtr()` is currently only used on `g.Style` in Dear ImGui, and we don't have pointers!
--- @class ImGuiStyleVarInfo
--- @field Count ImU32 # 1+
--- @field DataType ImGuiDataType
--- @field Key string # key in parent structure. Note that the cpp Dear ImGui uses `ImU32 Offset`!
--- @param count ImU32
--- @param data_type ImGuiDataType
--- @param key string
--- @return ImGuiStyleVarInfo
--- @nodiscard
MT.ImGuiStyleVarInfo = {}
MT.ImGuiStyleVarInfo.__index = MT.ImGuiStyleVarInfo
function ImGuiStyleVarInfo(count, data_type, key)
return setmetatable({ Count = count, DataType = data_type, Key = key }, MT.ImGuiStyleVarInfo)
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.
--- @class ImGuiStyleMod
--- @field VarIdx ImGuiStyleVar
--- @field BackupVal table
--- @param idx ImGuiStyleVar
--- @param v int|float|ImVec2
function ImGuiStyleMod(idx, v)
local this = { VarIdx = idx, BackupVal = {nil, nil} }
--- @cast this ImGuiStyleMod
if type(v) == "number" then
this.BackupVal[1] = v
else -- ImVec2
this.BackupVal[1] = v.x; this.BackupVal[2] = v.y
end
return this
end
-------------------------------------------------------------------------------
-- [SECTION] Data types support
-------------------------------------------------------------------------------
-- Type information associated to one ImGuiDataType. Retrieve with DataTypeGetInfo().
--- @class ImGuiDataTypeInfo
--- @field Size size_t # Size in bytes
--- @field Name string # Short descriptive name for the type, for debugging
--- @field PrintFmt string # Default printf format for the type
--- @field ScanFmt string # Default scanf format for the type
-- Status storage for the last submitted item
--- @class ImGuiLastItemData
--- @field ID ImGuiID
--- @field ItemFlags ImGuiItemFlags # See ImGuiItemFlags (called 'InFlags' before v1.91.4).
--- @field StatusFlags ImGuiItemStatusFlags # See ImGuiItemStatusFlags
--- @field Rect ImRect # Full rectangle
--- @field NavRect ImRect # Navigation scoring rectangle (not displayed)
--- @field DisplayRect ImRect # Display rectangle. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags.HasDisplayRect) is set.
--- @field ClipRect ImRect # Clip rectangle at the time of submitting item. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags.HasClipRect) is set..
--- @field Shortcut ImGuiKeyChord # Shortcut at the time of submitting item. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags.HasShortcut) is set..
--- @return ImGuiLastItemData
--- @nodiscard
function ImGuiLastItemData()
return {
ID = 0,
ItemFlags = 0, -- See ImGuiItemFlags (called 'InFlags' before v1.91.4).
StatusFlags = 0, -- See ImGuiItemStatusFlags
Rect = ImRect(), -- Full rectangle
NavRect = ImRect(), -- Navigation scoring rectangle (not displayed)
-- Rarely used fields are not explicitly cleared, only valid when the corresponding ImGuiItemStatusFlags are set.
DisplayRect = ImRect(), -- Display rectangle. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags.HasDisplayRect) is set.
ClipRect = ImRect(), -- Clip rectangle at the time of submitting item. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags.HasClipRect) is set..
Shortcut = 0 -- Shortcut at the time of submitting item. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags.HasShortcut) is set..
}
end
--- @param dest ImGuiLastItemData
--- @param src ImGuiLastItemData
function ImGuiLastItemData_Copy(dest, src)
dest.ID = src.ID
dest.ItemFlags = src.ItemFlags
dest.StatusFlags = src.StatusFlags
ImRect_Copy(dest.Rect, src.Rect)
ImRect_Copy(dest.NavRect, src.NavRect)
ImRect_Copy(dest.DisplayRect, src.DisplayRect)
ImRect_Copy(dest.ClipRect, src.ClipRect)
dest.Shortcut = src.Shortcut
end
-- Store data emitted by TreeNode() for usage by TreePop()
-- - To implement ImGuiTreeNodeFlags.NavLeftJumpsToParent: store the minimum amount of data
-- which we can't infer in TreePop(), to perform the equivalent of NavApplyItemToResult().
-- Only stored when the node is a potential candidate for landing on a Left arrow jump.
--- @class ImGuiTreeNodeStackData
--- @field ID ImGuiID
--- @field TreeFlags ImGuiTreeNodeFlags
--- @field ItemFlags ImGuiItemFlags
--- @field NavRect ImRect
--- @field DrawLinesX1 float
--- @field DrawLinesToNodesY2 float
--- @field DrawLinesTableColumn ImGuiTableColumnIdx
--- @return ImGuiTreeNodeStackData
--- @nodiscard
local function ImGuiTreeNodeStackData()
return {
ID = nil,
TreeFlags = nil,
ItemFlags = nil, -- Used for nav landing
NavRect = ImRect(), -- Used for nav landing
DrawLinesX1 = nil,
DrawLinesToNodesY2 = nil,
DrawLinesTableColumn = nil
}
end
-- Storage for SetNextItem** functions
--- @class ImGuiNextItemData
--- @field HasFlags ImGuiNextItemDataFlags
--- @field ItemFlagsSet ImGuiItemFlags
--- @field FocusScopeId ImGuiID
--- @field SelectionUserData any
--- @field Width number
--- @field Shortcut ImGuiKeyChord
--- @field ShortcutFlags ImGuiInputFlags
--- @field OpenVal boolean
--- @field OpenCond ImGuiCond
--- @field RefVal any
--- @field StorageId ImGuiID
--- @field ColorMarker ImU32
MT.ImGuiNextItemData = {}
MT.ImGuiNextItemData.__index = MT.ImGuiNextItemData
-- Also cleared manually by ItemAdd()!
function MT.ImGuiNextItemData:ClearFlags()
self.HasFlags = 0 --[[ImGuiNextItemDataFlags.None]]
self.ItemFlagsSet = 0 --[[ImGuiItemFlags.None]]
end
--- @return ImGuiNextItemData
--- @nodiscard
function ImGuiNextItemData()
return setmetatable({
HasFlags = 0, -- Called HasFlags instead of Flags to avoid mistaking this
ItemFlagsSet = 0, -- Currently only tested/used for ImGuiItemFlags.AllowOverlap and ImGuiItemFlags.HasSelectionUserData.
-- Members below are NOT cleared by ItemAdd() meaning they are still valid during e.g. NavProcessItem(). Always rely on HasFlags.
FocusScopeId = 0, -- Set by SetNextItemSelectionUserData()
SelectionUserData = -1, -- Set by SetNextItemSelectionUserData() (note that nil/0 is a valid value, we use -1 == ImGuiSelectionUserData_Invalid to mark invalid values)
Width = 0.0, -- Set by SetNextItemWidth()
Shortcut = 0, -- Set by SetNextItemShortcut()
ShortcutFlags = 0, -- Set by SetNextItemShortcut()
OpenVal = false, -- Set by SetNextItemOpen()
OpenCond = 0, -- Set by SetNextItemOpen()
RefVal = 0, -- Not exposed yet, for ImGuiInputTextFlags.ParseEmptyAsRefVal
StorageId = 0, -- Set by SetNextItemStorageID()
ColorMarker = 0 -- Set by SetNextItemColorMarker(). Not exposed yet, supported by DragScalar,SliderScalar and for ImGuiSliderFlags.ColorMarkers.
}, MT.ImGuiNextItemData)
end
-- Storage for SetNexWindow** functions
--- @class ImGuiNextWindowData
--- @field HasFlags ImGuiNextWindowDataFlags
--- @field PosCond ImGuiCond
--- @field SizeCond ImGuiCond
--- @field CollapsedCond ImGuiCond
--- @field PosVal ImVec2?
--- @field PosPivotVal ImVec2?
--- @field SizeVal ImVec2?
--- @field ContentSizeVal ImVec2?
--- @field ScrollVal ImVec2?
--- @field WindowFlags ImGuiWindowFlags?
--- @field ChildFlags ImGuiChildFlags?
--- @field CollapsedVal boolean?
--- @field SizeConstraintRect ImRect?
--- @field SizeCallback function?
--- @field SizeCallbackUserData any
--- @field BgAlphaVal number?
--- @field MenuBarOffsetMinVal ImVec2?
--- @field RefreshFlagsVal int?
MT.ImGuiNextWindowData = {}
MT.ImGuiNextWindowData.__index = MT.ImGuiNextWindowData
function MT.ImGuiNextWindowData:ClearFlags()
self.HasFlags = 0 --[[ImGuiNextWindowDataFlags.None]]
end
--- @return ImGuiNextWindowData
--- @nodiscard
function ImGuiNextWindowData()
return setmetatable({
HasFlags = 0,
-- Members below are NOT cleared. Always rely on HasFlags.
PosCond = 0,
SizeCond = 0,
CollapsedCond = 0,
PosVal = ImVec2(),
PosPivotVal = ImVec2(),
SizeVal = ImVec2(),
ContentSizeVal = ImVec2(),
ScrollVal = ImVec2(),
WindowFlags = nil,
ChildFlags = nil,
WindowFlagsSet = 0, -- Flags to set. Use SetNextWindowFlags()!
WindowFlagsClear = 0,
ChildFlagsSet = 0, -- Flags to set. Use SetNextWindowChildFlags().
ChildFlagsClear = 0,
ViewportId = 0,
DockId = 0,
DockCond = 0,
PosUndock = false,
WindowClass = nil,
CollapsedVal = nil,
SizeConstraintRect = nil,
SizeCallback = nil,
SizeCallbackUserData = nil,
BgAlphaVal = nil, -- Override background alpha
MenuBarOffsetMinVal = ImVec2(), -- (Always on) This is not exposed publicly, so we don't clear it and it doesn't have a corresponding flag (could we? for consistency?)
RefreshFlagsVal = nil,
CacheKeySet = false, -- [Port] SetNextWindowCached()
CacheKeyVal = nil
}, MT.ImGuiNextWindowData)
end
--- @enum ImGuiWindowBgClickFlags
ImGuiWindowBgClickFlags = {
None = 0,
Move = bitLShift(1, 0), -- Click on bg/void + drag to move window. Cleared by default when using io.ConfigWindowsMoveFromTitleBarOnly.
}
--- @enum ImGuiNextWindowDataFlags
ImGuiNextWindowDataFlags = {
None = 0,
HasPos = bitLShift(1, 0),
HasSize = bitLShift(1, 1),
HasContentSize = bitLShift(1, 2),
HasCollapsed = bitLShift(1, 3),
HasSizeConstraint = bitLShift(1, 4),
HasFocus = bitLShift(1, 5),
HasBgAlpha = bitLShift(1, 6),
HasScroll = bitLShift(1, 7),
HasWindowFlags = bitLShift(1, 8),
HasChildFlags = bitLShift(1, 9),
HasRefreshPolicy = bitLShift(1, 10),
HasViewport = bitLShift(1, 11),
HasDock = bitLShift(1, 12),
HasWindowClass = bitLShift(1, 13)
}
-- FIXME: this is in development, not exposed/functional as a generic feature yet.
-- Horizontal/Vertical enums are fixed to 0/1 so they may be used to index ImVec2
--- @enum ImGuiLayoutType
ImGuiLayoutType =
{
Horizontal = 0,
Vertical = 1
}
--- @enum ImGuiLogFlags
ImGuiLogFlags =
{
None = 0,
OutputTTY = bitLShift(1, 0),
OutputFile = bitLShift(1, 1),
OutputBuffer = bitLShift(1, 2),
OutputClipboard = bitLShift(1, 3),
}
ImGuiLogFlags.OutputMask_ = bitOr(ImGuiLogFlags.OutputTTY, ImGuiLogFlags.OutputFile, ImGuiLogFlags.OutputBuffer, ImGuiLogFlags.OutputClipboard)
-- Flags for internal's BeginColumns(). This is an obsolete API. Prefer using BeginTable() nowadays!
--- @enum ImGuiOldColumnFlags
ImGuiOldColumnFlags =
{
None = 0,
NoBorder = bitLShift(1, 0), -- Disable column dividers
NoResize = bitLShift(1, 1), -- Disable resizing columns when clicking on the dividers
NoPreserveWidths = bitLShift(1, 2), -- Disable column width preservation when adjusting columns
NoForceWithinWindow = bitLShift(1, 3), -- Disable forcing columns to fit within window
GrowParentContentsSize = bitLShift(1, 4), -- Restore pre-1.51 behavior of extending the parent window contents size but _without affecting the columns width at all_. Will eventually remove.
}
--- @enum ImGuiSeparatorFlags
ImGuiSeparatorFlags =
{
None = 0,
Horizontal = bitLShift(1, 0), -- Axis default to current layout type, so generally Horizontal unless e.g. in a menu bar
Vertical = bitLShift(1, 1),
SpanAllColumns = bitLShift(1, 2) -- Make separator cover all columns of a legacy Columns() set.
}
--- @class ImGuiStyle
MT.ImGuiStyle = {}
MT.ImGuiStyle.__index = MT.ImGuiStyle
--- @return ImGuiStyle
--- @nodiscard
function ImGuiStyle()
--- @type ImGuiStyle
local this = {
FontSizeBase = 0.0, -- Will default to io.Fonts.Fonts[0] on first frame.
FontScaleMain = 1.0, -- Main scale factor. May be set by application once, or exposed to end-user.
FontScaleDpi = 1.0, -- Additional scale factor from viewport/monitor contents scale. When io.ConfigDpiScaleFonts is enabled, this is automatically overwritten when changing monitor DPI.
Alpha = 1.0, -- Global alpha applies to everything in Dear ImGui.
DisabledAlpha = 0.60, -- Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha.
FramePadding = ImVec2(4, 3), -- Padding within a framed rectangle (used by most widgets)
FrameRounding = 0.0, -- Radius of frame corners rounding. Set to 0.0 to have rectangular frames (used by most widgets).
FrameBorderSize = 0.0, -- Thickness of border around frames. Generally set to 0.0 or 1.0. Other values not well tested.
WindowPadding = ImVec2(8, 8), -- Padding within a window
TouchExtraPadding = ImVec2(0, 0), -- Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much!
IndentSpacing = 21.0, -- Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
WindowRounding = 0, -- Radius of window corners rounding. Set to 0.0 to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended.
WindowBorderSize = 1, -- Thickness of border around windows. Generally set to 0.0 or 1.0. Other values not well tested.
ScrollbarSize = 14.0, -- Width of the vertical scrollbar, Height of the horizontal scrollbar
ScrollbarRounding = 9.0, -- Radius of grab corners rounding for scrollbar
ScrollbarPadding = 2.0, -- Padding of scrollbar grab within its frame (same for both axes)
GrabMinSize = 12.0, -- Minimum width/height of a grab box for slider/scrollbar
GrabRounding = 0.0, -- Radius of grabs corners rounding. Set to 0.0 to have rectangular slider grabs.
ImageRounding = 0.0, -- Rounding of Image() calls.
ImageBorderSize = 0.0, -- Thickness of border around Image() calls.
WindowBorderHoverPadding = 4.0, -- Hit-testing extent outside/inside resizing border. Also extend determination of hovered window. Generally meaningfully larger than WindowBorderSize to make it easy to reach borders.
TreeLinesFlags = 262144 --[[ImGuiTreeNodeFlags.DrawLinesNone]],
TreeLinesSize = 1.0, -- Thickness of outlines when using ImGuiTreeNodeFlags.DrawLines.
TreeLinesRounding = 0.0, -- Radius of lines connecting child nodes to the vertical line.
MenuItemRounding = 0.0, -- Radius of MenuItem, BeginMenu rounding.
SelectableRounding = 0.0, -- Radius of selectable rounding. MODIFYING THIS IS DISCOURAGED. CONTIGUOUS SELECTIONS WILL NOT LOOK RIGHT. (#7589)
ColorMarkerSize = 3.0, -- Size of R/G/B/A color markers for ColorEdit4() and for Drags/Sliders when using ImGuiSliderFlags.ColorMarkers.
ColorButtonPosition = 1 --[[ImGuiDir.Right]], -- Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir.Right.
InputTextCursorSize = 1.0, -- Thickness of cursor/caret in InputText().
SeparatorSize = 1.0, -- Thickness of border in Separator().
SeparatorTextBorderSize = 3.0, -- Thickness of border in SeparatorText().
SeparatorTextAlign = ImVec2(0.0, 0.5), -- Alignment of text within the separator. Defaults to (0.0, 0.5) (left aligned, center).
SeparatorTextPadding = ImVec2(20.0, 3.0), -- Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y.
DisplaySafeAreaPadding = ImVec2(3, 3), -- If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows.
DisplayWindowPadding = ImVec2(19, 19), -- Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows.
-- Rendering & Tessellation
AntiAliasedLines = true, -- Enable anti-aliased lines/borders. Used at the beginning of the frame to set ImDrawFlags_AALines in all draw-lists.
AntiAliasedLineEnds = false, -- Enable anti-aliased lines/borders ends. Nicer for thick lines but more expensive. Used at the beginning of the frame to set ImDrawFlags_AALineEnds in all draw-lists.
AntiAliasedFill = true, -- Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Used at the beginning of the frame to set ImDrawFlags_AAFill in all draw-lists.
CurveTessellationMaxError = 1.12, -- Maximum error (in pixels) when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
CircleTessellationMaxError = 0.30, -- Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
-- Behaviors
HoverStationaryDelay = 0.15, -- Delay for IsItemHovered(ImGuiHoveredFlags.Stationary). Time required to consider mouse stationary.
HoverDelayShort = 0.15, -- Delay for IsItemHovered(ImGuiHoveredFlags.DelayShort). Usually used along with HoverStationaryDelay.
HoverDelayNormal = 0.40, -- Delay for IsItemHovered(ImGuiHoveredFlags.DelayNormal). "
Colors = {},
ButtonTextAlign = ImVec2(0.5, 0.5), -- Alignment of button text when button is larger than text.
SelectableTextAlign = ImVec2(0.0, 0.0), -- Alignment of selectable text. Defaults to (0.0, 0.0) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.
WindowMinSize = ImVec2(32, 32), -- Minimum window size
WindowTitleAlign = ImVec2(0.0, 0.5), -- Alignment for title bar text
WindowMenuButtonPosition = 0 --[[ImGuiDir.Left]], -- Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir.Left.
ChildRounding = 0.0, -- Radius of child window corners rounding. Set to 0.0 to have rectangular child windows
ChildBorderSize = 1.0, -- Thickness of border around child windows. Generally set to 0.0 or 1.0. Other values not well tested.
PopupRounding = 0.0, -- Radius of popup window corners rounding. Set to 0.0 to have rectangular child windows
PopupBorderSize = 1.0, -- Thickness of border around popup or tooltip windows. Generally set to 0.0 or 1.0. Other values not well tested.
DockingSeparatorSize = 2.0, -- Thickness of resizing border between docked windows
DockingNodeHasCloseButton = true, -- Docking nodes have their own CloseButton() to close all docked windows.
MouseCursorScale = 1.0, -- Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
ItemSpacing = ImVec2(8, 4), -- Horizontal and vertical spacing between widgets/lines
ItemInnerSpacing = ImVec2(4, 4), -- Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
CellPadding = ImVec2(4, 2), -- Padding within a table cell. Cellpadding.x is locked for entire table. CellPadding.y may be altered between different rows.
ColumnsMinSpacing = 6.0, -- Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
LogSliderDeadzone = 4.0, -- The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
TabRounding = 5.0, -- Radius of upper corners of a tab. Set to 0.0 to have rectangular tabs.
TabBorderSize = 0.0, -- Thickness of border around tabs.
TabMinWidthBase = 1.0, -- Minimum tab width, to make tabs larger than their contents. TabBar buttons are not affected.
TabMinWidthShrink = 80.0, -- Minimum tab width after shrinking, when using ImGuiTabBarFlags.FittingPolicyMixed policy. FLT_MAX: never shrink, will behave like ImGuiTabBarFlags.FittingPolicyScroll.
TabCloseButtonMinWidthSelected = -1.0, -- -1: always visible. 0.0: visible when hovered. >0.0: visible when hovered if minimum width.
TabCloseButtonMinWidthUnselected = 0.0, -- -1: always visible. 0.0: visible when hovered. >0.0: visible when hovered if minimum width. FLT_MAX: never show close button when unselected.
TabBarBorderSize = 1.0, -- Thickness of tab-bar separator, which takes on the tab active color to denote focus.
TabBarOverlineSize = 1.0, -- Thickness of tab-bar overline, which highlights the selected tab-bar.
TableAngledHeadersAngle = 35.0 * (3.141592653589793 --[[IM_PI]] / 180.0), -- Angle of angled headers (supported values range from -50 degrees to +50 degrees).
TableAngledHeadersTextAlign = ImVec2(0.5, 0.0), -- Alignment of angled headers within the cell
DragDropTargetRounding = 0.0, -- Radius of the drag and drop target frame.
DragDropTargetBorderSize = 2.0, -- Thickness of the drag and drop target border.
DragDropTargetPadding = 3.0, -- Size to expand the drag and drop target from actual target item size.
HoverFlagsForTooltipMouse = bitOr(8192 --[[ImGuiHoveredFlags.Stationary]], 32768 --[[ImGuiHoveredFlags.DelayShort]], 1024 --[[ImGuiHoveredFlags.AllowWhenDisabled]]), -- Default flags when using IsItemHovered(ImGuiHoveredFlags.ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse.
HoverFlagsForTooltipNav = bitOr(131072 --[[ImGuiHoveredFlags.NoSharedDelay]], 65536 --[[ImGuiHoveredFlags.DelayNormal]], 1024 --[[ImGuiHoveredFlags.AllowWhenDisabled]]), -- Default flags when using IsItemHovered(ImGuiHoveredFlags.ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad.
-- [Internal]
_MainScale = 1.0,
_NextFrameFontSizeBase = 0.0,
-- Obsolete names (#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS)
CurveTessellationTol = 0.0, -- [OBSOLETE] Old CurveTessellationTol = CurveTessellationMaxError*CurveTessellationMaxError.
AntiAliasedLinesUseTex = true, -- [OBSOLETE] Enable anti-aliased lines/borders using textures for legacy strokes. Require backend to render with bilinear filtering (NOT point/nearest filtering).
}
-- Default theme
ImGui.StyleColorsDark(this)
setmetatable(this, MT.ImGuiStyle)
return this
end
-- Storage for each window pushed into the stack.
--- @class ImGuiWindowStackData
--- @return ImGuiWindowStackData
--- @nodiscard
function ImGuiWindowStackData()
return {
Window = nil,
ParentLastItemDataBackup = ImGuiLastItemData(),
StackSizesInBegin = nil, -- Store size of various stacks for asserting
DisabledOverrideReenable = nil, -- Non-child window override disabled flag
DisabledOverrideReenableAlphaBackup = nil,
ParentLastComboPreviewRect = ImRect(),
}
end
-- Storage data for BeginComboPreview()/EndComboPreview()
--- @class ImGuiComboPreviewData
--- @field PreviewRect ImRect
--- @field BackupCursorPos ImVec2
--- @field BackupCursorMaxPos ImVec2
--- @field BackupCursorPosPrevLine ImVec2
--- @field BackupPrevLineTextBaseOffset float
--- @field BackupLayout ImGuiLayoutType
--- @return ImGuiComboPreviewData
--- @nodiscard
local function ImGuiComboPreviewData()
return {
PreviewRect = ImRect(),
BackupCursorPos = ImVec2(),
BackupCursorMaxPos = ImVec2(),
BackupCursorPosPrevLine = ImVec2(),
BackupPrevLineTextBaseOffset = 0.0,
BackupLayout = 0,
BackupWorkRectMaxX = 0.0,
BackupContentRectMaxX = 0.0,
WithinPreview = false,
}
end
--- @alias IMSTB_TEXTEDIT_STRING ImGuiInputTextState
--- @alias ImStbTexteditState STB_TexteditState
-- Internal state of the currently focused/edited text input box
-- For a given item ID, access with ImGui.GetInputTextState()
--- @class ImGuiInputTextState
--- @field Ctx ImGuiContext # parent UI context (needs to be set explicitly by parent)
--- @field Stb ImStbTexteditState # State for stb_textedit.lua
--- @field Flags ImGuiInputTextFlags # copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags.Password is set.
--- @field ID ImGuiID # widget id owning the text state
--- @field TextLen int # UTF-8 length of the string in TextA (in bytes)
--- @field TextSrc ImStringBuffer # == TextA.Data unless read-only, in which case == buf passed to InputText(). For _ReadOnly fields, pointer will be null outside the InputText() call
--- @field TextA ImVector<char> # main UTF8 buffer. TextA.Size is a buffer size! Should always be >= buf_size passed by user (and of course >= CurLenA + 1)
--- @field TextToRevertTo ImVector<char> # value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered)
--- @field CallbackTextBackup ImVector<char> # temporary storage for callback to support automatic reconcile of undo-stack
--- @field BufCapacity int # end-user buffer capacity (include zero terminator)
--- @field Scroll ImVec2 # horizontal offset (managed manually) + vertical scrolling (pulled from child window's own Scroll.y)
--- @field LineCount int # last line count (solely for debugging)
--- @field WrapWidth float # word-wrapping width
--- @field CursorAnim float # timer for cursor blink, reset on every user action so the cursor reappears immediately
--- @field CursorFollow bool # set when we want scrolling to follow the current cursor position (not always!)
--- @field CursorCenterY bool # set when we want scrolling to be centered over the cursor position (while resizing a word-wrapping field)
--- @field SelectedAllMouseLock bool # after a double-click to select all, we ignore further mouse drags to update selection
--- @field EditedBefore bool # edited since activated
--- @field EditedThisFrame bool # edited this frame
MT.ImGuiInputTextState = {}
MT.ImGuiInputTextState.__index = MT.ImGuiInputTextState
local function ImGuiInputTextState()
local this = {
Ctx = nil, -- parent UI context (needs to be set explicitly by parent).
Stb = STB_TexteditState(), -- FIXME: no global! -- State for stb_textedit.h
Flags = 0, -- copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags.Password is set.
ID = 0, -- widget id owning the text state
TextLen = 0, -- UTF-8 length of the string in TextA (in bytes)
TextSrc = nil, -- == TextA.Data unless read-only, in which case == buf passed to InputText(). For _ReadOnly fields, pointer will be null outside the InputText() call.
TextA = ImVector(), -- main UTF8 buffer. TextA.Size is a buffer size! Should always be >= buf_size passed by user (and of course >= CurLenA + 1).
TextToRevertTo = ImVector(), -- value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered)
CallbackTextBackup = ImVector(), -- temporary storage for callback to support automatic reconcile of undo-stack
BufCapacity = 0, -- end-user buffer capacity (include zero terminator)
Scroll = ImVec2(), -- horizontal offset (managed manually) + vertical scrolling (pulled from child window's own Scroll.y)
LineCount = 0, -- last line count (solely for debugging)
WrapWidth = 0.0, -- word-wrapping width
CursorAnim = 0.0, -- timer for cursor blink, reset on every user action so the cursor reappears immediately
CursorFollow = false, -- set when we want scrolling to follow the current cursor position (not always!)
CursorCenterY = false, -- set when we want scrolling to be centered over the cursor position (while resizing a word-wrapping field)
SelectedAllMouseLock = false, -- after a double-click to select all, we ignore further mouse drags to update selection
EditedBefore = false, -- edited since activated
EditedThisFrame = false, -- edited this frame
ValidatedThisFrame = false, -- validated this frame (e.g. Enter pressed)
WantReloadUserBuf = false, -- force a reload of user buf so it may be modified externally. may be automatic in future version.
LastMoveDirectionLR = 0 --[[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)
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.
-- Store as part of ImGuiDeactivatedItemData?
--- @class ImGuiInputTextDeactivatedState
--- @field ID ImGuiID # widget id owning the text state (which just got deactivated)
--- @field ElapseFrame int
--- @field TextA ImVector<char> # text buffer
MT.ImGuiInputTextDeactivatedState = {}
MT.ImGuiInputTextDeactivatedState.__index = MT.ImGuiInputTextDeactivatedState
function MT.ImGuiInputTextDeactivatedState:ClearFreeMemory()
self.ID = 0
self.ElapseFrame = 0
self.TextA:clear()
end
--- @return ImGuiInputTextDeactivatedState
--- @nodiscard
local function ImGuiInputTextDeactivatedState()
return setmetatable({
ID = 0, -- widget id owning the text state (which just got deactivated)
ElapseFrame = 0,
TextA = ImVector() -- text buffer
}, MT.ImGuiInputTextDeactivatedState)
end
--- @alias ImGuiKeyRoutingIndex ImS16
-- Routing table entry (sizeof() == 16 bytes)
--- @class ImGuiKeyRoutingData
--- @field NextEntryIndex ImGuiKeyRoutingIndex
--- @field Mods ImU16 # Technically we'd only need 4-bits but for simplify we store ImGuiMod_ values which need 16-bits.
--- @field RoutingCurrScore ImU16 # [DEBUG] For debug display
--- @field RoutingNextScore ImU16 # Lower is better (0: perfect score)
--- @field RoutingCurr ImGuiID
--- @field RoutingNext ImGuiID
--- @return ImGuiKeyRoutingData
--- @nodiscard
function ImGuiKeyRoutingData()
return {
NextEntryIndex = -1,
Mods = 0, -- Technically we'd only need 4-bits but for simplify we store ImGuiMod_ values which need 16-bits.
RoutingCurrScore = 0, -- [DEBUG] For debug display
RoutingNextScore = 0, -- Lower is better (0: perfect score)
RoutingCurr = 4294967295 --[[ImGuiKeyOwner_NoOwner]],
RoutingNext = 4294967295 --[[ImGuiKeyOwner_NoOwner]]
}
end
-- Routing table: maintain a desired owner for each possible key-chord (key + mods), and setup owner in NewFrame() when mods are matching.
-- Stored in main context (1 instance)
--- @class ImGuiKeyRoutingTable
--- @field Index ImGuiKeyRoutingIndex[] # Index of first entry in Entries[]
--- @field Entries ImVector<ImGuiKeyRoutingData>
--- @field EntriesNext ImVector<ImGuiKeyRoutingData> # Double-buffer to avoid reallocation (could use a shared buffer)
MT.ImGuiKeyRoutingTable = {}
MT.ImGuiKeyRoutingTable.__index = MT.ImGuiKeyRoutingTable
function MT.ImGuiKeyRoutingTable:Clear()
for n = 1, 155 --[[ImGuiKey.NamedKey_COUNT]] do self.Index[n] = -1 end
self.Entries:clear()
self.EntriesNext:clear()
end
--- @return ImGuiKeyRoutingTable
local function ImGuiKeyRoutingTable()
local this = setmetatable({
Index = {}, -- Index of first entry in Entries[]
Entries = ImVector(),
EntriesNext = ImVector() -- Double-buffer to avoid reallocation (could use a shared buffer)
}, MT.ImGuiKeyRoutingTable)
this:Clear()
return this
end
-- Helper: ImGuiTextIndex
-- Maintain a line index for a text buffer. This is a strong candidate to be moved into the public API.
--- @class ImGuiTextIndex
--- @field Offsets ImVector<int>
--- @field EndOffset int # Because we don't own text buffer we need to maintain EndOffset (may bake in LineOffsets?)
local IMGUI_TEXT_INDEX = {}
IMGUI_TEXT_INDEX.__index = IMGUI_TEXT_INDEX
function IMGUI_TEXT_INDEX:clear() self.Offsets:clear(); self.EndOffset = 0; end
function IMGUI_TEXT_INDEX:size() return self.Offsets.Size end
-- [Port] 'n' is a 0-based line number like upstream (Offsets.Data is 1-based), 'base' the 1-based position of the buffer start; returns 1-based positions
function IMGUI_TEXT_INDEX:get_line_begin(base, n)
return base + (self.Offsets.Size ~= 0 and self.Offsets.Data[n + 1] or 0)
end
function IMGUI_TEXT_INDEX:get_line_end(base, n)
return base + ((n + 1 < self.Offsets.Size) and (self.Offsets.Data[n + 2] - 1) or self.EndOffset)
end
--- @return ImGuiTextIndex
local function ImGuiTextIndex()
return setmetatable({ Offsets = ImVector(), EndOffset = 0 }, IMGUI_TEXT_INDEX)
end
--- @class ImGuiDebugAllocEntry
--- @field FrameCount int
--- @field AllocCount int
--- @field FreeCount int
--- @return ImGuiDebugAllocEntry
local function ImGuiDebugAllocEntry()
return { FrameCount = 0, AllocCount = 0, FreeCount = 0 }
end
--- @class ImGuiDebugAllocInfo
--- @field TotalAllocCount int # Number of call to MemAlloc().
--- @field TotalFreeCount int
--- @field LastEntriesIdx ImS16 # Current index in buffer
--- @field LastEntriesBuf ImGuiDebugAllocEntry[] # Track last 6 frames that had allocations
--- @return ImGuiDebugAllocInfo
local function ImGuiDebugAllocInfo()
local this = {
TotalAllocCount = 0, -- Number of call to MemAlloc().
TotalFreeCount = 0,
LastEntriesIdx = 1, -- Current index in buffer
LastEntriesBuf = {nil, nil, nil, nil, nil, nil}, -- Track last 6 frames that had allocations
}
for i = 1, 6 do
this.LastEntriesBuf[i] = ImGuiDebugAllocEntry()
end
return this
end
--- @class ImGuiMetricsConfig
--- @return ImGuiMetricsConfig
local function ImGuiMetricsConfig()
return
{
ShowDebugLog = false,
ShowIDStackTool = false,
ShowWindowsRects = false,
ShowWindowsBeginOrder = false,
ShowTablesRects = false,
ShowDrawCmdMesh = true,
ShowDrawCmdBoundingBoxes = true,
ShowTextEncodingViewer = false,
ShowTextureUsedRect = false,
ShowDockingNodes = false,
ShowWindowsRectsType = -1,
ShowTablesRectsType = -1,
HighlightMonitorIdx = -1,
HighlightViewportID = 0,
SettingsDiscardMonths = 6,
SettingsHighlightOldEntries = false,
ShowFontPreview = true,
}
end
-- Storage for navigation query/results
--- @class ImGuiNavItemData
--- @field Window ImGuiWindow? # Init,Move -- Best candidate window (result->ItemWindow->RootWindowForNav == request->Window)
--- @field ID ImGuiID # Init,Move -- Best candidate item ID
--- @field FocusScopeId ImGuiID # Init,Move -- Best candidate focus scope ID
--- @field RectRel ImRect # Init,Move -- Best candidate bounding box in window relative space
--- @field ItemFlags ImGuiItemFlags # ????,Move -- Best candidate item flags
--- @field SelectionUserData ImGuiSelectionUserData # I+Mov -- Best candidate SetNextItemSelectionUserData() value. Valid if (ItemFlags & ImGuiItemFlags.HasSelectionUserData)
--- @field DistBox float # Move -- Best candidate box 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
MT.ImGuiNavItemData = {}
MT.ImGuiNavItemData.__index = MT.ImGuiNavItemData
function MT.ImGuiNavItemData:Clear()
self.Window = nil
self.ID = 0
self.FocusScopeId = 0
self.ItemFlags = 0
self.SelectionUserData = (-1) --[[ImGuiSelectionUserData_Invalid]]
self.DistBox = 3.4028234663853e+38 --[[FLT_MAX]]
self.DistCenter = 3.4028234663853e+38 --[[FLT_MAX]]
self.DistAxial = 3.4028234663853e+38 --[[FLT_MAX]]
end
--- @return ImGuiNavItemData
local function ImGuiNavItemData()
local this = setmetatable({ }, MT.ImGuiNavItemData)
this.RectRel = ImRect()
this:Clear()
return this
end
-------------------------------------------------------------------------------
-- [SECTION] Typing-select support
-------------------------------------------------------------------------------
-- Flags for GetTypingSelectRequest()
--- @enum ImGuiTypingSelectFlags
ImGuiTypingSelectFlags = {
None = 0,
AllowBackspace = bitLShift(1, 0), -- Backspace to delete character inputs. If using: ensure GetTypingSelectRequest() is not called more than once per frame (filter by e.g. focus state)
AllowSingleCharMode = bitLShift(1, 1), -- Allow "single char" search mode which is activated when pressing the same character multiple times.
}
-- Returned by GetTypingSelectRequest(), designed to eventually be public.
--- @class ImGuiTypingSelectRequest
--- @field Flags ImGuiTypingSelectFlags # Flags passed to GetTypingSelectRequest()
--- @field SearchBufferLen int
--- @field SearchBuffer string # Search buffer contents (use full string. unless SingleCharMode is set, in which case use SingleCharSize).
--- @field SelectRequest bool # Set when buffer was modified this frame, requesting a selection.
--- @field SingleCharMode bool # Notify when buffer contains same character repeated, to implement special mode. In this situation it preferred to not display any on-screen search indication.
--- @field SingleCharSize int # Length in bytes of first letter codepoint (1 for ascii, 2-4 for UTF-8). If (SearchBufferLen==RepeatCharSize) only 1 letter has been input.
local function ImGuiTypingSelectRequest()
return { Flags = 0, SearchBufferLen = 0, SearchBuffer = "", SelectRequest = false, SingleCharMode = false, SingleCharSize = 0 }
end
-- Storage for GetTypingSelectRequest()
-- [Port] SearchBuffer is an immutable Lua string (C++: char[64], 63 bytes max + zero terminator). SearchBuffer[0] != 0 -> #SearchBuffer ~= 0.
--- @class ImGuiTypingSelectState
--- @field Request ImGuiTypingSelectRequest # User-facing data
--- @field SearchBuffer string # Search buffer: no need to make dynamic as this search is very transient.
--- @field FocusScope ImGuiID
--- @field LastRequestFrame int
--- @field LastRequestTime float
--- @field SingleCharModeLock bool # After a certain single char repeat count we lock into SingleCharMode. Two benefits: 1) buffer never fill, 2) we can provide an immediate SingleChar mode without timer elapsing.
MT.ImGuiTypingSelectState = {}
MT.ImGuiTypingSelectState.__index = MT.ImGuiTypingSelectState
--- @return ImGuiTypingSelectState
function ImGuiTypingSelectState()
return setmetatable({ Request = ImGuiTypingSelectRequest(), SearchBuffer = "", FocusScope = 0, LastRequestFrame = 0, LastRequestTime = 0.0, SingleCharModeLock = false }, MT.ImGuiTypingSelectState)
end
function MT.ImGuiTypingSelectState:Clear() self.SearchBuffer = ""; self.SingleCharModeLock = false end -- We preserve remaining data for easier debugging
-------------------------------------------------------------------------------
-- [SECTION] Box-select support
-------------------------------------------------------------------------------
--- @class ImGuiBoxSelectState
--- @field ID ImGuiID
--- @field IsActive bool
--- @field IsStarting bool
--- @field IsStartedFromVoid bool # Starting click was not from an item.
--- @field IsStartedSetNavIdOnce bool
--- @field RequestClear bool
--- @field KeyMods ImGuiKeyChord # Latched key-mods for box-select logic.
--- @field StartPosRel ImVec2 # Start position in window-contents relative space (to support scrolling)
--- @field EndPosRel ImVec2 # End position in window-contents relative space
--- @field ScrollAccum ImVec2 # Scrolling accumulator (to behave at high-frame spaces)
--- @field Window ImGuiWindow?
--- @field UnclipMode bool # (Temp/Transient, here in hot area). Set/cleared by the BeginMultiSelect()/EndMultiSelect() owning active box-select.
--- @field UnclipRect ImRect # Rectangle where ItemAdd() clipping may be temporarily disabled. Need support by multi-select supporting widgets.
--- @field UnclipRects ImRect[] # Per-axis versions. [Port] 1-based: [1] = upstream [0] (X), [2] = upstream [1] (Y)
--- @field BoxSelectRectPrev ImRect # Selection rectangle in absolute coordinates (derived every frame from BoxSelectStartPosRel and MousePos)
--- @field BoxSelectRectCurr ImRect
--- @return ImGuiBoxSelectState
function ImGuiBoxSelectState()
return {
-- Active box-selection data (persistent, 1 active at a time)
ID = 0, IsActive = false, IsStarting = false, IsStartedFromVoid = false, IsStartedSetNavIdOnce = false, RequestClear = false,
KeyMods = 0,
StartPosRel = ImVec2(), EndPosRel = ImVec2(), ScrollAccum = ImVec2(),
Window = nil,
-- Temporary/Transient data
UnclipMode = false,
UnclipRect = ImRect(),
UnclipRects = { ImRect(), ImRect() },
BoxSelectRectPrev = ImRect(),
BoxSelectRectCurr = ImRect(),
}
end
-------------------------------------------------------------------------------
-- [SECTION] Multi-select support
-------------------------------------------------------------------------------
-- We always assume that -1 is an invalid value (which works for indices and pointers)
-- (ImGuiSelectionUserData_Invalid is defined above with the ImGuiSelectionUserData alias)
-- Temporary storage for multi-select
--- @class ImGuiMultiSelectTempData
--- @field IO ImGuiMultiSelectIO # MUST BE FIRST FIELD. Requests are set and returned by BeginMultiSelect()/EndMultiSelect() + written to by user during the loop.
--- @field Storage ImGuiMultiSelectState?
--- @field FocusScopeId ImGuiID # Copied from g.CurrentFocusScopeId (unless another selection scope was pushed manually)
--- @field Flags ImGuiMultiSelectFlags
--- @field ScopeRectMin ImVec2
--- @field BackupCursorMaxPos ImVec2
--- @field BoxSelectId ImGuiID
--- @field KeyMods ImGuiKeyChord
--- @field LoopRequestSetAll int # -1: no operation, 0: clear all, 1: select all.
--- @field IsEndIO bool # Set when switching IO from BeginMultiSelect() to EndMultiSelect() state.
--- @field IsFocused bool # Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection.
--- @field IsKeyboardSetRange bool # Set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation.
--- @field NavIdPassedBy bool
--- @field RangeSrcPassedBy bool # Set by the item that matches RangeSrcItem.
--- @field RangeDstPassedBy bool # Set by the item that matches NavJustMovedToId when IsSetRange is set.
--- @field IsSoleOrUnknownSelectionSize bool
MT.ImGuiMultiSelectTempData = {}
MT.ImGuiMultiSelectTempData.__index = MT.ImGuiMultiSelectTempData
--- @return ImGuiMultiSelectTempData
function ImGuiMultiSelectTempData()
local this = setmetatable({ IO = ImGuiMultiSelectIO(), ScopeRectMin = ImVec2(), BackupCursorMaxPos = ImVec2() }, MT.ImGuiMultiSelectTempData)
this:Clear()
return this
end
-- Zero-clear except IO as we preserve IO.Requests[] buffer allocation.
function MT.ImGuiMultiSelectTempData:Clear()
self:ClearIO()
self.Storage = nil
self.FocusScopeId = 0
self.Flags = 0
ImVec2_CopyV(self.ScopeRectMin, 0.0, 0.0)
ImVec2_CopyV(self.BackupCursorMaxPos, 0.0, 0.0)
self.BoxSelectId = 0
self.KeyMods = 0
self.LoopRequestSetAll = 0
self.IsEndIO = false
self.IsFocused = false
self.IsKeyboardSetRange = false
self.NavIdPassedBy = false
self.RangeSrcPassedBy = false
self.RangeDstPassedBy = false
self.IsSoleOrUnknownSelectionSize = false
end
function MT.ImGuiMultiSelectTempData:ClearIO() local io = self.IO; io.Requests:resize(0); io.RangeSrcItem = (-1) --[[ImGuiSelectionUserData_Invalid]]; io.NavIdItem = (-1) --[[ImGuiSelectionUserData_Invalid]]; io.NavIdSelected = false; io.RangeSrcReset = false end
-- Persistent storage for multi-select (as long as selection is alive)
--- @class ImGuiMultiSelectState
--- @field Window ImGuiWindow?
--- @field ID ImGuiID
--- @field LastFrameActive int # Last used frame-count, for GC.
--- @field LastSelectionSize int # Set by BeginMultiSelect() based on optional info provided by user. May be -1 if unknown.
--- @field RangeSelected int # -1 (don't have) or true/false
--- @field NavIdSelected int # -1 (don't have) or true/false
--- @field RangeSrcItem ImGuiSelectionUserData #
--- @field NavIdItem ImGuiSelectionUserData # SetNextItemSelectionUserData() value for NavId (if part of submitted items)
--- @return ImGuiMultiSelectState
function ImGuiMultiSelectState()
return { Window = nil, ID = 0, LastFrameActive = 0, LastSelectionSize = 0, RangeSelected = -1, NavIdSelected = -1, RangeSrcItem = (-1) --[[ImGuiSelectionUserData_Invalid]], NavIdItem = (-1) --[[ImGuiSelectionUserData_Invalid]] }
end
--- @param id ImGuiID
--- @return ImGuiBoxSelectState?
function ImGui.GetBoxSelectState(id) local g = GImGui; if id ~= 0 and g.BoxSelectState.ID == id and g.BoxSelectState.IsActive then return g.BoxSelectState end return nil end
--- @param id ImGuiID
--- @return ImGuiMultiSelectState?
function ImGui.GetMultiSelectState(id) local g = GImGui; return g.MultiSelectStorage:GetByKey(id) end
-------------------------------------------------------------------------------
-- [SECTION] ImGuiContext (main Dear ImGui context)
-------------------------------------------------------------------------------
--- @class ImGuiContext
--- @field Initialized bool
--- @field WithinFrameScope bool # Set by NewFrame(), cleared by EndFrame()
--- @field WithinFrameScopeWithImplicitWindow bool # Set by NewFrame(), cleared by EndFrame() when the implicit debug window has been pushed
--- @field FrameCount int
--- @field FrameCountEnded int
--- @field CurrentWindow ImGuiWindow
--- @field HoveredWindow ImGuiWindow
--- @field Style ImGuiStyle
--- @field FontAtlases ImVector<ImFontAtlas>
--- @field Font ImFont
--- @field FontBaked ImFontBaked?
--- @field FontSize float
--- @field FontSizeBase float
--- @field FontBakedScale float
--- @field CurrentPixelDensity float
--- @field DrawListSharedData ImDrawListSharedData
--- @field NextItemData ImGuiNextItemData
--- @field LastItemData ImGuiLastItemData
--- @field NextWindowData ImGuiNextWindowData
--- @field WithinEndChildID ImGuiID
--- @field WithinEndPopupID ImGuiID
--- @field StyleVarStack ImVector
--- @field AnyIdHasBeenEditedThisFrame bool
--- @field ActiveId ImGuiID
--- @field ActiveIdMouseButton ImS8
--- @field Windows ImVector<ImGuiWindow>
--- @field WindowsFocusOrder ImVector<ImGuiWindow>
--- @field WindowsTempSortBuffer ImVector<ImGuiWindow>
--- @field CurrentWindowStack ImVector<ImGuiWindowStackData>
--- @field FocusScopeStack ImVector<ImGuiFocusScopeData>
--- @field ItemFlagsStack ImVector<ImGuiItemFlags>
--- @field GroupStack ImVector<ImGuiGroupData>
--- @field OpenPopupStack ImVector<ImGuiPopupData>
--- @field BeginPopupStack ImVector<ImGuiPopupData>
--- @field TreeNodeStack ImVector<ImGuiTreeNodeStackData>
--- @field IO ImGuiIO
--- @field PlatformIO ImGuiPlatformIO
--- @field Viewports ImVector<ImGuiViewportP>
--- @field DebugLogFlags ImGuiDebugLogFlags
--- @field DebugFlashStyleColorIdx ImGuiCol
--- @field ColorEditCurrentID ImGuiID # Set temporarily while inside of the parent-most ColorEdit4/ColorPicker4 (because they call each others)
--- @field ColorEditSavedID ImGuiID # ID we are saving/restoring HS for
--- @field ColorEditSavedHue float # Backup of last Hue associated to LastColor, so we can restore Hue in lossy RGB<>HSV round trips
--- @field ColorEditSavedSat float # Backup of last Saturation associated to LastColor, so we can restore Saturation in lossy RGB<>HSV round trips
--- @field ColorEditSavedColor ImU32 # RGB value with alpha set to 0
--- @field ColorPickerRef ImVec4 # Initial/reference color at the time of opening the color picker
--- @field MenusIdSubmittedThisFrame ImVector<ImGuiID>
--- @field InputTextState ImGuiInputTextState
--- @field InputTextLineIndex ImGuiTextIndex
--- @field InputTextDeactivatedState ImGuiInputTextDeactivatedState
--- @field InputTextPasswordFontBackupBaked ImFontBaked
--- @field KeysMayBeCharInput ImBitArrayForNamedKeys
--- @field KeysOwnerData ImGuiKeyOwnerData[]
--- @field KeysRoutingTable ImGuiKeyRoutingTable
--- @field NavFocusRoute ImVector<ImGuiFocusScopeData>
--- @field PlatformImeData ImGuiPlatformImeData
--- @field PlatformImeDataPrev ImGuiPlatformImeData
--- @field UserTextures ImVector<ImTextureData>
--- @field DebugMetricsConfig ImGuiMetricsConfig
--- @field DebugAllocInfo ImGuiDebugAllocInfo
--- @field TempBuffer ImVector<char>
-------------------------------------------------------------------------------
-- [SECTION] Tab bar, Tab item support
-- PORT: public flags from imgui.h + private extensions
-------------------------------------------------------------------------------
-- Flags for ImGui.BeginTabBar()
--- @enum ImGuiTabBarFlags
ImGuiTabBarFlags = {
None = 0,
Reorderable = bitLShift(1, 0), -- Allow manually dragging tabs to re-order them + New tabs are appended at the end of list
AutoSelectNewTabs = bitLShift(1, 1), -- Automatically select new tabs when they appear
TabListPopupButton = bitLShift(1, 2), -- Disable buttons to open the tab list popup
NoCloseWithMiddleMouseButton = bitLShift(1, 3), -- Disable behavior of closing tabs (that are submitted with p_open ~= nil) with middle mouse button. You may handle this behavior manually on user's side with if (IsItemHovered() && IsMouseClicked(2)) p_open = false.
NoTabListScrollingButtons = bitLShift(1, 4), -- Disable scrolling buttons (apply when fitting policy is ImGuiTabBarFlags.FittingPolicyScroll)
NoTooltip = bitLShift(1, 5), -- Disable tooltips when hovering a tab
DrawSelectedOverline = bitLShift(1, 6), -- Draw selected overline markers over selected tab
-- Fitting/Resize policy
FittingPolicyMixed = bitLShift(1, 7), -- Shrink down tabs when they don't fit, until width is style.TabMinWidthShrink, then enable scrolling. Setting TabMinWidthShrink to FLT_MAX makes this behave like ImGuiTabBarFlags.FittingPolicyScroll.
FittingPolicyShrink = bitLShift(1, 8), -- Shrink down tabs when they don't fit
FittingPolicyScroll = bitLShift(1, 9), -- Enable scrolling buttons when tabs don't fit
-- Private
DockNode = bitLShift(1, 20), -- Part of a dock node [we don't use this in the master branch but it facilitate branch syncing to keep this around]
IsFocused = bitLShift(1, 21),
SaveSettings = bitLShift(1, 22), -- FIXME: Settings are handled by the docking system, this only request the tab bar to mark settings dirty when reordering tabs
}
ImGuiTabBarFlags.FittingPolicyMask_ = bitOr(ImGuiTabBarFlags.FittingPolicyMixed, ImGuiTabBarFlags.FittingPolicyShrink, ImGuiTabBarFlags.FittingPolicyScroll)
ImGuiTabBarFlags.FittingPolicyDefault_ = ImGuiTabBarFlags.FittingPolicyMixed
ImGuiTabBarFlags.FittingPolicyResizeDown = ImGuiTabBarFlags.FittingPolicyShrink -- Renamed in 1.92.2
-- Flags for ImGui.BeginTabItem()
--- @enum ImGuiTabItemFlags
ImGuiTabItemFlags = {
None = 0,
UnsavedDocument = bitLShift(1, 0), -- Display a dot next to the title + set ImGuiTabItemFlags.NoAssumedClosure.
SetSelected = bitLShift(1, 1), -- Trigger flag to programmatically make the tab selected when calling BeginTabItem()
NoCloseWithMiddleMouseButton = bitLShift(1, 2), -- Disable behavior of closing tabs (that are submitted with p_open ~= nil) with middle mouse button. You may handle this behavior manually on user's side with if (IsItemHovered() && IsMouseClicked(2)) p_open = false.
NoPushId = bitLShift(1, 3), -- Don't call PushID()/PopID() on BeginTabItem()/EndTabItem()
NoTooltip = bitLShift(1, 4), -- Disable tooltip for the given tab
NoReorder = bitLShift(1, 5), -- Disable reordering this tab or having another tab cross over this tab
Leading = bitLShift(1, 6), -- Enforce the tab position to the left of the tab bar (after the tab list popup button)
Trailing = bitLShift(1, 7), -- Enforce the tab position to the right of the tab bar (before the scrolling buttons)
NoAssumedClosure = bitLShift(1, 8), -- Tab is selected when trying to close + closure is not immediately assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.
-- Private
NoCloseButton = bitLShift(1, 20), -- Track whether p_open was set or not (we'll need this info on the next frame to recompute ContentWidth during layout)
Button = bitLShift(1, 21), -- Used by TabItemButton, change the tab item behavior to mimic a button
Invisible = bitLShift(1, 22), -- To reserve space e.g. with ImGuiTabItemFlags.Leading
Unsorted = bitLShift(1, 23), -- [Docking] Trailing tabs with the _Unsorted flag will be sorted based on the DockOrder of their Window.
}
ImGuiTabItemFlags.SectionMask_ = bitOr(ImGuiTabItemFlags.Leading, ImGuiTabItemFlags.Trailing)
-- Storage for one active tab item. In this port the label is stored in `Name` (C++ uses NameOffset into TabBar.TabsNames)
--- @class ImGuiTabItem
--- @return ImGuiTabItem
function ImGuiTabItem()
return {
ID = 0, Flags = 0, Window = nil, -- Window: When TabItem is part of a DockNode's TabBar, we hold on to a window.
LastFrameVisible = -1, LastFrameSelected = -1, -- LastFrameSelected: This allows us to infer an ordered list of the last activated tabs with little maintenance
-- Offset: Position relative to beginning of tab bar
-- Width: Width currently displayed
-- ContentWidth: Width of label + padding, stored during BeginTabItem() call (misnamed as "Content" would normally imply width of label only)
-- RequestedWidth: Width optionally requested by caller, -1.0 is unused
Offset = 0.0, Width = 0.0, ContentWidth = 0.0, RequestedWidth = -1.0,
NameOffset = -1, Name = nil,
BeginOrder = -1, -- 0-based -- BeginTabItem() order, used to re-order tabs after toggling ImGuiTabBarFlags.Reorderable
IndexDuringLayout = -1, -- 0-based -- Index only used during TabBarLayout(). Tabs gets reordered so 'Tabs[n].IndexDuringLayout == n' but may mismatch during additions.
WantClose = false, -- Marked as closed by SetTabItemClosed()
}
end
-- Storage for a tab bar. Tabs: ImVector of ImGuiTabItem tables (references; "pointers" stay valid across reorders)
--- @class ImGuiTabBar
--- @return ImGuiTabBar
function ImGuiTabBar()
return {
Window = nil, Tabs = ImVector(), Flags = 0, ID = 0, -- ID: Zero for tab-bars used by docking
-- SelectedTabId: Selected tab/window
-- NextSelectedTabId: Next selected tab/window. Will also trigger a scrolling animation
-- VisibleTabId: Can occasionally be != SelectedTabId (e.g. when previewing contents for Ctrl+Tab preview)
SelectedTabId = 0, NextSelectedTabId = 0, NextScrollToTabId = 0, VisibleTabId = 0,
CurrFrameVisible = -1, PrevFrameVisible = -1,
BarRect = ImRect(), BarRectPrevWidth = 0.0, -- BarRectPrevWidth: Backup of previous width. When width change we enforce keep horizontal scroll on focused tab.
CurrTabsContentsHeight = 0.0, PrevTabsContentsHeight = 0.0, -- PrevTabsContentsHeight: Record the height of contents submitted below the tab bar
-- WidthAllTabs: Actual width of all tabs (locked during layout)
-- WidthAllTabsIdeal: Ideal width if all tabs were visible and not clipped
WidthAllTabs = 0.0, WidthAllTabsIdeal = 0.0,
ScrollingAnim = 0.0, ScrollingTarget = 0.0, ScrollingTargetDistToVisibility = 0.0, ScrollingSpeed = 0.0,
ScrollingRectMinX = 0.0, ScrollingRectMaxX = 0.0, SeparatorMinX = 0.0, SeparatorMaxX = 0.0,
ReorderRequestTabId = 0, ReorderRequestOffset = 0, BeginCount = 0,
WantLayout = false, VisibleTabWasSubmitted = false, TabsAddedNew = false, ScrollButtonEnabled = false, -- TabsAddedNew: Set to true when a new tab item or button has been added to the tab bar during last frame
TabsActiveCount = 0, -- Number of tabs submitted this frame.
LastTabItemIdx = -1, -- 0-based -- Index of last BeginTabItem() tab for use by EndTabItem()
ItemSpacingY = 0.0, FramePadding = ImVec2(), BackupCursorPos = ImVec2(), -- FramePadding: style.FramePadding locked at the time of BeginTabBar()
}
end
--- @class ImGuiShrinkWidthItem
function ImGuiShrinkWidthItem() return { Index = 0, Width = 0.0, InitialWidth = 0.0 } end
--- @param shared_font_atlas? ImFontAtlas
--- @return ImGuiContext
--- @nodiscard
function ImGuiContext(shared_font_atlas) -- TODO: tidy up / complete this structure
local this = {
Style = ImGuiStyle(),
ColorStack = ImVector(), -- Stack for PushStyleColor()/PopStyleColor() - inherited by Begin()
StyleVarStack = ImVector(), -- Stack for PushStyleVar()/PopStyleVar() - inherited by Begin()
Config = nil,
Initialized = false,
WithinFrameScope = false, -- Set by NewFrame(), cleared by EndFrame()
WithinFrameScopeWithImplicitWindow = false, -- Set by NewFrame(), cleared by EndFrame() when the implicit debug window has been pushed
Windows = ImVector(), -- Windows sorted in display order, back to front
WindowsById = {}, -- Map window's ID to window ref
WindowsBorderHoverPadding = 0, -- Padding around resizable windows for which hovering on counts as hovering the window == ImMax(style.TouchExtraPadding, style.WindowBorderHoverPadding). This isn't so multi-dpi friendly.
WindowsActiveCount = 0, -- Number of unique windows submitted by frame
WindowsTempSortBuffer = ImVector(), -- Temporary buffer used in EndFrame() to reorder windows so parents are kept before their child
CurrentWindowStack = ImVector(),
CurrentWindow = nil, -- Window being drawn into
WindowsFocusOrder = ImVector(), -- Root windows, sorted in focus order, back to front.
IO = ImGuiIO(),
PlatformIO = ImGuiPlatformIO(),
ConfigFlagsCurrFrame = 0 --[[ImGuiConfigFlags.None]], -- = g.IO.ConfigFlags at the time of NewFrame()
ConfigFlagsLastFrame = 0 --[[ImGuiConfigFlags.None]],
MouseLastValidPos = ImVec2(),
KeysMayBeCharInput = ImBitArray(155 --[[ImGuiKey.NamedKey_COUNT]], -512 --[[ImGuiKey.NamedKey_BEGIN]]), -- Lookup to tell if a key can emit char input, see IsKeyChordPotentiallyCharInput().
KeysOwnerData = {}, -- size = ImGuiKey.NamedKey_COUNT
KeysRoutingTable = ImGuiKeyRoutingTable(),
InputEventsQueue = ImVector(), -- Input events which will be trickled/written into IO structure.
InputEventsTrail = ImVector(), -- Past input events processed in NewFrame(). This is to allow domain-specific application to access e.g mouse/pen trail.
InputEventsNextMouseSource = 0 --[[ImGuiMouseSource.Mouse]],
InputEventsNextEventId = 1,
MovingWindow = nil, -- Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow.RootWindowDockTree.
WheelingWindow = nil, -- Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window.
WheelingWindowStartFrame = -1, WheelingWindowScrolledFrame = -1, -- This may be set one frame before WheelingWindow is ~= nil
WheelingWindowReleaseTimer = 0.0,
WheelingWindowRefMousePos = ImVec2(),
WheelingWindowWheelRemainder = ImVec2(),
WheelingAxisAvg = ImVec2(),
ActiveIdClickOffset = ImVec2(), -- Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior)
HoveredWindow = nil, -- Window the mouse is hovering. Will typically catch mouse inputs.
HoveredWindowUnderMovingWindow = nil, -- Hovered window ignoring MovingWindow. Only set if MovingWindow is set.
HoveredIdIsDisabled = false, -- At least one widget passed the rect test, but has been discarded by disabled flag or popup inhibit. May be true even if HoveredId == 0.
ActiveIdFromShortcut = false,
AnyIdHasBeenEditedThisFrame = false,
ActiveId = 0, -- Active widget
ActiveIdWindow = nil,
ActiveIdMouseButton = -1,
ActiveIdIsJustActivated = false, -- Set at the time of activation for one frame
ActiveIdWasSelected = false, ActiveIdWasSoleSelected = false, -- Active ID was selected at the time of activating
LastActiveIdWasSelected = false, LastActiveIdWasSoleSelected = false,
ActiveIdNoClearOnFocusLoss = false, -- Disable losing active id if the active id window gets unfocused.
ActiveIdHasBeenPressedBefore = false, -- Track whether the active id led to a press (this is to allow changing between PressOnClick and PressOnRelease without pressing twice). Used by range_select branch.
ActiveIdIsAlive = nil, -- Active widget has been seen this frame (we can't use a bool as the ActiveId may change within the frame)
ActiveIdPreviousFrame = 0,
ActiveIdTimer = 0.0,
ActiveIdSource = 0 --[[ImGuiInputSource.None]], -- Activating source: ImGuiInputSource.Mouse OR ImGuiInputSource.Keyboard OR ImGuiInputSource.Gamepad
LastActiveId = 0, -- Store the last non-zero ActiveId, useful for animation.
LastActiveIdTimer = 0.0, -- Store the last non-zero ActiveId timer since the beginning of activation, useful for animation.
ActiveIdUsingNavDirMask = 0x00, -- Active widget will want to read those nav move requests (e.g. can activate a button and move away from it)
ActiveIdUsingAllKeyboardKeys = false, -- Active widget will want to read all keyboard keys inputs. (this is a shortcut for not taking ownership of 100+ keys, frequently used by drag operations)
DeactivatedItemData = {
ID = 0,
ElapseFrame = 0,
HasBeenEditedBefore = false,
IsAlive = false
},
HoveredId = 0, -- Hovered widget, filled during the frame
HoveredIdTimer = 0.0, -- Measure contiguous hovering time
HoveredIdNotActiveTimer = 0.0, -- Measure contiguous hovering time where the item has not been active
HoveredIdAllowOverlap = false,
HoverItemDelayId = 0, HoverItemDelayIdPreviousFrame = 0, HoverItemUnlockedStationaryId = 0, HoverWindowUnlockedStationaryId = 0,
HoverItemDelayTimer = 0.0, HoverItemDelayClearTimer = 0.0, -- Currently used by IsItemHovered()
ActiveIdAllowOverlap = false, -- Active widget allows another widget to steal active id (generally for overlapping widgets, but not always)
NavLayer = 0 --[[ImGuiNavLayer.Main]], -- Focused layer (main scrolling layer, or menu/title bar layer)
NavId = 0, -- Focused item for navigation
NavOpenContextMenuItemId = 0, NavOpenContextMenuWindowId = 0,
NavWindow = nil, -- Focused window for navigation. Could be called 'FocusedWindow'
NavHighlightActivatedId = 0,
NavHighlightActivatedTimer = 0.0,
NavCursorVisible = false, -- Nav focus cursor/rectangle is visible? We hide it after a mouse click. We show it after a nav move.
NavCursorHideFrames = 0,
NavHighlightItemUnderNav = false, -- Disable mouse hovering highlight. Highlight navigation focused item instead of mouse hovered item.
NavIdIsAlive = false, -- Nav widget has been seen this frame ~~ NavRectRel is valid
NavInputSource = 2 --[[ImGuiInputSource.Keyboard]], -- Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource.Keyboard or ImGuiInputSource.Gamepad
NavLastValidSelectionUserData = (-1) --[[ImGuiSelectionUserData_Invalid]], -- Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data.
NavWindowingTarget = nil, NavWindowingTargetAnim = nil, NavWindowingListWindow = nil, -- Target window when doing Ctrl+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most!
NavWindowingTimer = 0.0, NavWindowingHighlightAlpha = 0.0,
NavWindowingToggleLayer = false, -- Set while Alt or GamepadMenu is held, may be cleared by other operations, and processed when releasing the key.
NavWindowingToggleKey = 0 --[[ImGuiKey.None]], -- Keyboard/gamepad key used when toggling to menu layer.
NavWindowingInputSource = 0 --[[ImGuiInputSource.None]],
NavWindowingAccumDeltaPos = ImVec2(0.0, 0.0), NavWindowingAccumDeltaSize = ImVec2(0.0, 0.0),
DimBgRatio = 0.0, -- 0.0..1.0 animation when fading in a dimming background (for modal window and Ctrl+Tab list)
LastKeyboardKeyPressTime = -1.0, LastKeyModsChangeTime = -1.0, LastKeyModsChangeFromNoneTime = -1.0, -- Record the last time a keyboard key (ignore mouse/gamepad ones) was pressed.
ConfigNavWindowingKeyNext = bit32.bor(4096 --[[ImGuiMod_Ctrl]], 512 --[[ImGuiKey.Tab]]), -- = ImGuiMod_Ctrl | ImGuiKey.Tab (or ImGuiMod_Super | ImGuiKey.Tab on OS X). Set to 0 to disable. For reconfiguration (see #4828)
ConfigNavWindowingKeyPrev = bit32.bor(4096 --[[ImGuiMod_Ctrl]], 8192 --[[ImGuiMod_Shift]], 512 --[[ImGuiKey.Tab]]), -- = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey.Tab (or ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey.Tab on OS X)
ConfigNavWindowingWithGamepad = true, -- = true. Enable Ctrl+Tab by holding ImGuiKey.GamepadFaceLeft (== ImGuiKey.NavGamepadMenu). When false, the button may still be used to toggle Menu layer.
NavFocusRoute = ImVector(), -- Reversed copy focus scope stack for NavId (should contains NavFocusScopeId). This essentially follow the window.ParentWindowForFocusRoute chain.
NavAnyRequest = false, -- ~~ NavMoveRequest || NavInitRequest this is to perform early out in ItemAdd()
NavInitRequest = false, -- Init request for appearing window to select first item
NavInitRequestFromMove = false,
NavInitResult = ImGuiNavItemData(), -- Init request result (first item of the window, or one for which SetItemDefaultFocus() was called)
NavMoveSubmitted = false, -- Move request submitted, will process result on next NewFrame()
NavMoveScoringItems = false, -- Move request submitted, still scoring incoming items
NavMoveForwardToNextFrame = false,
NavMoveFlags = 0 --[[ImGuiNavMoveFlags.None]],
NavMoveScrollFlags = 0 --[[ImGuiScrollFlags.None]],
NavMoveKeyMods = 0 --[[ImGuiMod_None]],
NavMoveDir = (-1) --[[ImGuiDir.None]], NavMoveDirForDebug = (-1) --[[ImGuiDir.None]], NavMoveClipDir = (-1) --[[ImGuiDir.None]], -- Direction of the move request (left/right/up/down)
NavMoveResultLocal = ImGuiNavItemData(), -- Best move request candidate within NavWindow
NavMoveResultOther = ImGuiNavItemData(), -- Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags.NavFlattened flag)
NavMoveResultLocalVisible = ImGuiNavItemData(), -- Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags.AlsoScoreVisibleSet flag)
NavTabbingResultFirst = ImGuiNavItemData(), -- First tabbing request candidate within NavWindow and flattened hierarchy
NavScoringRect = ImRect(), -- Rectangle used for scoring, in screen space. Based of window.NavRectRel[], modified for directional navigation scoring.
NavScoringNoClipRect = ImRect(), -- Some nav operations (such as PageUp/PageDown) enforce a region which clipper will attempt to always keep submitted. Unset/invalid if inverted.
ConfigNavEnableTabbing = true, -- = true. Enable tabbing (Tab, Shift+Tab). PLEASE LET ME KNOW IF YOU USE THIS.
NavTabbingCounter = 0, -- >0 when counting items for tabbing
NavTabbingDir = 0, -- Generally -1 or +1, 0 when tabbing without a nav id
NavNextActivateId = 0, -- Set by ActivateItemByID(), queued until next frame.
NavNextActivateFlags = 0 --[[ImGuiActivateFlags.None]],
NavActivateId = 0, NavActivateDownId = 0, NavActivatePressedId = 0, -- ~~ (g.ActiveId == 0) && (IsKeyPressed(ImGuiKey.Space) || IsKeyDown(ImGuiKey.Enter) || IsKeyPressed(ImGuiKey.NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItemByID()
NavActivateFlags = 0 --[[ImGuiActivateFlags.None]],
-- Navigation: record of last move request
NavJustMovedFromFocusScopeId = 0, -- Just navigated from this focus scope id (result of a successfully MoveRequest).
NavJustMovedToId = 0, -- Just navigated to this id (result of a successfully MoveRequest).
NavJustMovedToFocusScopeId = 0, -- Just navigated to this focus scope id (result of a successfully MoveRequest).
NavJustMovedToKeyMods = 0,
NavJustMovedToIsTabbing = false, -- Copy of ImGuiNavMoveFlags.IsTabbing. Maybe we should store whole flags.
NavJustMovedToIsInit = false, -- Copy of ImGuiNavMoveFlags.IsInit. Maybe we should store whole flags.
NavJustMovedToHasSelectionData = false, -- Copy of move result's ItemFlags & ImGuiItemFlags.HasSelectionUserData). Maybe we should just store ImGuiNavItemData.
FrameCount = 0,
FrameCountEnded = -1,
FrameCountPlatformEnded = -1,
FrameCountRendered = -1,
Time = 0,
NextItemData = ImGuiNextItemData(), -- Storage for SetNextItem** functions
LastItemData = ImGuiLastItemData(), -- Storage for last submitted item (setup by ItemAdd)
NextWindowData = ImGuiNextWindowData(), -- Storage for SetNextWindow** functions
Viewports = ImVector(), -- Active viewports (always 1+, and generally 1 unless multi-viewports are enabled). Each viewports hold their copy of ImDrawData.
FallbackMonitor = nil, -- Virtual monitor used as fallback if backend doesn't provide monitor information.
GcCompactAll = false, -- Request full GC
CurrentViewport = nil,
MouseViewport = nil, MouseLastHoveredViewport = nil,
PlatformLastFocusedViewportId = 1,
ViewportCreatedCount = 0, PlatformWindowsCreatedCount = 0, -- Unique sequential creation counter (mostly for testing/debugging)
ViewportFocusedStampCount = 0, -- Every time the front-most window changes, we stamp its viewport with an incrementing counter
Font = nil, -- Currently bound font. (== FontStack.back().Font)
FontSize = 0.0, -- Currently bound font size == line height (== FontSizeBase + externals scales applied in the UpdateCurrentFontSize() function).
FontSizeBase = 0.0, -- Font size before scaling == style.FontSizeBase == value passed to PushFont() when specified.
CurrentDpiScale = 0.0, -- Current window/viewport DpiScale == CurrentViewport.DpiScale
FontRefSize = 0.0,
CurrentPixelDensity = 1.0,
FontAtlases = ImVector(), -- List of font atlases used by the context (generally only contains g.IO.Fonts aka the main font atlas)
FontStack = ImVector(), -- Stack for PushFont()/PopFont() - inherited by Begin()
OpenPopupStack = ImVector(), -- Which popups are open (persistent)
BeginPopupStack = ImVector(), -- Which level of BeginPopup() we are in (reset every frame)
TreeNodeStack = ImVector(ImGuiTreeNodeStackData),
WithinEndChildID = 0, WithinEndPopupID = 0, -- Set within EndChild()
BeginMenuDepth = 0, BeginComboDepth = 0,
ColorEditCurrentID = 0, ColorEditSavedID = 0, -- Set temporarily while inside of the parent-most ColorEdit4/ColorPicker4 (because they call each others).
ColorEditSavedHue = 0.0, ColorEditSavedSat = 0.0, -- Backup of last Hue associated to LastColor, so we can restore Hue in lossy RGB<>HSV round trips
ColorEditSavedColor = 0, -- RGB value with alpha set to 0.
ColorPickerRef = ImVec4(), -- Initial/reference color at the time of opening the color picker.
MenusIdSubmittedThisFrame = ImVector(), -- A list of menu IDs that were rendered at least once
DrawListSharedData = ImDrawListSharedData(),
StackSizesInBeginForCurrentWindow = nil,
MouseCursor = 0 --[[ImGuiMouseCursor.Arrow]],
MouseStationaryTimer = 0.0, -- Time the mouse has been stationary (with some loose heuristic)
TypingSelectState = ImGuiTypingSelectState(), -- State for GetTypingSelectRequest()
InputTextState = ImGuiInputTextState(),
InputTextLineIndex = ImGuiTextIndex(), -- Temporary storage
InputTextDeactivatedState = ImGuiInputTextDeactivatedState(),
InputTextPasswordFontBackupBaked = ImFontBaked(),
InputTextReactivateId = 0, -- ID of InputText to reactivate on next frame (for io.ConfigInputTextEnterKeepActive behavior)
TempInputId = 0, -- Temporary text input when using Ctrl+Click on a slider, etc.
ActiveIdValueOnActivation = 0, -- Backup of initial value at the time of activation. ONLY SET BY SPECIFIC WIDGETS: DragXXX and SliderXXX.
DataTypeZeroValue = 0, -- 0 for all data types
ComboPreviewData = ImGuiComboPreviewData(),
CurrentTabBar = nil,
TabBars = ImPool(ImGuiTabBar), -- ImPool<ImGuiTabBar>
-- Docking
DockContext = ImGuiDockContext(),
DockNodeWindowMenuHandler = nil,
DebugHoveredDockNode = nil, -- Hovered dock node.
CurrentTabBarStack = ImVector(), -- ImVector<ImGuiPtrOrIndex>
ShrinkWidthBuffer = ImVector(), -- ImGuiShrinkWidthItem
-- Multi-Select state
BoxSelectState = ImGuiBoxSelectState(),
CurrentMultiSelect = nil,
MultiSelectTempDataStacked = 0, -- Temporary multi-select data size (because we leave previous instances undestructed, we generally don't use MultiSelectTempData.Size)
MultiSelectTempData = ImVector(), -- ImVector<ImGuiMultiSelectTempData>, 1-based
MultiSelectStorage = ImPool(ImGuiMultiSelectState), -- ImPool<ImGuiMultiSelectState>
WindowResizeBorderExpectedRect = ImRect(), -- Expected border rect, switch to relative edit if moving
WindowResizeRelativeMode = false,
ScrollbarSeekMode = 0, -- 0: scroll to clicked location, -1/+1: prev/next page.
ScrollbarClickDeltaToGrabCenter = 0.0, -- When scrolling to mouse location: distance between mouse and center of grab box, normalized in parent space.
SliderGrabClickOffset = 0.0,
SliderCurrentAccum = 0.0, -- Accumulated slider delta when using navigation controls.
SliderCurrentAccumDirty = false, -- Has the accumulated slider delta changed since last time we tried to apply it?
DragCurrentAccumDirty = false,
DragCurrentAccum = 0.0, -- Accumulator for dragging modification. Always high-precision, not rounded by end-user precision settings
DragSpeedDefaultRatio = 1.0 / 100.0, -- If speed == 0.0, uses (max-min) * DragSpeedDefaultRatio
TooltipOverrideCount = 0,
TooltipPreviousWindow = nil, -- Window of last tooltip submitted during the frame
FocusScopeStack = ImVector(), -- Stack for PushFocusScope()/PopFocusScope() - inherited by BeginChild(), pushed into by Begin()
CurrentFocusScopeId = 0, -- Value for currently appending items == g.FocusScopeStack.back(). Not to be mistaken with g.NavFocusScopeId.
CurrentItemFlags = 0 --[[ImGuiItemFlags.None]], -- Value for currently appending items == g.ItemFlagsStack.back()
DisabledAlphaBackup = 0.0, -- Backup for style.Alpha for BeginDisabled()
DisabledStackSize = 0,
MixedValueLabel = "-", -- Value replacement when displaying a mixed value.
ItemFlagsStack = ImVector(), -- Stack for PushItemFlag()/PopItemFlag() - inherited by Begin()
GroupStack = ImVector(), -- Stack for BeginGroup()/EndGroup() - not inherited by Begin()
PlatformImeData = ImGuiPlatformImeData(), -- Data updated by current frame. Will be applied at end of the frame. For some backends, this is required to have WantVisible=true in order to receive text message.
PlatformImeDataPrev = ImGuiPlatformImeData(), -- Previous frame data. When changed we call the platform_io.Platform_SetImeDataFn() handler.
-- Extensions
UserTextures = ImVector(), -- List of textures created/managed by user or third-party extension. Automatically appended into platform_io.Textures[].
-- Settings
SettingsLoaded = false,
SettingsDirtyTimer = 0.0, -- Save .ini Settings to memory when time reaches zero
SettingsIniData = "", -- In memory .ini settings (string)
SettingsHandlers = ImVector(), -- List of .ini settings handlers
SettingsWindows = ImVector(), -- ImGuiWindow .ini settings entries
SettingsTables = ImVector(), -- ImGuiTable .ini settings entries
-- Hooks
Hooks = ImVector(), -- Hooks for extensions (e.g. test engine)
HookIdNext = 0, -- Next available HookId
SessionDate = ImGuiPackedDate(), -- Packed copy of platform_io.Platform_SessionDate, when valid.
DemoMarkerCallback = nil, -- [Luau] fun(file, line, section)
-- Drag and Drop
DragDropActive = false,
DragDropWithinSource = false, -- Set when within a BeginDragDropXXX/EndDragDropXXX block for a drag source.
DragDropWithinTarget = false, -- Set when within a BeginDragDropXXX/EndDragDropXXX block for a drag target.
DragDropSourceFlags = 0,
DragDropSourceFrameCount = -1,
DragDropMouseButton = -1,
DragDropPayload = ImGuiPayload(),
DragDropTargetRect = ImRect(), -- Store rectangle of current target candidate (we favor small targets when overlapping)
DragDropTargetClipRect = ImRect(), -- Store ClipRect at the time of item's drawing
DragDropTargetId = 0,
DragDropTargetFullViewport = 0,
DragDropAcceptFlagsCurr = 0, DragDropAcceptFlagsPrev = 0,
DragDropAcceptIdCurrRectSurface = 0.0, -- Target item surface (we resolve overlapping targets by prioritizing the smaller surface)
DragDropAcceptFrameCount = -1, -- Last time a target expressed a desire to accept the source
DragDropHoldJustPressedId = 0, -- Set when holding a payload just made ButtonBehavior() return a press.
DragDropAcceptIdPrev = 0, DragDropAcceptIdCurr = 0, -- Target item id from previous frame (we need to store this to allow for overlapping drag and drop targets)
-- Clipper
ClipperTempDataStacked = 0,
ClipperTempData = ImVector(),
-- Logging
LogEnabled = false, -- Currently capturing
LogLineFirstItem = false,
LogFlags = 0, -- Capture flags/type
LogWindow = nil,
LogFile = nil, -- If ~= nil log to stdout/ file
LogBuffer = ImGuiTextBuffer(), -- Accumulation buffer when log to clipboard.
LogNextPrefix = nil, LogNextSuffix = nil, -- See comment in LogSetNextTextDecoration(): doesn't copy underlying data, use carefully!
LogLinePosY = 3.4028234663853e+38 --[[FLT_MAX]],
LogDepthRef = 0,
LogDepthToExpand = 2, LogDepthToExpandDefault = 2,
-- Error handling
ErrorCallback = nil, ErrorCallbackUserData = nil, -- = nil. May be exposed in public API eventually.
ErrorTooltipLockedPos = ImVec2(),
ErrorFirst = true,
ErrorCountCurrentFrame = 0, -- [Internal] Number of errors submitted this frame.
StackSizesInNewFrame = nil, -- [Internal]
ContextName = "", -- Storage for a context name (to facilitate debugging multi-context setups)
DebugBreakInWindow = 0, -- Set to break in Begin() call.
LocalizationTable = {},
DebugLogBuf = ImGuiTextBuffer(),
DebugLogLines = { "" }, -- [Luau] upstream DebugLogIndex: Lua array of complete lines (without '\n'), last one being filled
DebugLogFlags = bitOr(1 --[[ImGuiDebugLogFlags.EventError]], 1048576 --[[ImGuiDebugLogFlags.OutputToTTY]]),
DebugFlashStyleColorIdx = 63 --[[ImGuiCol.COUNT]], -- (Keep close to ColorStack to share cache line)
DebugBeginReturnValueCullDepth = -1, -- Cycle between 0..9 then wrap around.
DebugLocateId = 0, -- Storage for DebugLocateItemOnHover() feature: this is read by ItemAdd() so we keep it in a hot/cached location
DebugLogAutoDisableFlags = 0 --[[ImGuiDebugLogFlags.None]],
DebugLogAutoDisableFrames = 0,
DebugLocateFrames = 0, -- For DebugLocateItemOnHover(). This is used together with DebugLocateId which is in a hot/cached spot above.
DebugItemPickerActive = false, -- Item picker is active (started with DebugStartItemPicker())
DebugItemPickerMouseButton = 0 --[[ImGuiMouseButton.Left]],
DebugItemPickerBreakId = 0, -- Will call IM_DEBUG_BREAK() when encountering this ID
DebugFlashStyleColorTime = 0.0,
DebugFlashStyleColorBackup = ImVec4(0, 0, 0, 0),
-- Same as DebugBreakClearData(). Those fields are scattered in their respective subsystem to stay in hot-data locations
DebugBreakInLocateId = false, -- Debug break in ItemAdd() call for g.DebugLocateId.
DebugBreakKeyChord = 611 --[[ImGuiKey.Pause]], -- = ImGuiKey_Pause
DebugBreakInShortcutRouting = 0 --[[ImGuiKey.None]], -- Set to break in SetShortcutRouting()/Shortcut() calls.
DebugBreakCallback = nil, -- [Luau] fun(ctx): called by IM_DEBUG_BREAK() (Lua can't break into a debugger)
DebugMetricsConfig = ImGuiMetricsConfig(),
DebugItemPathQuery = ImGuiDebugItemPathQuery(),
DebugIDStackTool = ImGuiIDStackTool(),
DebugHookIdInfoId = 0, -- Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool
DebugAllocInfo = ImGuiDebugAllocInfo(),
--- Misc
FramerateSecPerFrame = {}, -- size = 60
FramerateSecPerFrameIdx = 0,
FramerateSecPerFrameCount = 0,
FramerateSecPerFrameAccum = 0,
WantCaptureMouseNextFrame = -1, -- Explicit capture override via SetNextFrameWantCaptureMouse()/SetNextFrameWantCaptureKeyboard(). Default to -1.
WantCaptureKeyboardNextFrame = -1, -- "
WantTextInputNextFrame = -1, -- Copied in EndFrame() from g.PlatformImeData.WantTextInput. Needs to be set for some backends (SDL3) to emit character inputs.
TempBuffer = ImVector(), -- Temporary text buffer
-- Tables (imgui_tables.lua)
CurrentTable = nil,
DebugBreakInTable = 0, -- Set to break in BeginTable() call.
TablesTempDataStacked = 0, -- Temporary table data size (because we leave previous instances undestructed, we generally don't use TablesTempData.Size)
TablesTempData = ImVector(), -- Temporary table data (buffers reused/shared across instances, support nesting)
Tables = ImPool(), -- ImPool<ImGuiTable>, 0-based indices (imgui_tables.lua)
TablesLastTimeActive = ImVector(), -- [table_idx + 1] = float
DrawChannelsTempMergeBuffer = ImVector(),
SettingsTables = ImVector(), -- ImChunkStream<ImGuiTableSettings>: plain vector, SettingsOffset = 1-based index
}
for i = 0, 59 do this.FramerateSecPerFrame[i] = 0 end
this.IO.Fonts = (shared_font_atlas ~= nil) and shared_font_atlas or ImFontAtlas()
if shared_font_atlas == nil then
this.IO.Fonts.OwnerContext = this
end
for i = 0, 155 --[[ImGuiKey.NamedKey_COUNT]] - 1 do
this.KeysOwnerData[i] = ImGuiKeyOwnerData()
end
this.IO.Ctx = this
this.InputTextState.Ctx = this
ImVec2_Copy(this.PlatformImeDataPrev.InputPos, ImVec2(-1.0, -1.0))
return this
end
-------------------------------------------------------------------------------
-- [SECTION] Settings support
-------------------------------------------------------------------------------
-- Windows data saved in imgui.ini file
--- @class ImGuiWindowSettings
--- @field ID int # Window ID
--- @field Pos ImVec2 # Window position
--- @field Size ImVec2 # Window size
--- @field Collapsed bool # Whether window is collapsed
--- @field IsChild bool # Whether window is a child window
--- @field WantApply bool # Set when loaded from .ini data
--- @field WantDelete bool # Set to invalidate/delete the settings entry
--- @field Name string # Window name
MT.ImGuiWindowSettings = {}
MT.ImGuiWindowSettings.__index = MT.ImGuiWindowSettings
function MT.ImGuiWindowSettings:GetName()
return self.Name
end
function ImGuiWindowSettings()
return setmetatable({
ID = 0,
Pos = ImVec2(0, 0), -- NB: Settings position are stored RELATIVE to the viewport! Whereas runtime ones are absolute positions.
Size = ImVec2(0, 0),
Collapsed = false,
IsChild = false,
WantApply = false, -- Set when loaded from .ini data (to enable merging/loading .ini data into an already running context)
WantDelete = false, -- Set to invalidate/delete the settings entry
Name = "",
ViewportId = 0,
ViewportPos = ImVec2(0, 0),
DockId = 0, -- ID of last known DockNode (even if the DockNode is invisible because it has only 1 active window), or 0 if none.
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
LastUsedDate = 0, -- [Luau] stored unpacked (YYYYMMDD int, 0 = invalid) instead of ImGuiPackedDate
}, MT.ImGuiWindowSettings)
end
--- @class ImGuiSettingsCleanupArgs
function ImGuiSettingsCleanupArgs()
return {
TypeHashFilter = 0, -- Set to restrict cleanup to a given .ini type, e.g. == ImHashStr("Window"), ImHashStr("Table"). Otherwise every types supporting Cleanup will be affected.
DiscardOlderThanMonths = 0, -- Enable to discard entries older than XX months.
DiscardWhenMissingDate = false, -- Enable to discard entries missing a date.
DiscardAll = false, -- Enable to discard all entries = same as calling ClearIniSettings() except it may be filtered.
SetCurrentSessionDateToAll = false, -- Enable to write current SessionDate to all supporting entries. // Let us know in #9460 if you use this.
SetCurrentSessionDateWhenMissingDate = false, -- Enable to write current SessionDate to all supporting entries missing a date. // Let us know in #9460 if you use this.
_DiscardOlderThanDate = 0, -- [Internal]
}
end
-- Simple column measurement, currently used for MenuItem() only.. This is very short-sighted/throw-away code and NOT a generic helper.
--- @class ImGuiMenuColumns
--- @field TotalWidth ImU32
--- @field NextTotalWidth ImU32
--- @field Spacing ImU16
--- @field OffsetIcon ImU16 # Always zero for now
--- @field OffsetLabel ImU16 # Offsets are locked in Update()
--- @field OffsetShortcut ImU16
--- @field OffsetMark ImU16
--- @field Widths ImU16[] # Width of: Icon, Label, Shortcut, Mark (accumulators for current frame)
MT.ImGuiMenuColumns = {}
MT.ImGuiMenuColumns.__index = MT.ImGuiMenuColumns
--- @return ImGuiMenuColumns
local function ImGuiMenuColumns()
return setmetatable({
TotalWidth = 0,
NextTotalWidth = 0,
Spacing = 0,
OffsetIcon = 0, -- Always zero for now
OffsetLabel = 0, -- Offsets are locked in Update()
OffsetShortcut = 0,
OffsetMark = 0,
Widths = {0, 0, 0, 0} -- Width of: Icon, Label, Shortcut, Mark (accumulators for current frame)
}, MT.ImGuiMenuColumns)
end
-------------------------------------------------------------------------------
-- [SECTION] ImGuiWindowTempData, ImGuiWindow
-------------------------------------------------------------------------------
-- Transient per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the DC variable name in ImGuiWindow.
-- (That's theory, in practice the delimitation between ImGuiWindow and ImGuiWindowTempData is quite tenuous and could be reconsidered..)
--- @class ImGuiWindowTempData
--- @field CursorPos ImVec2
--- @field CursorPosPrevLine ImVec2
--- @field CursorStartPos ImVec2
--- @field CursorMaxPos ImVec2
--- @field IdealMaxPos ImVec2
--- @field CurrLineSize ImVec2
--- @field PrevLineSize ImVec2
--- @field CurrLineTextBaseOffset float
--- @field PrevLineTextBaseOffset float
--- @field IsSameLine bool
--- @field IsSetPos bool
--- @field Indent ImVec1
--- @field ColumnsOffset ImVec1
--- @field GroupOffset ImVec1
--- @field CursorStartPosLossyness ImVec2
--- @field TextWrapPos float
--- @field TextWrapPosStack ImVector
--- @field MenuBarAppending bool
--- @field MenuBarOffset ImVec2
--- @field MenuColumns ImGuiMenuColumns
--- @field TreeDepth int
--- @field TreeHasStackDataDepthMask ImU32
--- @field TreeRecordsClippedNodesY2Mask ImU32
--- @field ChildWindows ImVector<ImGuiWindow>
--- @field LayoutType ImGuiLayoutType
--- @return ImGuiWindowTempData
--- @nodiscard
local function ImGuiWindowTempData()
return {
DockTabItemStatusFlags = 0,
DockTabItemRect = ImRect(),
CursorPos = ImVec2(), -- Current emitting position, in absolute coordinates.
CursorPosPrevLine = ImVec2(),
CursorStartPos = ImVec2(), -- Initial position after Begin(), generally ~ window position + WindowPadding.
CursorMaxPos = ImVec2(), -- Used to implicitly calculate ContentSize at the beginning of next frame, for scrolling range and auto-resize. Always growing during the frame.
IdealMaxPos = ImVec2(), -- Used to implicitly calculate ContentSizeIdeal at the beginning of next frame, for auto-resize only. Always growing during the frame.
CurrLineSize = ImVec2(),
PrevLineSize = ImVec2(),
CurrLineTextBaseOffset = 0, -- Baseline offset (0.0 by default on a new line, generally == style.FramePadding.y when a framed item has been added).
PrevLineTextBaseOffset = 0,
IsSameLine = false,
IsSetPos = false,
NavLayersActiveMask = 0, NavLayersActiveMaskNext = 0, -- Which layers have been written to (result from previous frame)
Indent = ImVec1(), -- Indentation / start position from left of window (increased by TreePush/TreePop, etc.)
ColumnsOffset = ImVec1(), -- Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree.Column.Tree. Need revamp columns API.
GroupOffset = ImVec1(),
CursorStartPosLossyness = ImVec2(), -- Record the loss of precision of CursorStartPos due to really large scrolling amount. This is used by clipper to compensate and fix the most common use case of large scroll area.
ItemWidth = 0, -- Current item width (>0.0: width in pixels, <0.0: align xx pixels to the right of window).
ItemWidthDefault = 0,
TextWrapPos = 0, -- Current text wrap pos.
ItemWidthStack = ImVector(), -- Store item widths to restore (attention: .back() is not == ItemWidth)
TextWrapPosStack = ImVector(), -- Store text wrap pos to restore (attention: .back() is not == TextWrapPos)
MenuBarAppending = false, -- FIXME: Remove this
MenuBarOffset = ImVec2(), -- MenuBarOffset.x is sort of equivalent of a per-layer CursorPos.x, saved/restored as we switch to the menu bar. The only situation when MenuBarOffset.y is > 0 if when (SafeAreaPadding.y > FramePadding.y), often used on TVs.
MenuColumns = ImGuiMenuColumns(), -- Simplified columns storage for menu items measurement
TreeDepth = 0, -- Current tree depth.
TreeHasStackDataDepthMask = nil, -- Store whether given depth has ImGuiTreeNodeStackData data. Could be turned into a ImU64 if necessary. Sync any changes with TREE_NODE_MAX_DEPTH/TREE_NODE_GET_DEPTH_MASK!
TreeRecordsClippedNodesY2Mask = nil, -- Store whether we should keep recording Y2. Cleared when passing clip max. Equivalent TreeHasStackDataDepthMask value should always be set.
ChildWindows = ImVector(),
CurrentTableIdx = -1, -- 0-based index into g.Tables, -1 if none
CurrentColumns = nil, -- ImGuiOldColumns (legacy Columns API)
StateStorage = nil, -- Current persistent per-window storage (store e.g. tree node open/close state)
LayoutType = nil
}
end
-- Storage for one window
--- @class ImGuiWindow
--- @field Ctx ImGuiContext
--- @field Name string
--- @field ID ImGuiID
--- @field Flags ImGuiWindowFlags
--- @field FlagsPreviousFrame ImGuiWindowFlags
--- @field ChildFlags ImGuiChildFlags
--- @field WindowClass ImGuiWindowClass
--- @field Viewport ImGuiViewportP
--- @field ViewportId ImGuiID
--- @field ViewportPos ImVec2
--- @field ViewportAllowPlatformMonitorExtend int
--- @field Pos ImVec2
--- @field Size ImVec2
--- @field SizeFull ImVec2
--- @field ContentSize ImVec2
--- @field ContentSizeIdeal ImVec2
--- @field ContentSizeExplicit ImVec2
--- @field Scroll ImVec2
--- @field IsExplicitChild bool
--- @field FocusOrder short # 1-based, order within WindowsFocusOrder, altered when windows are focused. Can be -1 if invalid
--- @field IDStack ImVector<ImGuiID>
--- @field DC ImGuiWindowTempData
--- @field OuterRectClipped ImRect
--- @field InnerRect ImRect
--- @field InnerClipRect ImRect
--- @field WorkRect ImRect
--- @field ParentWorkRect ImRect
--- @field ClipRect ImRect
--- @field ContentRegionRect ImRect
--- @field DrawList ImDrawList # Points to DrawListInst
--- @field DrawListInst ImDrawList
--- @field ParentWindow? ImGuiWindow
--- @field RootWindow ImGuiWindow
--- @field RootWindowPopupTree ImGuiWindow
--- @field RootWindowDockTree ImGuiWindow
--- @field StateStorage {[ImGuiID]: bool}
--- @field MemoryDrawListIdxCapacity int
--- @field MemoryDrawListVtxCapacity int
--- @field MemoryCompacted bool
MT.ImGuiWindow = {}
MT.ImGuiWindow.__index = MT.ImGuiWindow
-- We don't use g.FontSize because the window may be != g.CurrentWindow.
--- @return ImRect
--- @nodiscard
function MT.ImGuiWindow:Rect()
return ImRect(self.Pos.x, self.Pos.y, self.Pos.x + self.Size.x, self.Pos.y + self.Size.y)
end
--- @return ImRect
--- @nodiscard
function MT.ImGuiWindow:TitleBarRect()
return ImRect(self.Pos, ImVec2(self.Pos.x + self.SizeFull.x, self.Pos.y + self.TitleBarHeight))
end
--- @return ImRect
--- @nodiscard
function MT.ImGuiWindow:MenuBarRect()
local y1 = self.Pos.y + self.TitleBarHeight
return ImRect(self.Pos.x, y1, self.Pos.x + self.SizeFull.x, y1 + self.MenuBarHeight)
end
--- @return ImGuiWindow
--- @nodiscard
function ImGuiWindow(ctx, name)
local this = {
ID = 0, -- == ImHashStr(Name)
MoveId = 0, -- == window.GetID("#MOVE")
Ctx = ctx, -- Parent UI context (needs to be set explicitly by parent).
Name = name, -- Window name
Flags = 0,
ChildFlags = 0, -- Set when window is a child window. See enum ImGuiChildFlags
WindowClass = ImGuiWindowClass(), -- Advanced users only. Set with SetNextWindowClass()
Pos = ImVec2(), -- Position (always rounded-up to nearest pixel)
Size = ImVec2(), -- Current size (==SizeFull or collapsed title bar size)
SizeFull = ImVec2(), -- Size when non collapsed
Active = false, -- Set to true on Begin(), unless Collapsed
WasActive = false,
Collapsed = false, -- Set when collapsing window to become only title-bar
SkipItems = false, -- Set when items can safely be all clipped (e.g. window not visible or collapsed)
SkipRefresh = false, -- [EXPERIMENTAL] Reuse previous frame drawn contents, Begin() returns false.
CacheSnap = nil, -- [Port] SetNextWindowCached(): state at the last refresh
CacheSkipItemsBackup = nil,
Appearing = false, -- Set during the frame where the window is appearing (or re-appearing)
Hidden = false, -- Do not display (== HiddenFrames*** > 0)
IsFallbackWindow = false, -- Set on the "Debug##Default" window.
IsExplicitChild = nil, -- Set when passed _ChildWindow, left to false by BeginDocked()
ResizeBorderHovered = -1, -- Current border being hovered for resize (-1: none, otherwise 0-3)
ResizeBorderHeld = -1, -- Current border being held for resize (-1: none, otherwise 0-3)
BeginCount = 0, -- Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs)
BeginCountPreviousFrame = 0, -- Number of Begin() during the previous frame
BeginOrderWithinParent = 0, -- Begin() order within immediate parent window, if we are a child window. Otherwise 0.
BeginOrderWithinContext = 0, -- Begin() order within entire imgui context. This is mostly used for debugging submission order related issues.
FocusOrder = nil, -- Order within WindowsFocusOrder[], altered when windows are focused.
HiddenFramesCanSkipItems = 0, -- Hide the window for N frames
HiddenFramesCannotSkipItems = 0, -- Hide the window for N frames while allowing items to be submitted so we can measure their size
HiddenFramesForRenderOnly = 0, -- Hide the window until frame N at Render() time only
DisableInputsFrames = 0, -- Disable window interactions for N frames
WindowRounding = 0, -- Window rounding at the time of Begin(). May be clamped lower to avoid rendering artifacts with title bar, menu bar etc.
WindowBorderSize = 1, -- Window border size at the time of Begin().
TitleBarHeight = 0, MenuBarHeight = 0,
DecoOuterSizeX1 = 0, DecoOuterSizeY1 = 0,
DecoOuterSizeX2 = 0, DecoOuterSizeY2 = 0,
DecoInnerSizeX1 = 0, DecoInnerSizeY1 = 0,
ContentSize = ImVec2(), -- Size of contents/scrollable client area (calculated from the extents reach of the cursor) from previous frame. Does not include window decoration or window padding.
ContentSizeIdeal = ImVec2(),
ContentSizeExplicit = ImVec2(), -- Size of contents/scrollable client area explicitly request by the user via SetNextWindowContentSize().
AutoFitFramesX = -1, AutoFitFramesY = -1,
AutoFitOnlyGrows = false,
HasCloseButton = true, -- Set when the window has a close button (p_open ~= nil)
BgClickFlags = 0, -- Configure behavior of click+dragging on window bg/void or over items. Default sets by io.ConfigWindowsMoveFromTitleBarOnly. If you use this please report in #3379.
SetWindowPosAllowFlags = 0, SetWindowSizeAllowFlags = 0, SetWindowCollapsedAllowFlags = 0, -- store acceptable condition flags for SetNextWindowPos() use.
SetWindowPosVal = ImVec2(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]]), -- store window position when using a non-zero Pivot (position set needs to be processed when we know the window size)
SetWindowPosPivot = ImVec2(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]]), -- store window pivot for positioning. ImVec2(0, 0) when positioning from top-left corner; ImVec2(0.5, 0.5) for centering; ImVec2(1, 1) for bottom right.
SettingsOffset = -1, -- Offset into SettingsWindows[] (offsets are always valid as we only grow the array from the back)
ScrollbarSizes = ImVec2(), -- Size taken by each scrollbars on their smaller axis. Pay attention! ScrollbarSizes.x == width of the vertical scrollbar, ScrollbarSizes.y = height of the horizontal scrollbar.
Scroll = ImVec2(),
ScrollbarX = false,
ScrollbarY = false, -- Are scrollbars visible?
ScrollMax = ImVec2(),
ScrollTarget = ImVec2(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]]), -- target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0. (FLT_MAX for no change)
ScrollTargetCenterRatio = ImVec2(0.5, 0.5), -- 0.0 = scroll so that target position is at top, 0.5 = scroll so that target position is centered
ScrollTargetEdgeSnapDist = ImVec2(), -- 0.0 = no snapping, >0.0 snapping threshold
ScrollbarXStabilizeToggledHistory = 0, -- Used to stabilize scrollbar visibility in case of feedback loops
DrawList = nil, -- == DrawListInst
DrawListInst = ImDrawList(ctx.DrawListSharedData),
RootWindow = nil, -- Point to ourself or first ancestor that is not a child window. Doesn't cross through popups/dock nodes.
RootWindowPopupTree = nil, -- Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child.
RootWindowDockTree = nil, -- Point to ourself or first ancestor that is not a child window. Cross through dock nodes.
ParentWindow = nil, -- If we are a child _or_ popup _or_ docked window, this is pointing to our parent. Otherwise nil.
ParentWindowInBeginStack = nil,
IDStack = ImVector(), -- ID stack. ID are hashes seeded with the value at the top of the stack. (In theory this should be in the TempData structure)
ViewportAllowPlatformMonitorExtend = -1, -- Reset to -1 every frame (index is guaranteed to be valid between NewFrame..EndFrame), only used in the Appearing frame of a tooltip/popup to enforce clamping to a given monitor
Viewport = nil, -- Always set in Begin(). Inactive windows may have a nil value here if their viewport was discarded.
ViewportId = 0, -- We backup the viewport id (since the viewport may disappear or never be created if the window is inactive)
ViewportPos = ImVec2(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]]), -- We backup the viewport position (since the viewport may disappear or never be created if the window is inactive)
ViewportOwned = nil,
DC = ImGuiWindowTempData(), -- Temporary per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the "DC" variable name.
OuterRectClipped = ImRect(), -- == Window.Rect() just after setup in Begin(). == window.Rect() for root window.
InnerRect = ImRect(), -- Inner rectangle (omit title bar, menu bar, scroll bar)
InnerClipRect = ImRect(), -- == InnerRect shrunk by WindowPadding*0.5 on each side, clipped within viewport or parent clip rect.
WorkRect = ImRect(), -- Initially covers the whole scrolling region. Reduced by containers e.g columns/tables when active. Shrunk by WindowPadding*1.0 on each side. This is meant to replace ContentRegionRect over time (from 1.71+ onward).
ParentWorkRect = ImRect(), -- Backup of WorkRect before entering a container such as columns/tables. Used by e.g. SpanAllColumns functions to easily access. Stacked containers are responsible for maintaining this. -- FIXME-WORKRECT: Could be a stack?
ContentRegionRect = ImRect(), -- FIXME: This is currently confusing/misleading. It is essentially WorkRect but not handling of scrolling. We currently rely on it as right/bottom aligned sizing operation need some size to rely on.
ClipRect = ImRect(), -- Current clipping/scissoring rectangle, evolve as we are using PushClipRect(), etc. == DrawList.clip_rect_stack.back().
LastFrameActive = -1, -- Last frame number the window was Active.
LastFrameJustFocused = -1, -- Last frame number the window was made Focused.
LastTimeActive = -1.0, -- Last timestamp the window was Active (using float as we don't need high precision there)
StateStorage = {},
ColumnsStorage = ImVector(), -- ImVector<ImGuiOldColumns>
WriteAccessed = false, -- Set to true when any widget access the current window
FontWindowScale = 1.0, -- User scale multiplier per-window, via SetWindowFontScale()
FontWindowScaleParents = 1.0,
HitTestHoleSize = ImVec2(), -- Define an optional rectangular hole where mouse will pass-through the window.
NavLastChildNavWindow = nil, -- When going to the menu bar, we remember the child window we came from. (This could probably be made implicit if we kept g.Windows sorted by last focused including child window.)
NavLastIds = {[0] = 0, [1] = 0}, -- FIXME: 1-based instead
NavRectRel = {[0] = ImRect(), [1] = ImRect()}, -- Reference rectangle, in window relative space
NavPreferredScoringPosRel = {[0] = ImVec2(), [1] = ImVec2()}, -- Preferred X/Y position updated when moving on a given axis, reset to FLT_MAX.
NavRootFocusScopeId = 0 -- Focus Scope ID at the time of Begin()
}
this.DrawList = this.DrawListInst
this.DrawList._OwnerName = name
setmetatable(this, MT.ImGuiWindow)
this.ID = ImHashStr(name)
this.IDStack:push_back(this.ID)
this.MoveId = this:GetID("#MOVE")
this.TabId = this:GetID("#TAB") -- == window.GetID("#TAB")
-- Docking
this.ChildId = this.ChildId or 0 -- ID of corresponding item in parent window (for navigation to return from child window to parent window)
this.SetWindowDockAllowFlags = 0 -- store acceptable condition flags for SetNextWindowDock() use.
this.ParentWindowForFocusRoute = nil -- Set to manual link a window to its logical parent so that Shortcut() chain are honored (e.g. Tool linked to Document)
this.DockIsActive = false -- When docking artifacts are actually visible. When this is set, DockNode is guaranteed to be != nil. ~~ (DockNode != nil) && (DockNode.Windows.Size > 1).
this.DockNodeIsVisible = false
this.DockTabIsVisible = false -- Is our window visible this frame? ~~ is the corresponding tab selected?
this.DockTabWantClose = false
this.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.
this.DockStyle = { Colors = {} }
for i = 0, 8 do this.DockStyle.Colors[i] = 0 end
this.DockNode = nil -- Which node are we docked into. Important: Prefer testing DockIsActive in many cases as this will still be set when the dock node is hidden.
this.DockNodeAsHost = nil -- Which node are we owning (for parent windows)
this.DockId = 0 -- Backup of last valid DockNode.ID, so single window remember their dock node id even when they are not bound any more
return this
end
--- @class ImDrawDataBuilder
--- @field Layers [ImVector<ImDrawList>?, ImVector<ImDrawList>?] # Pointers to global layers for: regular, tooltip. LayersP[0] is owned by DrawData.
--- @field LayerData1 ImVector<ImDrawList>
--- @return ImDrawDataBuilder
--- @nodiscard
local function ImDrawDataBuilder()
return
{
Layers = {nil, nil}, -- Pointers to global layers for: regular, tooltip. LayersP[0] is owned by DrawData.
LayerData1 = ImVector()
}
end
-------------------------------------------------------------------------------
-- [SECTION] Viewport support
-------------------------------------------------------------------------------
-- ImGuiViewport Private/Internals fields (cardinal sin: we are using inheritance!)
-- Every instance of ImGuiViewport is in fact a ImGuiViewportP.
--- @class ImGuiViewportP : ImGuiViewport
--- @field Window? ImGuiWindow
--- @field Idx int # Initial value = -1, then becomes 1-based index
--- @field LastFrameActive int
--- @field LastFocusedStampCount int
--- @field LastNameHash ImGuiID
--- @field LastPos ImVec2
--- @field LastSize ImVec2
--- @field Alpha float
--- @field LastAlpha float
--- @field LastFocusedHadNavWindow bool
--- @field PlatformMonitor short
--- @field BgFgDrawListsLastFrameActive [int, int] # 1-based
--- @field BgFgDrawListsLastTimeActive [int, int] # 1-based
--- @field BgFgDrawLists [ImDrawList, ImDrawList] # 1-based
--- @field DrawDataP ImDrawData
--- @field DrawDataBuilder ImDrawDataBuilder
--- @field LastPlatformPos ImVec2
--- @field LastPlatformSize ImVec2
--- @field LastRendererSize ImVec2
--- @field WorkInsetMin ImVec2
--- @field WorkInsetMax ImVec2
--- @field BuildWorkInsetMin ImVec2
--- @field BuildWorkInsetMax ImVec2
local IMGUI_VIEWPORT_P = {}
IMGUI_VIEWPORT_P.__index = IMGUI_VIEWPORT_P
setmetatable(IMGUI_VIEWPORT_P, {__index = MT.ImGuiViewport})
function IMGUI_VIEWPORT_P:ClearRequestFlags()
self.PlatformRequestClose = false
self.PlatformRequestMove = false
self.PlatformRequestResize = false
end
-- Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect)
function IMGUI_VIEWPORT_P:CalcWorkRectPos(inset_min)
return ImVec2(self.Pos.x + inset_min.x, self.Pos.y + inset_min.y)
end
function IMGUI_VIEWPORT_P:CalcWorkRectSize(inset_min, inset_max)
return ImVec2(ImMax(0.0, self.Size.x - inset_min.x - inset_max.x), ImMax(0.0, self.Size.y - inset_min.y - inset_max.y))
end
-- Update public fields
function IMGUI_VIEWPORT_P:UpdateWorkRect()
self.WorkPos = self:CalcWorkRectPos(self.WorkInsetMin)
self.WorkSize = self:CalcWorkRectSize(self.WorkInsetMin, self.WorkInsetMax)
end
-- Helpers to retrieve ImRect (we don't need to store BuildWorkRect as every access tend to change it, hence the code asymmetry)
--- @nodiscard
function IMGUI_VIEWPORT_P:GetMainRect()
return ImRect(self.Pos.x, self.Pos.y,
self.Pos.x + self.Size.x,
self.Pos.y + self.Size.y)
end
--- @nodiscard
function IMGUI_VIEWPORT_P:GetWorkRect()
return ImRect(self.WorkPos.x, self.WorkPos.y,
self.WorkPos.x + self.WorkSize.x,
self.WorkPos.y + self.WorkSize.y)
end
--- @nodiscard
function IMGUI_VIEWPORT_P:GetBuildWorkRect()
local pos = self:CalcWorkRectPos(self.BuildWorkInsetMin)
local size = self:CalcWorkRectSize(self.BuildWorkInsetMin, self.BuildWorkInsetMax)
return ImRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y)
end
--- @return ImGuiViewportP
--- @nodiscard
function ImGuiViewportP()
local this = setmetatable(ImGuiViewport(), IMGUI_VIEWPORT_P) --- @cast this ImGuiViewportP
this.Window = nil -- Set when the viewport is owned by a window (and ImGuiViewportFlags.CanHostOtherWindows is NOT set)
this.Idx = -1
this.LastFrameActive = -1 -- Last frame number this viewport was activated by a window
this.LastFocusedStampCount = -1 -- Last stamp number from when a window hosted by this viewport was focused (by comparing this value between two viewport we have an implicit viewport z-order we use as fallback)
this.LastNameHash = 0
this.LastPos = ImVec2() -- ImGuiViewport() { memset(this, 0, sizeof(*this)); }
this.LastSize = ImVec2()
this.Alpha = 1.0 -- Window opacity (when dragging dockable windows/viewports we make them transparent)
this.LastAlpha = 1.0
this.LastFocusedHadNavWindow = false -- Instead of maintaining a LastFocusedWindow (which may harder to correctly maintain), we merely store weither NavWindow ~= nil last time the viewport was focused.
this.PlatformMonitor = -1
this.BgFgDrawListsLastFrameActive = {-1, -1} -- Last frame the background (1) and foreground (2) draw lists were used.
this.BgFgDrawListsLastTimeActive = {-1, -1} -- Timestamps for Gc.
this.BgFgDrawLists = {nil, nil} -- Convenience background [1] and foreground [2] draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays.
this.DrawDataP = ImDrawData()
this.DrawDataBuilder = ImDrawDataBuilder() -- Temporary data while building final ImDrawData
this.LastPlatformPos = ImVec2(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]])
this.LastPlatformSize = ImVec2(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]])
this.LastRendererSize = ImVec2(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]])
-- Per-viewport work area
-- - Insets are >= 0.0 values, distance from viewport corners to work area.
-- - BeginMainMenuBar() and DockspaceOverViewport() tend to use work area to avoid stepping over existing contents.
-- - Generally 'safeAreaInsets' in iOS land, 'DisplayCutout' in Android land.
this.WorkInsetMin = ImVec2(0, 0) -- Work Area inset locked for the frame. GetWorkRect() always fits within GetMainRect().
this.WorkInsetMax = ImVec2(0, 0) -- "
this.BuildWorkInsetMin = ImVec2(0, 0) -- Work Area inset accumulator for current frame, to become next frame's WorkInset
this.BuildWorkInsetMax = ImVec2(0, 0) -- "
return this
end
-------------------------------------------------------------------------------
-- [SECTION] ImFontLoader
-------------------------------------------------------------------------------
-- Hooks and storage for a given font backend.
-- This structure is likely to evolve as we add support for incremental atlas updates.
-- Conceptually this could be public, but API is still going to be evolve.
--- @class ImFontLoader
--- @field Name string
--- @field LoaderInit? fun(atlas: ImFontAtlas): bool
--- @field LoaderShutdown? fun(atlas: ImFontAtlas)
--- @field FontSrcInit? fun(atlas: ImFontAtlas, src: ImFontConfig): bool
--- @field FontSrcDestroy? fun(atlas: ImFontAtlas, src: ImFontConfig)
--- @field FontSrcContainsGlyph? fun(atlas: ImFontAtlas, src: ImFontConfig, codepoint: ImWchar): bool
--- @field FontBakedInit? fun(atlas: ImFontAtlas, src: ImFontConfig, baked: ImFontBaked, loader_data_for_baked_src?: any): bool
--- @field FontBakedDestroy? fun(atlas: ImFontAtlas, src: ImFontConfig, baked: ImFontBaked, loader_data_for_baked_src?: any)
--- @field FontBakedLoadGlyph fun(atlas: ImFontAtlas, src: ImFontConfig, baked: ImFontBaked, loader_data_for_baked_src?: any, codepoint: ImWchar, out_glyph?: ImFontGlyph, advance_x?: float): bool, float?
--- @field FontBakedSrcLoaderDataSize unsigned_int
MT.ImFontLoader = {}
MT.ImFontLoader.__index = MT.ImFontLoader
--- @return ImFontLoader
--- @nodiscard
function ImFontLoader()
--- @type ImFontLoader
local this = setmetatable({}, MT.ImFontLoader)
this.Name = nil
this.LoaderInit = nil
this.LoaderShutdown = nil
this.FontSrcInit = nil
this.FontSrcDestroy = nil
this.FontSrcContainsGlyph = nil
this.FontBakedInit = nil
this.FontBakedDestroy = nil
this.FontBakedLoadGlyph = nil -- Required
-- Size of backend data, Per Baked * Per Source. Buffers are managed by core to avoid excessive allocations.
-- FIXME: At this point the two other types of buffers may be managed by core to be consistent?
this.FontBakedSrcLoaderDataSize = 0
return this
end
-------------------------------------------------------------------------------
-- [SECTION] ImFontAtlas internal API
-------------------------------------------------------------------------------
-- Packed rectangle lookup entry (we need an indirection to allow removing/reordering rectangles)
-- User are returned ImFontAtlasRectId values which are meant to be persistent.
-- We handle this with an indirection. While Rects[] may be in theory shuffled, compacted etc., RectsIndex[] cannot it is keyed by ImFontAtlasRectId.
-- RectsIndex[] is used both as an index into Rects[] and an index into itself. This is basically a free-list. See ImFontAtlasBuildAllocRectIndexEntry() code.
-- Having this also makes it easier to e.g. sort rectangles during repack.
--- @class ImFontAtlasRectEntry
--- @field TargetIndex int # 0-based! When IsUsed = true, TargetIndex = this rect's index in Rects; IsUsed = false, TargetIndex = the next unused RectsIndex entry's index
--- @field Generation unsigned_int # How many times this entry is reused
--- @field IsUsed bool
--- @return ImFontAtlasRectEntry
--- @nodiscard
function ImFontAtlasRectEntry()
return {
TargetIndex = 0,
Generation = 0,
IsUsed = false
}
end
-- Refer to ImFontAtlasPackGetRect() to better understand how this works.
local ImFontAtlasRectId_IndexMask_ = 0x0007FFFF -- 20-bits signed: index to access builder.RectsIndex[].
local ImFontAtlasRectId_GenerationMask_ = 0x3FF00000 -- 10-bits: entry generation, so each ID is unique and get can safely detected old identifiers.
local ImFontAtlasRectId_GenerationShift_ = 20
--- @param id ImFontAtlasRectId # Expects 0-based!
--- @return int # 0-based!
function ImFontAtlasRectId_GetIndex(id) return bitAnd(id, ImFontAtlasRectId_IndexMask_) end
--- @param id ImFontAtlasRectId # Expects 0-based!
--- @return unsigned_int
function ImFontAtlasRectId_GetGeneration(id) return bitRShift(bitAnd(id, ImFontAtlasRectId_GenerationMask_), ImFontAtlasRectId_GenerationShift_) end
--- @param index_idx int # Expects 0-based!
--- @param gen_idx int
--- @return ImFontAtlasRectId # 0-based!
function ImFontAtlasRectId_Make(index_idx, gen_idx)
assert(index_idx >= 0 and index_idx <= ImFontAtlasRectId_IndexMask_ and gen_idx <= bitRShift(ImFontAtlasRectId_GenerationMask_, ImFontAtlasRectId_GenerationShift_))
return bitOr(index_idx, bitLShift(gen_idx, ImFontAtlasRectId_GenerationShift_))
end
-- Data available to potential texture post-processing functions
--- @class ImFontAtlasPostProcessData
--- @field FontAtlas ImFontAtlas
--- @field Font ImFont
--- @field FontSrc ImFontConfig
--- @field FontBaked ImFontBaked
--- @field Glyph ImFontGlyph
--- @field Pixels unsigned_char[]
--- @field _PixelsBase int # LUA: in cpp code the `Pixels` is a pointer. we can do this to avoid creating another custom structure to mimic it
--- @field Format ImTextureFormat
--- @field Pitch int
--- @field Width int
--- @field Height int
--- @param atlas ImFontAtlas
--- @param font ImFont
--- @param font_src ImFontConfig
--- @param font_baked ImFontBaked
--- @param glyph ImFontGlyph
--- @param pixels unsigned_char[]
--- @param pixels_base int
--- @param format ImTextureFormat
--- @param pitch int
--- @param width int
--- @param height int
--- @return ImFontAtlasPostProcessData
--- @nodiscard
function ImFontAtlasPostProcessData(atlas, font, font_src, font_baked, glyph, pixels, pixels_base, format, pitch, width, height)
return {
FontAtlas = atlas,
Font = font,
FontSrc = font_src,
FontBaked = font_baked,
Glyph = glyph,
-- Pixel data
Pixels = pixels,
_PixelsBase = pixels_base,
Format = format,
Pitch = pitch,
Width = width,
Height = height,
}
end
-------------------------------------------------------------------------------
-- [SECTION] Inputs support
-------------------------------------------------------------------------------
--- @enum ImGuiInputSource
ImGuiInputSource = {
None = 0,
Mouse = 1, -- Note: may be Mouse or TouchScreen or Pen. See io.MouseSource to distinguish them.
Keyboard = 2,
Gamepad = 3,
COUNT = 4
}
--- @class ImGuiInputEventMousePos
--- @field PosX float
--- @field PosY float
--- @field MouseSource ImGuiMouseSource
--- @return ImGuiInputEventMousePos
--- @nodiscard
function ImGuiInputEventMousePos()
return { PosX = 0, PosY = 0, MouseSource = 0 --[[ImGuiMouseSource.Mouse]] }
end
--- @class ImGuiInputEventMouseButton
--- @field Button int
--- @field Down bool
--- @field MouseSource ImGuiMouseSource
--- @return ImGuiInputEventMouseButton
--- @nodiscard
function ImGuiInputEventMouseButton()
return { Button = 0, Down = false, MouseSource = 0 --[[ImGuiMouseSource.Mouse]] }
end
--- @class ImGuiInputEventMouseWheel
--- @field WheelX float
--- @field WheelY float
--- @field MouseSource ImGuiMouseSource
--- @return ImGuiInputEventMouseWheel
--- @nodiscard
function ImGuiInputEventMouseWheel()
return { WheelX = 0, WheelY = 0, MouseSource = 0 --[[ImGuiMouseSource.Mouse]] }
end
--- @class ImGuiInputEventKey
--- @field Key ImGuiKey
--- @field Down bool
--- @field AnalogValue float
--- @return ImGuiInputEventKey
--- @nodiscard
function ImGuiInputEventKey()
return { Key = 0, Down = false, AnalogValue = 0 }
end
--- @class ImGuiInputEventText
--- @field Char unsigned_int
--- @return ImGuiInputEventText
--- @nodiscard
function ImGuiInputEventText()
return { Char = nil }
end
--- @class ImGuiInputEventAppFocused
--- @field Focused bool
--- @return ImGuiInputEventAppFocused
--- @nodiscard
function ImGuiInputEventAppFocused()
return { Focused = nil }
end
--- @class ImGuiInputEventMouseViewport
--- @field HoveredViewportID ImGuiID
--- @return ImGuiInputEventMouseViewport
--- @nodiscard
function ImGuiInputEventMouseViewport()
return { HoveredViewportID = 0 }
end
--- @class ImGuiInputEvent
--- @return ImGuiInputEvent
--- @nodiscard
function ImGuiInputEvent()
return {
Type = 0,
Source = 0,
EventId = 0, -- Unique, sequential increasing integer to identify an event (if you need to correlate them to other data).
-- union
MousePos = nil, -- if Type == ImGuiInputEventType.MousePos
MouseWheel = nil, -- if Type == ImGuiInputEventType.MouseWheel
MouseButton = nil, -- if Type == ImGuiInputEventType.MouseButton
MouseViewport = nil, -- if Type == ImGuiInputEventType.MouseViewport
Key = nil, -- if Type == ImGuiInputEventType.Key
Text = nil, -- if Type == ImGuiInputEventType.Text
AppFocused = nil, -- if Type == ImGuiInputEventType.Focus
}
end
-- [Internal] Key ranges
ImGuiKey_LegacyNativeKey_BEGIN = 0
ImGuiKey_LegacyNativeKey_END = 512
ImGuiKey_Keyboard_BEGIN = ImGuiKey.NamedKey_BEGIN
ImGuiKey_Keyboard_END = ImGuiKey.GamepadStart
ImGuiKey_Gamepad_BEGIN = ImGuiKey.GamepadStart
ImGuiKey_Gamepad_END = ImGuiKey.GamepadRStickDown + 1
ImGuiKey_Mouse_BEGIN = ImGuiKey.MouseLeft
ImGuiKey_Mouse_END = ImGuiKey.MouseWheelY + 1
ImGuiKey_Aliases_BEGIN = ImGuiKey_Mouse_BEGIN
ImGuiKey_Aliases_END = ImGuiKey_Mouse_END
-- [Internal] Named shortcuts for Navigation
ImGuiKey.NavKeyboardTweakSlow = ImGuiMod_Ctrl
ImGuiKey.NavKeyboardTweakFast = ImGuiMod_Shift
ImGuiKey.NavGamepadTweakSlow = ImGuiKey.GamepadL1
ImGuiKey.NavGamepadTweakFast = ImGuiKey.GamepadR1
ImGuiKey.NavGamepadMenu = ImGuiKey.GamepadFaceLeft -- Toggle menu layer. Hold to enable Windowing.
ImGuiKey.NavGamepadContextMenu = ImGuiKey.GamepadFaceUp -- Open context menu (same as Shift+F10)
--- @enum ImGuiInputEventType
ImGuiInputEventType = {
None = 0,
MousePos = 1,
MouseWheel = 2,
MouseButton = 3,
MouseViewport = 4,
Key = 5,
Text = 6,
Focus = 7,
COUNT = 8
}
-- Extend ImGuiInputFlags
-- Flags for extended versions of IsKeyPressed(), IsMouseClicked(), Shortcut(), SetKeyOwner(), SetItemKeyOwner()
-- Don't mistake with ImGuiInputTextFlags! (which is for ImGui.InputText() function)
-- Flags for IsKeyPressed(), IsKeyChordPressed(), IsMouseClicked(), Shortcut()
-- - Repeat mode: Repeat rate selection
ImGuiInputFlags.RepeatRateDefault = bitLShift(1, 1) -- Repeat rate: Regular (default)
ImGuiInputFlags.RepeatRateNavMove = bitLShift(1, 2) -- Repeat rate: Fast
ImGuiInputFlags.RepeatRateNavTweak = bitLShift(1, 3) -- Repeat rate: Faster
-- - Repeat mode: Specify when repeating key pressed can be interrupted.
-- - In theory ImGuiInputFlags.RepeatUntilOtherKeyPress may be a desirable default, but it would break too many behavior so everything is opt-in.
ImGuiInputFlags.RepeatUntilRelease = bitLShift(1, 4) -- Stop repeating when released (default for all functions except Shortcut). This only exists to allow overriding Shortcut() default behavior.
ImGuiInputFlags.RepeatUntilKeyModsChange = bitLShift(1, 5) -- Stop repeating when released OR if keyboard mods are changed (default for Shortcut)
ImGuiInputFlags.RepeatUntilKeyModsChangeFromNone = bitLShift(1, 6) -- Stop repeating when released OR if keyboard mods are leaving the None state. Allows going from Mod+Key to Key by releasing Mod.
ImGuiInputFlags.RepeatUntilOtherKeyPress = bitLShift(1, 7) -- Stop repeating when released OR if any other keyboard key is pressed during the repeat
-- Flags for SetKeyOwner(), SetItemKeyOwner()
-- - Locking key away from non-input aware code. Locking is useful to make input-owner-aware code steal keys from non-input-owner-aware code. If all code is input-owner-aware locking would never be necessary.
ImGuiInputFlags.LockThisFrame = bitLShift(1, 20) -- Further accesses to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared at end of frame.
ImGuiInputFlags.LockUntilRelease = bitLShift(1, 21) -- Further accesses to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared when the key is released or at end of each frame if key is released.
-- - Condition for SetItemKeyOwner()
ImGuiInputFlags.CondHovered = bitLShift(1, 22) -- Only set if item is hovered (default to both)
ImGuiInputFlags.CondActive = bitLShift(1, 23) -- Only set if item is active (default to both)
ImGuiInputFlags.CondDefault_ = bitOr(ImGuiInputFlags.CondHovered, ImGuiInputFlags.CondActive)
-- [Internal] Mask of which function support which flags
ImGuiInputFlags.RepeatRateMask_ = bitOr(ImGuiInputFlags.RepeatRateDefault, ImGuiInputFlags.RepeatRateNavMove, ImGuiInputFlags.RepeatRateNavTweak)
ImGuiInputFlags.RepeatUntilMask_ = bitOr(ImGuiInputFlags.RepeatUntilRelease, ImGuiInputFlags.RepeatUntilKeyModsChange, ImGuiInputFlags.RepeatUntilKeyModsChangeFromNone, ImGuiInputFlags.RepeatUntilOtherKeyPress)
ImGuiInputFlags.RepeatMask_ = bitOr(ImGuiInputFlags.Repeat, ImGuiInputFlags.RepeatRateMask_, ImGuiInputFlags.RepeatUntilMask_)
ImGuiInputFlags.CondMask_ = bitOr(ImGuiInputFlags.CondHovered, ImGuiInputFlags.CondActive)
ImGuiInputFlags.RouteTypeMask_ = bitOr(ImGuiInputFlags.RouteActive, ImGuiInputFlags.RouteFocused, ImGuiInputFlags.RouteGlobal, ImGuiInputFlags.RouteAlways)
ImGuiInputFlags.RouteOptionsMask_ = bitOr(ImGuiInputFlags.RouteOverFocused, ImGuiInputFlags.RouteOverActive, ImGuiInputFlags.RouteUnlessBgFocused, ImGuiInputFlags.RouteFromRootWindow)
ImGuiInputFlags.SupportedByIsKeyPressed = ImGuiInputFlags.RepeatMask_
ImGuiInputFlags.SupportedByIsMouseClicked = ImGuiInputFlags.Repeat
ImGuiInputFlags.SupportedByShortcut = bitOr(ImGuiInputFlags.RepeatMask_, ImGuiInputFlags.RouteTypeMask_, ImGuiInputFlags.RouteOptionsMask_)
ImGuiInputFlags.SupportedBySetNextItemShortcut = bitOr(ImGuiInputFlags.RepeatMask_, ImGuiInputFlags.RouteTypeMask_, ImGuiInputFlags.RouteOptionsMask_, ImGuiInputFlags.Tooltip)
ImGuiInputFlags.SupportedBySetKeyOwner = bitOr(ImGuiInputFlags.LockThisFrame, ImGuiInputFlags.LockUntilRelease)
ImGuiInputFlags.SupportedBySetItemKeyOwner = bitOr(ImGuiInputFlags.SupportedBySetKeyOwner, ImGuiInputFlags.CondMask_)
--- @enum ImGuiAxis # can be used to index ImVec2
ImGuiAxis =
{
None = 0,
X = 1,
Y = 2
}
--- @enum ImGuiPlotType
ImGuiPlotType =
{
Lines = 0,
Histogram = 1
}
-------------------------------------------------------------------------------
-- [SECTION] Navigation support
-------------------------------------------------------------------------------
--- @enum ImGuiActivateFlags
ImGuiActivateFlags = {
None = 0,
PreferInput = bitLShift(1, 0), -- Favor activation that requires keyboard text input (e.g. for Slider/Drag). Default for Enter key.
PreferTweak = bitLShift(1, 1), -- Favor activation for tweaking with arrows or gamepad (e.g. for Slider/Drag). Default for Space key and if keyboard is not used.
TryToPreserveState = bitLShift(1, 2), -- Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection)
FromTabbing = bitLShift(1, 3), -- Activation requested by a tabbing request (ImGuiNavMoveFlags.IsTabbing)
FromShortcut = bitLShift(1, 4), -- Activation requested by an item shortcut via SetNextItemShortcut() function.
FromFocusApi = bitLShift(1, 5) -- Activation requested by an api request (ImGuiNavMoveFlags.FocusApi)
}
-- Early work-in-progress API for ScrollToItem()
-- FIXME: Missing flags to request making both edges visible when possible.
--- @enum ImGuiScrollFlags
ImGuiScrollFlags = {
None = 0,
KeepVisibleEdgeX = bitLShift(1, 0), -- If item is not visible: scroll as little as possible on X axis to bring item back into view [default for X axis]
KeepVisibleEdgeY = bitLShift(1, 1), -- If item is not visible: scroll as little as possible on Y axis to bring item back into view [default for Y axis for windows that are already visible]
KeepVisibleCenterX = bitLShift(1, 2), -- If item is not visible: scroll to make the item centered on X axis [rarely used]
KeepVisibleCenterY = bitLShift(1, 3), -- If item is not visible: scroll to make the item centered on Y axis
AlwaysCenterX = bitLShift(1, 4), -- Always center the result item on X axis [rarely used]
AlwaysCenterY = bitLShift(1, 5), -- Always center the result item on Y axis [default for Y axis for appearing window)
NoScrollParent = bitLShift(1, 6), -- Disable forwarding scrolling to parent window if required to keep item/rect visible (only scroll window the function was applied to).
}
ImGuiScrollFlags.MaskX_ = bitOr(ImGuiScrollFlags.KeepVisibleEdgeX, ImGuiScrollFlags.KeepVisibleCenterX, ImGuiScrollFlags.AlwaysCenterX)
ImGuiScrollFlags.MaskY_ = bitOr(ImGuiScrollFlags.KeepVisibleEdgeY, ImGuiScrollFlags.KeepVisibleCenterY, ImGuiScrollFlags.AlwaysCenterY)
-- Stacked storage data for BeginGroup()/EndGroup()
--- @class ImGuiGroupData
--- @field WindowID ImGuiID
--- @field BackupCursorPos ImVec2
--- @field BackupCursorMaxPos ImVec2
--- @field BackupCursorPosPrevLine ImVec2
--- @field BackupIndent ImVec1
--- @field BackupGroupOffset ImVec1
--- @field BackupCurrLineSize ImVec2
--- @field BackupCurrLineTextBaseOffset float
--- @field BackupActiveIdIsAlive ImGuiID
--- @field BackupAnyIdHasBeenEditedThisFrame bool
--- @field BackupDeactivatedIdIsAlive bool
--- @field BackupHoveredIdIsAlive bool
--- @field BackupIsSameLine bool
--- @field EmitItem bool
--- @return ImGuiGroupData
--- @nodiscard
function ImGuiGroupData()
return
{
WindowID = nil,
BackupCursorPos = ImVec2(),
BackupCursorMaxPos = ImVec2(),
BackupCursorPosPrevLine = ImVec2(),
BackupIndent = ImVec1(),
BackupGroupOffset = ImVec1(),
BackupCurrLineSize = ImVec2(),
BackupCurrLineTextBaseOffset = nil,
BackupActiveIdIsAlive = nil,
BackupAnyIdHasBeenEditedThisFrame = nil,
BackupDeactivatedIdIsAlive = nil,
BackupHoveredIdIsAlive = nil,
BackupIsSameLine = nil,
EmitItem = nil
}
end
--- @enum ImGuiTooltipFlags
ImGuiTooltipFlags =
{
None = 0,
OverridePrevious = bitLShift(1, 1) -- Clear/ignore previously submitted tooltip (defaults to append)
}
-------------------------------------------------------------------------------
-- [SECTION] Popup support
-------------------------------------------------------------------------------
--- @enum ImGuiPopupPositionPolicy
ImGuiPopupPositionPolicy =
{
Default = 0,
ComboBox = 1,
Tooltip = 2
}
-- Storage for popup stacks (g.OpenPopupStack and g.BeginPopupStack)
--- @class ImGuiPopupData
--- @field PopupId ImGuiID
--- @field Window ImGuiWindow
--- @field RestoreNavWindow ImGuiWindow
--- @field ParentNavLayer int
--- @field OpenFrameCount int
--- @field OpenParentId ImGuiID
--- @field OpenPopupPos ImVec2
--- @field OpenMousePos ImVec2
--- @return ImGuiPopupData
--- @nodiscard
function ImGuiPopupData()
return {
PopupId = 0, -- Set on OpenPopup()
Window = nil, -- Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup()
RestoreNavWindow = nil, -- Set on OpenPopup(), a NavWindow that will be restored on popup close
ParentNavLayer = -1, -- Resolved on BeginPopup(). Actually a ImGuiNavLayer type (declared down below), initialized to -1 which is not part of an enum, but serves well-enough as "not any of layers" value
OpenFrameCount = -1, -- Set on OpenPopup()
OpenParentId = 0, -- Set on OpenPopup(), we need this to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items)
OpenPopupPos = ImVec2(), -- Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse)
OpenMousePos = ImVec2() -- Set on OpenPopup(), copy of mouse position at the time of opening popup
}
end
--- @enum ImGuiWindowRefreshFlags
ImGuiWindowRefreshFlags = {
None = 0,
TryToAvoidRefresh = bitLShift(1, 0), -- [EXPERIMENTAL] Try to keep existing contents, USER MUST NOT HONOR BEGIN() RETURNING FALSE AND NOT APPEND.
RefreshOnHover = bitLShift(1, 1), -- [EXPERIMENTAL] Always refresh on hover
RefreshOnFocus = bitLShift(1, 2) -- [EXPERIMENTAL] Always refresh on focus
-- Refresh policy/frequency, Load Balancing etc.
}
--- @enum ImGuiNavRenderCursorFlags
ImGuiNavRenderCursorFlags = {
None = 0,
Compact = bitLShift(1, 1), -- Compact highlight, no padding/distance from focused item
AlwaysDraw = bitLShift(1, 2), -- Draw rectangular highlight if (g.NavId == id) even when g.NavCursorVisible == false, aka even when using the mouse.
}
-------------------------------------------------------------------------------
-- [SECTION] Metrics, Debug Tools
-------------------------------------------------------------------------------
-- See IMGUI_DEBUG_LOG() and IMGUI_DEBUG_LOG_XXX() macros.
--- @enum ImGuiDebugLogFlags
ImGuiDebugLogFlags = {
-- Event types
None = 0,
EventError = bitLShift(1, 0), -- Error submitted by IM_ASSERT_USER_ERROR()
EventActiveId = bitLShift(1, 1),
EventFocus = bitLShift(1, 2),
EventPopup = bitLShift(1, 3),
EventNav = bitLShift(1, 4),
EventClipper = bitLShift(1, 5),
EventSelection = bitLShift(1, 6),
EventIO = bitLShift(1, 7),
EventFont = bitLShift(1, 8),
EventInputRouting = bitLShift(1, 9),
EventDocking = bitLShift(1, 10),
EventViewport = bitLShift(1, 11),
EventTable = bitLShift(1, 12),
OutputToTTY = bitLShift(1, 20), -- Also send output to TTY
OutputToDebugger = bitLShift(1, 21),
OutputToTestEngine = bitLShift(1, 22),
}
ImGuiDebugLogFlags.EventMask_ = bitOr(
ImGuiDebugLogFlags.EventError,
ImGuiDebugLogFlags.EventActiveId,
ImGuiDebugLogFlags.EventFocus,
ImGuiDebugLogFlags.EventPopup,
ImGuiDebugLogFlags.EventNav,
ImGuiDebugLogFlags.EventClipper,
ImGuiDebugLogFlags.EventSelection,
ImGuiDebugLogFlags.EventTable,
ImGuiDebugLogFlags.EventIO,
ImGuiDebugLogFlags.EventFont,
ImGuiDebugLogFlags.EventInputRouting,
ImGuiDebugLogFlags.EventDocking,
ImGuiDebugLogFlags.EventViewport
)
-------------------------------------------------------------------------------
-- [SECTION] Localization support
-------------------------------------------------------------------------------
-- This is experimental and not officially supported, it'll probably fall short of features, if/when it does we may backtrack.
--- @enum ImGuiLocKey
ImGuiLocKey = {
VersionStr = 1,
TableSizeOne = 2,
TableSizeAllFit = 3,
TableSizeAllDefault = 4,
TableReset = 5,
TableResetOrder = 6,
TableResetVisibility = 7,
WindowingMainMenuBar = 8,
WindowingPopup = 9,
WindowingUntitled = 10,
OpenLink_s = 11,
CopyLink = 12,
DockingHideTabBar = 13,
DockingHoldShiftToDock = 14,
DockingDragToUndockOrMoveNode = 15,
COUNT = 15
}
--- @class ImGuiLocEntry
--- @field Key ImGuiLocKey
--- @field Text string
--- @param key ImGuiLocKey
--- @return string
function ImGui.LocalizeGetMsg(key)
local g = GImGui
local msg = g.LocalizationTable[key]
if msg then return msg else return "*Missing Text*" end
end
--- @param id ImGuiID
function ImGui.TempInputIsActive(id)
local g = GImGui
return (g.TempInputId == id and g.ActiveId == id) or (g.InputTextDeactivatedState.ID == id)
end
-- Get input text state if active
--- @param id ImGuiID
--- @return ImGuiInputTextState?
function ImGui.GetInputTextState(id)
local g = GImGui
if id ~= 0 and g.InputTextState.ID == id then
return g.InputTextState
end
return nil
end
-- Storage for PushFocusScope(), g.FocusScopeStack[], g.NavFocusRoute[]
--- @class ImGuiFocusScopeData
--- @field ID ImGuiID
--- @field WindowID ImGuiID
--- @return ImGuiFocusScopeData
--- @nodiscard
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) 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
-- [Luau] Desc holds the description string (upstream: DescOffset into ImGuiDebugItemPathQuery::ResultsDescBuf)
function ImGuiStackLevelInfo() return { ID = 0, QueryFrameCount = 0, QuerySuccess = false, DataType = -1, Desc = nil } end
-- [Luau] Results is a 1-based Lua array of ImGuiStackLevelInfo; ResultsDescBuf/ResultPathBuf are replaced by Lua strings.
function ImGuiDebugItemPathQuery() return { MainID = 0, Active = false, Complete = false, Step = 0, Results = {} } end
function ImGuiIDStackTool() return { OptHexEncodeNonAsciiChars = true, OptCopyToClipboardOnCtrlC = false, LastActiveFrame = -1, CopyToClipboardLastTime = -3.4028234663853e+38 --[[FLT_MAX]] } end
--- @enum ImGuiContextHookType
ImGuiContextHookType = { NewFramePre = 0, NewFramePost = 1, EndFramePre = 2, EndFramePost = 3, RenderPre = 4, RenderPost = 5, Shutdown = 6, PendingRemoval_ = 7 }
--- @class ImGuiContextHook
--- @field HookId ImGuiID -- A unique ID assigned by AddContextHook()
--- @field Type ImGuiContextHookType
--- @field Owner ImGuiID
--- @field Callback fun(ctx: ImGuiContext, hook: ImGuiContextHook)?
--- @field UserData any
function ImGuiContextHook() return { HookId = 0, Type = 0 --[[ImGuiContextHookType.NewFramePre]], 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.CurrentPixelDensity end
function ImGui.ScrollToBringRectIntoView(window, rect) ImGui.ScrollToRect(window, rect, 2 --[[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 >= 0 --[[ImGuiKey_LegacyNativeKey_BEGIN]] and key < 512 --[[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
end
__FILES['imgui_tables.lua'] = function()
--- Dear ImGui WIP
-- (tables and columns code)
-- 1:1 port of imgui_tables.cpp (docking branch, 1.93 WIP)
-- Index of this file:
--
-- [SECTION] Commentary
-- [SECTION] Structs (port: struct constructors from imgui_internal.h / imgui.h)
-- [SECTION] Tables: Main code
-- [SECTION] Tables: Simple accessors
-- [SECTION] Tables: Row changes
-- [SECTION] Tables: Columns changes
-- [SECTION] Tables: Columns width management
-- [SECTION] Tables: Drawing
-- [SECTION] Tables: Sorting
-- [SECTION] Tables: Headers
-- [SECTION] Tables: Context Menu
-- [SECTION] Tables: Settings (.ini data)
-- [SECTION] Tables: Garbage Collection
-- [SECTION] Columns, BeginColumns, EndColumns, etc.
-------------------------------------------------------------------------------
-- [SECTION] Commentary
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- Typical tables call flow: (root level is generally public API):
-------------------------------------------------------------------------------
-- - BeginTable() user begin into a table
-- | BeginChild() - (if ScrollX/ScrollY is set)
-- | TableBeginInitMemory() - first time table is used
-- | TableResetSettings() - on settings reset
-- | TableLoadSettings() - on settings load
-------------------------------------------------------------------------------
-- - TableSetupColumn() user submit columns details (optional)
-- - TableSetupScrollFreeze() user submit scroll freeze information (optional)
-------------------------------------------------------------------------------
-- - TableUpdateLayout() [Internal] followup to BeginTable(): setup everything: widths, columns positions, clipping rectangles. Automatically called by the FIRST call to TableNextRow() or TableHeadersRow().
-- | TableApplyQueuedRequests() - apply queued resizing/reordering/hiding requests
-- | - TableSetColumnWidth() - apply resizing width (for mouse resize, often requested by previous frame)
-- | - TableUpdateColumnsWeightFromWidth()- recompute columns weights (of stretch columns) from their respective width
-- | - TableSetColumnDisplayOrder() - apply reordering a column
-- | TableSetupDrawChannels() - setup ImDrawList channels
-- | TableUpdateBorders() - detect hovering columns for resize, ahead of contents submission
-- | TableBeginContextMenuPopup()
-- | - TableDrawDefaultContextMenu() - draw right-click context menu contents
-------------------------------------------------------------------------------
-- - TableHeadersRow() or TableHeader() user submit a headers row (optional)
-- | TableSortSpecsClickColumn() - when left-clicked: alter sort order and sort direction
-- | TableOpenContextMenu() - when right-clicked: trigger opening of the default context menu
-- - TableGetSortSpecs() user queries updated sort specs (optional, generally after submitting headers)
-- - TableNextRow() user begin into a new row (also automatically called by TableHeadersRow())
-- | TableEndRow() - finish existing row
-- | TableBeginRow() - add a new row
-- - TableSetColumnIndex() / TableNextColumn() user begin into a cell
-- | TableEndCell() - close existing column/cell
-- | TableBeginCell() - enter into current column/cell
-- - [...] user emit contents
-------------------------------------------------------------------------------
-- - EndTable() user ends the table
-- | TableDrawBorders() - draw outer borders, inner vertical borders
-- | TableMergeDrawChannels() - merge draw channels if clipping isn't required
-- | EndChild() - (if ScrollX/ScrollY is set)
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- TABLE SIZING
-------------------------------------------------------------------------------
-- (Read carefully because this is subtle but it does make sense!)
-------------------------------------------------------------------------------
-- About 'outer_size':
-- Its meaning needs to differ slightly depending on if we are using ScrollX/ScrollY flags.
-- Default value is ImVec2(0.0, 0.0).
-- X
-- - outer_size.x <= 0.0 -> Right-align from window/work-rect right-most edge. With -FLT_MIN or 0.0 will align exactly on right-most edge.
-- - outer_size.x > 0.0 -> Set Fixed width.
-- Y with ScrollX/ScrollY disabled: we output table directly in current window
-- - outer_size.y < 0.0 -> Bottom-align (but will auto extend, unless _NoHostExtendY is set). Not meaningful if parent window can vertically scroll.
-- - outer_size.y = 0.0 -> No minimum height (but will auto extend, unless _NoHostExtendY is set)
-- - outer_size.y > 0.0 -> Set Minimum height (but will auto extend, unless _NoHostExtendY is set)
-- Y with ScrollX/ScrollY enabled: using a child window for scrolling
-- - outer_size.y < 0.0 -> Bottom-align. Not meaningful if parent window can vertically scroll.
-- - outer_size.y = 0.0 -> Bottom-align, consistent with BeginChild(). Not recommended unless table is last item in parent window.
-- - outer_size.y > 0.0 -> Set Exact height. Recommended when using Scrolling on any axis.
-------------------------------------------------------------------------------
-- Outer size is also affected by the NoHostExtendX/NoHostExtendY flags.
-- Important to note how the two flags have slightly different behaviors!
-- - ImGuiTableFlags.NoHostExtendX -> Make outer width auto-fit to columns (overriding outer_size.x value). Only available when ScrollX/ScrollY are disabled and Stretch columns are not used.
-- - ImGuiTableFlags.NoHostExtendY -> Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY is disabled. Data below the limit will be clipped and not visible.
-- In theory ImGuiTableFlags.NoHostExtendY could be the default and any non-scrolling tables with outer_size.y ~= 0.0 would use exact height.
-- This would be consistent but perhaps less useful and more confusing (as vertically clipped items are not useful and not easily noticeable).
-------------------------------------------------------------------------------
-- About 'inner_width':
-- With ScrollX disabled:
-- - inner_width -> *ignored*
-- With ScrollX enabled:
-- - inner_width < 0.0 -> *illegal* fit in known width (right align from outer_size.x) <-- weird
-- - inner_width = 0.0 -> fit in outer_width: Fixed size columns will take space they need (if avail, otherwise shrink down), Stretch columns becomes Fixed columns.
-- - inner_width > 0.0 -> override scrolling width, generally to be larger than outer_size.x. Fixed column take space they need (if avail, otherwise shrink down), Stretch columns share remaining space!
-------------------------------------------------------------------------------
-- Details:
-- - If you want to use Stretch columns with ScrollX, you generally need to specify 'inner_width' otherwise the concept
-- of "available space" doesn't make sense.
-- - Even if not really useful, we allow 'inner_width < outer_size.x' for consistency and to facilitate understanding
-- of what the value does.
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- COLUMNS SIZING POLICIES
-- (Reference: ImGuiTableFlags.SizingXXX flags and ImGuiTableColumnFlags.WidthXXX flags)
-------------------------------------------------------------------------------
-- About overriding column sizing policy and width/weight with TableSetupColumn():
-- We use a default parameter of -1 for 'init_width'/'init_weight'.
-- - with ImGuiTableColumnFlags.WidthFixed, init_width <= 0 (default) --> width is automatic
-- - with ImGuiTableColumnFlags.WidthFixed, init_width > 0 (explicit) --> width is custom
-- - with ImGuiTableColumnFlags.WidthStretch, init_weight <= 0 (default) --> weight is 1.0
-- - with ImGuiTableColumnFlags.WidthStretch, init_weight > 0 (explicit) --> weight is custom
-- Widths are specified _without_ CellPadding. If you specify a width of 100.0, the column will be cover (100.0 + Padding * 2.0)
-- and you can fit a 100.0 wide item in it without clipping and with padding honored.
-------------------------------------------------------------------------------
-- About default sizing policy (if you don't specify a ImGuiTableColumnFlags.WidthXXXX flag)
-- - with Table policy ImGuiTableFlags.SizingFixedFit --> default Column policy is ImGuiTableColumnFlags.WidthFixed, default Width is equal to contents width
-- - with Table policy ImGuiTableFlags.SizingFixedSame --> default Column policy is ImGuiTableColumnFlags.WidthFixed, default Width is max of all contents width
-- - with Table policy ImGuiTableFlags.SizingStretchSame --> default Column policy is ImGuiTableColumnFlags.WidthStretch, default Weight is 1.0
-- - with Table policy ImGuiTableFlags.SizingStretchWeight --> default Column policy is ImGuiTableColumnFlags.WidthStretch, default Weight is proportional to contents
-- Default Width and default Weight can be overridden when calling TableSetupColumn().
-------------------------------------------------------------------------------
-- About mixing Fixed/Auto and Stretch columns together:
-- - the typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns.
-- - using mixed policies with ScrollX does not make much sense, as using Stretch columns with ScrollX does not make much sense in the first place!
-- that is, unless 'inner_width' is passed to BeginTable() to explicitly provide a total width to layout columns in.
-- - when using ImGuiTableFlags.SizingFixedSame with mixed columns, only the Fixed/Auto columns will match their widths to the width of the maximum contents.
-- - when using ImGuiTableFlags.SizingStretchSame with mixed columns, only the Stretch columns will match their weights/widths.
-------------------------------------------------------------------------------
-- About using column width:
-- If a column is manually resizable or has a width specified with TableSetupColumn():
-- - you may use GetContentRegionAvail().x to query the width available in a given column.
-- - right-side alignment features such as SetNextItemWidth(-x) or PushItemWidth(-x) will rely on this width.
-- If the column is not resizable and has no width specified with TableSetupColumn():
-- - its width will be automatic and be set to the max of items submitted.
-- - therefore you generally cannot have ALL items of the columns use e.g. SetNextItemWidth(-FLT_MIN).
-- - but if the column has one or more items of known/fixed size, this will become the reference width used by SetNextItemWidth(-FLT_MIN).
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- TABLES CLIPPING/CULLING
-------------------------------------------------------------------------------
-- About clipping/culling of Rows in Tables:
-- - For large numbers of rows, it is recommended you use ImGuiListClipper to submit only visible rows.
-- ImGuiListClipper is reliant on the fact that rows are of equal height.
-- See 'Demo->Tables->Vertical Scrolling' or 'Demo->Tables->Advanced' for a demo of using the clipper.
-- - Note that auto-resizing columns don't play well with using the clipper.
-- By default a table with _ScrollX but without _Resizable will have column auto-resize.
-- So, if you want to use the clipper, make sure to either enable _Resizable, either setup columns width explicitly with _WidthFixed.
-------------------------------------------------------------------------------
-- About clipping/culling of Columns in Tables:
-- - Both TableSetColumnIndex() and TableNextColumn() return true when the column is visible or performing
-- width measurements. Otherwise, you may skip submitting the contents of a cell/column, BUT ONLY if you know
-- it is not going to contribute to row height.
-- In many situations, you may skip submitting contents for every column but one (e.g. the first one).
-- - Case A: column is not hidden by user, and at least partially in sight (most common case).
-- - Case B: column is clipped / out of sight (because of scrolling or parent ClipRect): TableNextColumn() return false as a hint but we still allow layout output.
-- - Case C: column is hidden explicitly by the user (e.g. via the context menu, or _DefaultHide column flag, etc.).
--
-- [A] [B] [C]
-- TableNextColumn(): true false false -> [userland] when TableNextColumn() / TableSetColumnIndex() returns false, user can skip submitting items but only if the column doesn't contribute to row height.
-- SkipItems: false false true -> [internal] when SkipItems is true, most widgets will early out if submitted, resulting is no layout output.
-- ClipRect: normal zero-width zero-width -> [internal] when ClipRect is zero, ItemAdd() will return false and most widgets will early out mid-way.
-- ImDrawList output: normal dummy dummy -> [internal] when using the dummy channel, ImDrawList submissions (if any) will be wasted (because cliprect is zero-width anyway).
--
-- - We need to distinguish those cases because non-hidden columns that are clipped outside of scrolling bounds should still contribute their height to the row.
-- However, in the majority of cases, the contribution to row height is the same for all columns, or the tallest cells are known by the programmer.
-------------------------------------------------------------------------------
-- About clipping/culling of whole Tables:
-- - Scrolling tables with a known outer size can be clipped earlier as BeginTable() will return false.
-------------------------------------------------------------------------------
-- Port notes:
-- - Column indices / display orders / draw channel indices are 0-based VALUES like upstream.
-- Per-column arrays (table.Columns, table.DisplayOrderToIndex, table.RowCellData) are Lua tables with 0-based keys.
-- table.ColumnsSize mirrors upstream `table->Columns.size()`.
-- - ImBitArray masks are Lua tables { [n] = true }.
-- - table.ColumnsNames is a Lua array of strings, column.NameOffset is a 0-based index into it (-1 = none).
-- - g.SettingsTables is a plain vector of ImGuiTableSettings; table.SettingsOffset is a 1-based index into it (-1 = none).
-- Each settings entry owns `Columns` (0-based Lua table of ImGuiTableColumnSettings).
-- - ImGuiTableSortSpecs.Specs is a 1-based Lua array of ImGuiTableColumnSortSpecs.
--- @type ImGuiContext?
local GImGui
local MT = ImGui.GetMetatables()
-- Sets local `GImGui` in this file(imgui_tables.lua).
-- This is currently only used in main code `ImGui.SetCurrentContext()`
--- @param ctx ImGuiContext?
function ImGui._SetCurrentContext_Tables(ctx)
GImGui = ctx
end
local bit32 = bit32
local band, bor, bnot, lshift, rshift = bit32.band, bit32.bor, bit32.bnot, bit32.lshift, bit32.rshift
local mathMax, mathMin = math.max, math.min -- [Luau] scalar ImMax()/ImMin() in table code are written as mathMax()/mathMin() (no call through the vector-aware helper)
IM_COL32_DISABLE = IM_COL32(0, 0, 0, 1) -- Special sentinel code which cannot be used as a regular color.
IMGUI_TABLE_MAX_COLUMNS = 512 -- Arbitrary "safety" maximum, may be lifted in the future if needed. Must fit in ImGuiTableColumnIdx/ImGuiTableDrawChannelIdx.
local function IMGUI_DEBUG_LOG_TABLE(fmt, ...)
local g = GImGui
if band(g.DebugLogFlags, 4096 --[[ImGuiDebugLogFlags.EventTable]]) ~= 0 then ImGui.DebugLog(fmt, ...) end
end
-------------------------------------------------------------------------------
-- [SECTION] Structs
-------------------------------------------------------------------------------
local function BitArrayClearAll(arr) table.clear(arr) end
local function BitArraySet(arr, n) arr[n] = true end
local function BitTest(arr, n) return arr[n] == true end
-- Sorting specification for one column of a table
--- @class ImGuiTableColumnSortSpecs
function ImGuiTableColumnSortSpecs()
return { ColumnUserID = 0, ColumnIndex = 0, SortOrder = 0, SortDirection = 0 --[[ImGuiSortDirection.None]] }
end
-- Sorting specifications for a table (often handling sort specs for a single column, occasionally more)
-- Obtained by calling TableGetSortSpecs().
-- When 'SpecsDirty == true' you can sort your data. It will be true with sorting specs have changed since last call, or the first time.
-- Make sure to set 'SpecsDirty = false' after sorting, else you may wastefully sort your data every frame!
--- @class ImGuiTableSortSpecs
function ImGuiTableSortSpecs()
return { Specs = nil, SpecsCount = 0, SpecsDirty = false }
end
-- We use the terminology "Enabled" to refer to a column that is not Hidden by user/api.
-- We use the terminology "Clipped" to refer to a column that is out of sight because of scrolling/clipping.
-- This is in contrast with some user-facing api such as IsItemVisible() / IsRectVisible() which use "Visible" to mean "not clipped".
--- @class ImGuiTableColumn
function ImGuiTableColumn()
return {
Flags = 0, WidthGiven = 0.0, MinX = 0.0, MaxX = 0.0, WidthRequest = -1.0, WidthAuto = 0.0, WidthMax = 0.0,
StretchWeight = -1.0, InitStretchWeightOrWidth = 0.0, ClipRect = ImRect(), ID = 0, UserData = 0,
WorkMinX = 0.0, WorkMaxX = 0.0, ItemWidth = 0.0, ContentMaxXFrozen = 0.0, ContentMaxXUnfrozen = 0.0,
ContentMaxXHeadersUsed = 0.0, ContentMaxXHeadersIdeal = 0.0, NameOffset = -1, DisplayOrder = -1,
IndexWithinEnabledSet = -1, PrevEnabledColumn = -1, NextEnabledColumn = -1, SortOrder = -1,
DrawChannelCurrent = -1, DrawChannelFrozen = -1, DrawChannelUnfrozen = -1,
IsEnabled = false, IsUserEnabled = false, IsUserEnabledNextFrame = false, IsVisibleX = false, IsVisibleY = false,
IsRequestOutput = false, IsSkipItems = false, IsPreserveWidthAuto = false, IsJustCreated = true,
IsLoadedSettings = false, IsNeedReconcileSrc = false, IsNeedReconcileDst = false,
NavLayerCurrent = 0, AutoFitQueue = 0, CannotSkipItemsQueue = 0, SortDirection = 0 --[[ImGuiSortDirection.None]],
SortDirectionsAvailCount = 0, SortDirectionsAvailMask = 0, SortDirectionsAvailList = 0,
}
end
--- `*dst = *src`
local function TableColumnCopy(dst, src)
for k, v in pairs(src) do
if k ~= "ClipRect" then dst[k] = v end
end
ImRect_Copy(dst.ClipRect, src.ClipRect)
return dst
end
local function TableColumnClone(src)
return TableColumnCopy(ImGuiTableColumn(), src)
end
-- Per-instance data that needs preserving across frames (seemingly most others do not need to be preserved aside from debug needs. Does that means they could be moved to ImGuiTableTempData?)
--- @class ImGuiTableInstanceData
function ImGuiTableInstanceData()
return { TableInstanceID = 0, LastOuterHeight = 0.0, LastTopHeadersRowHeight = 0.0, LastFrozenHeight = 0.0, HoveredRowLast = -1, HoveredRowNext = -1 }
end
-- Transient data that are only needed between BeginTable() and EndTable(), those buffers are shared (1 per level of stacked table).
-- - Accessing those requires chasing an extra pointer so for very frequently used data we leave them in the main table structure.
-- - We also leave out of this structure data that tend to be particularly useful for debugging/metrics.
-- FIXME-TABLE: more transient data could be stored in a stacked ImGuiTableTempData: e.g. SortSpecs.
--- @class ImGuiTableTempData
function ImGuiTableTempData()
return {
WindowID = 0, TableIndex = 0, LastTimeActive = -1.0, AngledHeadersExtraWidth = 0.0,
AngledHeadersRequests = ImVector(), ReconcileColumnsRequests = ImVector(),
OldColumnsRawData = nil, OldColumnsData = nil, OldColumnsDataSize = 0,
UserOuterSize = ImVec2(), DrawSplitter = ImDrawListSplitter(),
HostBackupWorkRect = ImRect(), HostBackupParentWorkRect = ImRect(),
HostBackupPrevLineSize = ImVec2(), HostBackupCurrLineSize = ImVec2(), HostBackupCursorMaxPos = ImVec2(),
HostBackupColumnsOffset = 0.0, HostBackupItemWidth = 0.0, HostBackupItemWidthStackSize = 0,
}
end
-- Storage for a table
--- @class ImGuiTable
function ImGuiTable()
return {
ID = 0, Flags = 0, RawData = nil, TempData = nil,
Columns = {}, ColumnsSize = 0, DisplayOrderToIndex = {}, RowCellData = {},
EnabledMaskByDisplayOrder = {}, EnabledMaskByIndex = {}, VisibleMaskByIndex = {},
SettingsLoadedFlags = 0, SettingsOffset = 0, LastFrameActive = -1, ColumnsCount = 0, CurrentRow = 0, CurrentColumn = 0,
InstanceCurrent = 0, InstanceInteracted = 0, RowPosY1 = 0.0, RowPosY2 = 0.0, RowMinHeight = 0.0, RowCellPaddingY = 0.0,
RowTextBaseline = 0.0, RowIndentOffsetX = 0.0, RowFlags = 0, LastRowFlags = 0, RowBgColorCounter = 0,
RowBgColor = { [0] = 0, [1] = 0 }, BorderColorStrong = 0, BorderColorLight = 0, BorderX1 = 0.0, BorderX2 = 0.0,
HostIndentX = 0.0, MinColumnWidth = 0.0, OuterPaddingX = 0.0, CellPaddingX = 0.0, CellSpacingX1 = 0.0, CellSpacingX2 = 0.0,
InnerWidth = 0.0, ColumnsGivenWidth = 0.0, ColumnsAutoFitWidth = 0.0, ColumnsStretchSumWeights = 0.0,
ResizedColumnNextWidth = 0.0, ResizeLockMinContentsX2 = 0.0, RefScale = 0.0, AngledHeadersHeight = 0.0, AngledHeadersSlope = 0.0,
OuterRect = ImRect(), InnerRect = ImRect(), WorkRect = ImRect(), InnerClipRect = ImRect(), BgClipRect = ImRect(),
Bg0ClipRectForDrawCmd = ImRect(), Bg2ClipRectForDrawCmd = ImRect(), HostClipRect = ImRect(), HostBackupInnerClipRect = ImRect(),
OuterWindow = nil, InnerWindow = nil, ColumnsNames = {}, DrawSplitter = nil,
InstanceDataFirst = ImGuiTableInstanceData(), InstanceDataExtra = ImVector(),
SortSpecs = ImGuiTableSortSpecs(), SortSpecsMulti = {}, SortSpecsSingle = ImGuiTableColumnSortSpecs(),
SortSpecsCount = 0, ColumnsEnabledCount = 0, ColumnsEnabledFixedCount = 0, DeclColumnsCount = 0, AngledHeadersCount = 0,
HoveredColumnBody = 0, HoveredColumnBorder = 0, HighlightColumnHeader = 0, AutoFitSingleColumn = 0, ResizedColumn = 0,
LastResizedColumn = 0, HeldHeaderColumn = 0, LastHeldHeaderColumn = 0, ReorderColumn = 0, ReorderColumnDstOrder = 0,
LeftMostEnabledColumn = 0, RightMostEnabledColumn = 0, LeftMostStretchedColumn = 0, RightMostStretchedColumn = 0,
ContextPopupColumn = 0, FreezeRowsRequest = 0, FreezeRowsCount = 0, FreezeColumnsRequest = 0, FreezeColumnsCount = 0,
RowCellDataCurrent = 0, DummyDrawChannel = 0, Bg2DrawChannelCurrent = 0, Bg2DrawChannelUnfrozen = 0, NavLayer = 0,
IsLayoutLocked = false, IsInsideRow = false, IsNewTable = false, IsInitializing = false, IsReconcileMode = false,
IsSortSpecsDirty = false, IsUsingHeaders = false, IsContextPopupOpen = false, DisableDefaultContextMenu = false,
IsSettingsRequestLoad = false, IsSettingsDirty = false, IsDefaultDisplayOrder = false, IsDefaultVisibility = false,
IsResetAllRequest = false, IsResetDisplayOrderRequest = false, IsResetVisibilityRequest = false, IsUnfrozenRows = false,
IsDefaultSizingPolicy = false, IsActiveIdAliveBeforeTable = false, IsActiveIdInTable = false,
HasScrollbarYCurr = false, HasScrollbarYPrev = false, MemoryCompacted = false, HostSkipItems = false,
}
end
--- @class ImGuiTableColumnSettings
function ImGuiTableColumnSettings()
return { WidthOrWeight = 0.0, ID = 0, Index = -1, DisplayOrder = -1, SortOrder = -1, SortDirection = 0 --[[ImGuiSortDirection.None]],
IsEnabled = -1, IsStretch = 0, IsLoaded = false }
end
-- Storage for a table .ini settings
--- @class ImGuiTableSettings
MT.ImGuiTableSettings = {}
MT.ImGuiTableSettings.__index = MT.ImGuiTableSettings
function ImGuiTableSettings()
return setmetatable({ ID = 0, SaveFlags = 0, RefScale = 0.0, ColumnsCount = 0, ColumnsCountMax = 0, LastUsedDate = 0, WantApply = false, Columns = {} }, MT.ImGuiTableSettings)
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
--- @class ImGuiOldColumnData
function ImGuiOldColumnData()
return { OffsetNorm = 0.0, OffsetNormBeforeResize = 0.0, Flags = 0, ClipRect = ImRect() }
end
--- @class ImGuiOldColumns
function ImGuiOldColumns()
return { ID = 0, Flags = 0, IsFirstFrame = false, IsBeingResized = false, Current = 0, Count = 0, OffMinX = 0.0, OffMaxX = 0.0,
LineMinY = 0.0, LineMaxY = 0.0, HostCursorPosY = 0.0, HostCursorMaxPosX = 0.0, HostInitialClipRect = ImRect(),
HostBackupClipRect = ImRect(), HostBackupParentWorkRect = ImRect(), Columns = ImVector(), Splitter = ImDrawListSplitter() }
end
ImGuiTableColumnFlags.NoDirectResize_ = ImGuiTableColumnFlags.NoDirectResize_ or lshift(1, 30) -- [Internal] Disable user resizing this column directly (it may however we resized indirectly from its left edge)
local function Rect(r) return ImRect(r.Min.x, r.Min.y, r.Max.x, r.Max.y) end
local function V2(v) return ImVec2(v.x, v.y) end
-------------------------------------------------------------------------------
-- [SECTION] Tables: Main code
-------------------------------------------------------------------------------
-- - TableFixFlags() [Internal]
-- - TableFindByID() [Internal]
-- - BeginTable()
-- - BeginTableEx() [Internal]
-- - TableBeginInitMemory() [Internal]
-- - TableApplyQueuedRequests() [Internal]
-- - TableSetupColumnFlags() [Internal]
-- - TableUpdateLayout() [Internal]
-- - TableUpdateBorders() [Internal]
-- - EndTable()
-- - TableSetupColumn()
-- - TableSetupScrollFreeze()
-------------------------------------------------------------------------------
-- Configuration
local TABLE_DRAW_CHANNEL_BG0 = 0
local TABLE_DRAW_CHANNEL_BG2_FROZEN = 1
local TABLE_DRAW_CHANNEL_NOCLIP = 2 -- When using ImGuiTableFlags.NoClip (this becomes the last visible channel)
local TABLE_BORDER_SIZE = 1.0 -- FIXME-TABLE: Currently hard-coded because of clipping assumptions with outer borders rendering.
local TABLE_RESIZE_SEPARATOR_HALF_THICKNESS = 4.0 -- Extend outside inner borders.
local TABLE_RESIZE_SEPARATOR_FEEDBACK_TIMER = 0.06 -- Delay/timer before making the hover feedback (color+cursor) visible because tables/columns tends to be more cramped.
-- Helper
local function TableFixFlags(flags, outer_window)
-- Adjust flags: set default sizing policy
if band(flags, 57344 --[[ImGuiTableFlags.SizingMask_]]) == 0 then
flags = bor(flags, (band(flags, 16777216 --[[ImGuiTableFlags.ScrollX]]) ~= 0 or band(outer_window.Flags, 64 --[[ImGuiWindowFlags.AlwaysAutoResize]]) ~= 0) and 8192 --[[ImGuiTableFlags.SizingFixedFit]] or 32768 --[[ImGuiTableFlags.SizingStretchSame]])
end
-- Adjust flags: enable NoKeepColumnsVisible when using ImGuiTableFlags.SizingFixedSame
if band(flags, 57344 --[[ImGuiTableFlags.SizingMask_]]) == 16384 --[[ImGuiTableFlags.SizingFixedSame]] then
flags = bor(flags, 262144 --[[ImGuiTableFlags.NoKeepColumnsVisible]])
end
-- Adjust flags: enforce borders when resizable
if band(flags, 1 --[[ImGuiTableFlags.Resizable]]) ~= 0 then
flags = bor(flags, 512 --[[ImGuiTableFlags.BordersInnerV]])
end
-- Adjust flags: disable NoHostExtendX/NoHostExtendY if we have any scrolling going on
if band(flags, bor(16777216 --[[ImGuiTableFlags.ScrollX]], 33554432 --[[ImGuiTableFlags.ScrollY]])) ~= 0 then
flags = band(flags, bnot(bor(65536 --[[ImGuiTableFlags.NoHostExtendX]], 131072 --[[ImGuiTableFlags.NoHostExtendY]])))
end
-- Adjust flags: NoBordersInBodyUntilResize takes priority over NoBordersInBody
if band(flags, 4096 --[[ImGuiTableFlags.NoBordersInBodyUntilResize]]) ~= 0 then
flags = band(flags, bnot(2048 --[[ImGuiTableFlags.NoBordersInBody]]))
end
-- Adjust flags: disable saved settings if there's nothing to save
if band(flags, bor(1 --[[ImGuiTableFlags.Resizable]], 4 --[[ImGuiTableFlags.Hideable]], 2 --[[ImGuiTableFlags.Reorderable]], 8 --[[ImGuiTableFlags.Sortable]])) == 0 then
flags = bor(flags, 16 --[[ImGuiTableFlags.NoSavedSettings]])
end
-- Inherit _NoSavedSettings from top-level window (child windows always have _NoSavedSettings set)
if band(outer_window.RootWindow.Flags, 256 --[[ImGuiWindowFlags.NoSavedSettings]]) ~= 0 then
flags = bor(flags, 16 --[[ImGuiTableFlags.NoSavedSettings]])
end
return flags
end
function ImGui.TableFindByID(id)
local g = GImGui
return g.Tables:GetByKey(id)
end
--- @param table_ ImGuiTable
--- @param instance_no int
function ImGui.TableGetInstanceData(table_, instance_no)
if instance_no == 0 then
return table_.InstanceDataFirst
end
return table_.InstanceDataExtra.Data[instance_no]
end
function ImGui.TableGetInstanceID(table_, instance_no)
return ImGui.TableGetInstanceData(table_, instance_no).TableInstanceID
end
-- Read about "TABLE SIZING" at the top of this file.
--- @param str_id string
--- @param columns_count int
--- @param flags? ImGuiTableFlags
--- @param outer_size? ImVec2
--- @param inner_width? float
function ImGui.BeginTable(str_id, columns_count, flags, outer_size, inner_width)
local id = ImGui.GetID(str_id)
return ImGui.BeginTableEx(str_id, id, columns_count, flags, outer_size, inner_width)
end
function ImGui.BeginTableEx(name, id, columns_count, flags, outer_size, inner_width)
if flags == nil then flags = 0 end
if outer_size == nil then outer_size = ImVec2(0.0, 0.0) end
if inner_width == nil then inner_width = 0.0 end
local g = GImGui
local outer_window = ImGui.GetCurrentWindow()
if outer_window.SkipItems then -- Consistent with other tables + beneficial side effect that assert on miscalling EndTable() will be more visible.
return false
end
-- Sanity checks
assert(columns_count > 0 and columns_count < 512 --[[IMGUI_TABLE_MAX_COLUMNS]])
if band(flags, 16777216 --[[ImGuiTableFlags.ScrollX]]) ~= 0 then
assert(inner_width >= 0.0)
end
-- If an outer size is specified ahead we will be able to early out when not visible. Exact clipping criteria may evolve.
-- FIXME: coarse clipping because access to table data causes two issues:
-- - instance numbers varying/unstable. may not be a direct problem for users, but could make outside access broken or confusing, e.g. TestEngine.
-- - can't implement support for ImGuiChildFlags.ResizeY as we need to somehow pull the height data from somewhere. this also needs stable instance numbers.
-- The side-effects of accessing table data on coarse clip would be:
-- - always reserving the pooled ImGuiTable data ahead for a fully clipped table (minor IMHO). Also the 'outer_window_is_measuring_size' criteria may already be defeating this in some situations.
-- - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[].
local use_child_window = band(flags, bor(16777216 --[[ImGuiTableFlags.ScrollX]], 33554432 --[[ImGuiTableFlags.ScrollY]])) ~= 0
local avail_size = ImGui.GetContentRegionAvail()
local actual_outer_size = ImTrunc(ImGui.CalcItemSize(outer_size, mathMax(avail_size.x, 4 --[[IMGUI_WINDOW_HARD_MIN_SIZE]]), use_child_window and mathMax(avail_size.y, 4 --[[IMGUI_WINDOW_HARD_MIN_SIZE]]) or 0.0))
local outer_rect = ImRect(outer_window.DC.CursorPos, outer_window.DC.CursorPos + actual_outer_size)
local outer_window_is_measuring_size = (outer_window.AutoFitFramesX > 0) or (outer_window.AutoFitFramesY > 0) -- Doesn't apply to AlwaysAutoResize windows!
if use_child_window and ImGui.IsClippedEx(outer_rect, 0) and not outer_window_is_measuring_size then
ImGui.ItemSize(outer_rect)
ImGui.ItemAdd(outer_rect, id)
g.NextWindowData:ClearFlags()
return false
end
-- Acquire storage for the table
local table_ = g.Tables:GetOrAddByKey(id)
if table_.Columns == nil then -- pool created a plain table
for k, v in pairs(ImGuiTable()) do table_[k] = v end
end
-- Acquire temporary buffers
local table_idx = g.Tables:GetIndex(table_)
g.TablesTempDataStacked = g.TablesTempDataStacked + 1
while g.TablesTempDataStacked > g.TablesTempData.Size do
g.TablesTempData:push_back(ImGuiTableTempData())
end
local temp_data = g.TablesTempData.Data[g.TablesTempDataStacked]
table_.TempData = temp_data
temp_data.TableIndex = table_idx
temp_data.ReconcileColumnsRequests:resize(0) -- FIXME: Use shrink(0) everywhere?
table_.DrawSplitter = table_.TempData.DrawSplitter
table_.DrawSplitter:Clear()
-- Fix flags
table_.IsDefaultSizingPolicy = band(flags, 57344 --[[ImGuiTableFlags.SizingMask_]]) == 0
flags = TableFixFlags(flags, outer_window)
-- Initialize
local previous_frame_active = table_.LastFrameActive
local instance_no = (previous_frame_active ~= g.FrameCount) and 0 or (table_.InstanceCurrent + 1)
local previous_flags = table_.Flags
table_.ID = id
table_.Flags = flags
table_.LastFrameActive = g.FrameCount
table_.OuterWindow = outer_window
table_.InnerWindow = outer_window
table_.ColumnsCount = columns_count
table_.IsLayoutLocked = false
table_.InnerWidth = inner_width
table_.NavLayer = outer_window.DC.NavLayerCurrent
table_.IsNewTable = (previous_frame_active == -1)
ImVec2_Copy(temp_data.UserOuterSize, outer_size)
-- Instance data (for instance 0, TableID == TableInstanceID)
local instance_id
table_.InstanceCurrent = instance_no
if instance_no > 0 then
assert(table_.ColumnsCount == columns_count, "BeginTable(): Cannot change columns count mid-frame while preserving same ID")
if table_.InstanceDataExtra.Size < instance_no then
table_.InstanceDataExtra:push_back(ImGuiTableInstanceData())
end
instance_id = ImGui.GetIDWithSeed(instance_no, ImGui.GetIDWithSeed("##Instances", nil, id)) -- Push "##Instances" followed by (int)instance_no in ID stack.
else
instance_id = id
end
local table_instance = ImGui.TableGetInstanceData(table_, table_.InstanceCurrent)
table_instance.TableInstanceID = instance_id
-- When not using a child window, WorkRect.Max will grow as we append contents.
if use_child_window then
-- Ensure no vertical scrollbar appears if we only want horizontal one, to make flag consistent
-- (we have no other way to disable vertical scrollbar of a window while keeping the horizontal one showing)
local override_content_size = ImVec2(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]])
if band(flags, 16777216 --[[ImGuiTableFlags.ScrollX]]) ~= 0 and band(flags, 33554432 --[[ImGuiTableFlags.ScrollY]]) == 0 then
override_content_size.y = 1.1754943508223e-38 --[[FLT_MIN]]
end
-- Ensure specified width (when not specified, Stretched columns will act as if the width == OuterWidth and
-- never lead to any scrolling). We don't handle inner_width < 0.0, we could potentially use it to right-align
-- based on the right side of the child window work rect, which would require knowing ahead if we are going to
-- have decoration taking horizontal spaces (typically a vertical scrollbar).
if band(flags, 16777216 --[[ImGuiTableFlags.ScrollX]]) ~= 0 and inner_width > 0.0 then
override_content_size.x = inner_width
end
if override_content_size.x ~= 3.4028234663853e+38 --[[FLT_MAX]] or override_content_size.y ~= 3.4028234663853e+38 --[[FLT_MAX]] then
ImGui.SetNextWindowContentSize(ImVec2((override_content_size.x ~= 3.4028234663853e+38 --[[FLT_MAX]]) and override_content_size.x or 0.0, (override_content_size.y ~= 3.4028234663853e+38 --[[FLT_MAX]]) and override_content_size.y or 0.0))
end
-- Reset scroll if we are reactivating it
if band(previous_flags, bor(16777216 --[[ImGuiTableFlags.ScrollX]], 33554432 --[[ImGuiTableFlags.ScrollY]])) == 0 then
if band(g.NextWindowData.HasFlags, 128 --[[ImGuiNextWindowDataFlags.HasScroll]]) == 0 then
ImGui.SetNextWindowScroll(ImVec2(0.0, 0.0))
end
end
-- Create scrolling region (without border and zero window padding)
local child_window_flags = (band(flags, 16777216 --[[ImGuiTableFlags.ScrollX]]) ~= 0) and 2048 --[[ImGuiWindowFlags.HorizontalScrollbar]] or 0 --[[ImGuiWindowFlags.None]]
ImGui.BeginChildEx(name, instance_id, outer_rect:GetSize(), 0 --[[ImGuiChildFlags.None]], child_window_flags)
table_.InnerWindow = g.CurrentWindow
ImRect_Copy(table_.WorkRect, table_.InnerWindow.WorkRect)
ImRect_Copy(table_.OuterRect, table_.InnerWindow:Rect())
ImRect_Copy(table_.InnerRect, table_.InnerWindow.InnerRect)
assert(table_.InnerWindow.WindowPadding.x == 0.0 and table_.InnerWindow.WindowPadding.y == 0.0 and table_.InnerWindow.WindowBorderSize == 0.0)
-- Allow submitting when host is measuring
if table_.InnerWindow.SkipItems and outer_window_is_measuring_size then
table_.InnerWindow.SkipItems = false
end
-- When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned
if instance_no == 0 then
table_.HasScrollbarYPrev = table_.HasScrollbarYCurr
table_.HasScrollbarYCurr = false
end
table_.HasScrollbarYCurr = table_.HasScrollbarYCurr or table_.InnerWindow.ScrollbarY
else
-- For non-scrolling tables, WorkRect == OuterRect == InnerRect.
-- But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable().
ImRect_Copy(table_.WorkRect, outer_rect)
ImRect_Copy(table_.OuterRect, outer_rect)
ImRect_Copy(table_.InnerRect, outer_rect)
table_.HasScrollbarYPrev = false
table_.HasScrollbarYCurr = false
table_.InnerWindow.DC.TreeDepth = table_.InnerWindow.DC.TreeDepth + 1 -- This is designed to always linking ImGuiTreeNodeFlags.DrawLines linking across a table
end
-- Push a standardized ID for both child-using and not-child-using tables
ImGui.PushOverrideID(id)
if instance_no > 0 then
ImGui.PushOverrideID(instance_id) -- FIXME: Somehow this is not resolved by stack-tool, even tho GetIDWithSeed() submitted the symbol.
end
-- Backup a copy of host window members we will modify
local inner_window = table_.InnerWindow
table_.HostIndentX = inner_window.DC.Indent.x
ImRect_Copy(table_.HostClipRect, inner_window.ClipRect)
table_.HostSkipItems = inner_window.SkipItems
temp_data.WindowID = inner_window.ID
ImRect_Copy(temp_data.HostBackupWorkRect, inner_window.WorkRect)
ImRect_Copy(temp_data.HostBackupParentWorkRect, inner_window.ParentWorkRect)
temp_data.HostBackupColumnsOffset = outer_window.DC.ColumnsOffset.x
ImVec2_Copy(temp_data.HostBackupPrevLineSize, inner_window.DC.PrevLineSize)
ImVec2_Copy(temp_data.HostBackupCurrLineSize, inner_window.DC.CurrLineSize)
ImVec2_Copy(temp_data.HostBackupCursorMaxPos, inner_window.DC.CursorMaxPos)
temp_data.HostBackupItemWidth = outer_window.DC.ItemWidth
temp_data.HostBackupItemWidthStackSize = outer_window.DC.ItemWidthStack.Size
inner_window.DC.PrevLineSize.x = 0.0; inner_window.DC.PrevLineSize.y = 0.0
inner_window.DC.CurrLineSize.x = 0.0; inner_window.DC.CurrLineSize.y = 0.0
-- Make borders not overlap our contents by offsetting HostClipRect (#6765, #7428, #3752)
-- (we normally shouldn't alter HostClipRect as we rely on TableMergeDrawChannels() expanding non-clipped column toward the
-- limits of that rectangle, in order for ImDrawListSplitter::Merge() to merge the draw commands. However since the overlap
-- problem only affect scrolling tables in this case we can get away with doing it without extra cost).
if inner_window ~= outer_window then
-- FIXME: Because inner_window's Scrollbar doesn't know about border size, since it's not encoded in window.WindowBorderSize,
-- it already overlaps it and doesn't need an extra offset. Ideally we should be able to pass custom border size with
-- different x/y values to BeginChild().
local border_size = TABLE_BORDER_SIZE
if band(flags, 1024 --[[ImGuiTableFlags.BordersOuterV]]) ~= 0 then
table_.HostClipRect.Min.x = mathMin(table_.HostClipRect.Min.x + border_size, table_.HostClipRect.Max.x)
if inner_window.DecoOuterSizeX2 == 0.0 then
table_.HostClipRect.Max.x = mathMax(table_.HostClipRect.Max.x - border_size, table_.HostClipRect.Min.x)
end
end
if band(flags, 256 --[[ImGuiTableFlags.BordersOuterH]]) ~= 0 then
table_.HostClipRect.Min.y = mathMin(table_.HostClipRect.Min.y + border_size, table_.HostClipRect.Max.y)
if inner_window.DecoOuterSizeY2 == 0.0 then
table_.HostClipRect.Max.y = mathMax(table_.HostClipRect.Max.y - border_size, table_.HostClipRect.Min.y)
end
end
end
-- Padding and Spacing
-- - None ........Content..... Pad .....Content........
-- - PadOuter | Pad ..Content..... Pad .....Content.. Pad |
-- - PadInner ........Content.. Pad | Pad ..Content........
-- - PadOuter+PadInner | Pad ..Content.. Pad | Pad ..Content.. Pad |
local pad_outer_x
if band(flags, 4194304 --[[ImGuiTableFlags.NoPadOuterX]]) ~= 0 then pad_outer_x = false
elseif band(flags, 2097152 --[[ImGuiTableFlags.PadOuterX]]) ~= 0 then pad_outer_x = true
else pad_outer_x = band(flags, 1024 --[[ImGuiTableFlags.BordersOuterV]]) ~= 0 end
local pad_inner_x = band(flags, 8388608 --[[ImGuiTableFlags.NoPadInnerX]]) == 0
local inner_spacing_for_border = (band(flags, 512 --[[ImGuiTableFlags.BordersInnerV]]) ~= 0) and TABLE_BORDER_SIZE or 0.0
local inner_spacing_explicit = (pad_inner_x and band(flags, 512 --[[ImGuiTableFlags.BordersInnerV]]) == 0) and g.Style.CellPadding.x or 0.0
local inner_padding_explicit = (pad_inner_x and band(flags, 512 --[[ImGuiTableFlags.BordersInnerV]]) ~= 0) and g.Style.CellPadding.x or 0.0
table_.CellSpacingX1 = inner_spacing_explicit + inner_spacing_for_border
table_.CellSpacingX2 = inner_spacing_explicit
table_.CellPaddingX = inner_padding_explicit
local outer_padding_for_border = (band(flags, 1024 --[[ImGuiTableFlags.BordersOuterV]]) ~= 0) and TABLE_BORDER_SIZE or 0.0
local outer_padding_explicit = pad_outer_x and g.Style.CellPadding.x or 0.0
table_.OuterPaddingX = (outer_padding_for_border + outer_padding_explicit) - table_.CellPaddingX
table_.CurrentColumn = -1
table_.CurrentRow = -1
table_.RowBgColorCounter = 0
table_.LastRowFlags = 0 --[[ImGuiTableRowFlags.None]]
ImRect_Copy(table_.InnerClipRect, (inner_window == outer_window) and table_.WorkRect or inner_window.ClipRect)
table_.InnerClipRect:ClipWith(table_.WorkRect) -- We need this to honor inner_width
table_.InnerClipRect:ClipWithFull(table_.HostClipRect)
table_.InnerClipRect.Max.y = (band(flags, 131072 --[[ImGuiTableFlags.NoHostExtendY]]) ~= 0) and mathMin(table_.InnerClipRect.Max.y, inner_window.WorkRect.Max.y) or table_.HostClipRect.Max.y
table_.RowPosY1 = table_.WorkRect.Min.y -- This is needed somehow
table_.RowPosY2 = table_.WorkRect.Min.y
table_.RowTextBaseline = 0.0 -- This will be cleared again by TableBeginRow()
table_.RowCellPaddingY = 0.0
table_.FreezeRowsRequest = 0; table_.FreezeRowsCount = 0 -- This will be setup by TableSetupScrollFreeze(), if any
table_.FreezeColumnsRequest = 0; table_.FreezeColumnsCount = 0
table_.IsUnfrozenRows = true
table_.DeclColumnsCount = 0; table_.AngledHeadersCount = 0
if previous_frame_active + 1 < g.FrameCount then
table_.IsActiveIdInTable = false
end
table_.AngledHeadersHeight = 0.0
temp_data.AngledHeadersExtraWidth = 0.0
-- Using opaque colors facilitate overlapping lines of the grid, otherwise we'd need to improve TableDrawBorders()
table_.BorderColorStrong = ImGui.GetColorU32(49 --[[ImGuiCol.TableBorderStrong]])
table_.BorderColorLight = ImGui.GetColorU32(50 --[[ImGuiCol.TableBorderLight]])
-- Make table current
g.CurrentTable = table_
inner_window.DC.NavIsScrollPushableX = false -- Shortcut for NavUpdateCurrentWindowIsScrollPushableX();
outer_window.DC.CurrentTableIdx = table_idx
if inner_window ~= outer_window then -- So EndChild() within the inner window can restore the table properly.
inner_window.DC.CurrentTableIdx = table_idx
end
if band(previous_flags, 2 --[[ImGuiTableFlags.Reorderable]]) ~= 0 and band(flags, 2 --[[ImGuiTableFlags.Reorderable]]) == 0 then
table_.IsResetDisplayOrderRequest = true
end
-- Mark as used to avoid GC
while table_idx >= g.TablesLastTimeActive.Size do
g.TablesLastTimeActive:push_back(-1.0)
end
g.TablesLastTimeActive.Data[table_idx + 1] = g.Time
temp_data.LastTimeActive = g.Time
table_.MemoryCompacted = false
-- Setup memory buffer (clear data if columns count changed)
local old_columns_count = table_.ColumnsSize
if old_columns_count ~= 0 and old_columns_count ~= columns_count then
-- Attempt to preserve width and other settings on column count/specs change (#4046, #9108)
IMGUI_DEBUG_LOG_TABLE("[table] Table 0x%08X column count %d -> %d, recreating storage.\n", table_.ID, old_columns_count, columns_count)
assert(temp_data.OldColumnsRawData == nil)
temp_data.OldColumnsRawData = table_.RawData -- Freed during layout
temp_data.OldColumnsData = table_.Columns
temp_data.OldColumnsDataSize = table_.ColumnsSize
for n = 0, temp_data.OldColumnsDataSize - 1 do
temp_data.OldColumnsData[n].IsNeedReconcileSrc = true
end
table_.RawData = nil
end
if table_.RawData == nil then
ImGui.TableBeginInitMemory(table_, columns_count)
table_.IsInitializing = true
end
if table_.IsResetAllRequest then
ImGui.TableResetSettings(table_)
end
if table_.IsInitializing then
-- Initialize
if table_.IsNewTable then
table_.SettingsOffset = -1
table_.IsSettingsRequestLoad = true
end
table_.IsSortSpecsDirty = true
table_.IsSettingsDirty = true -- Records itself into .ini file even when in default state (#7934)
table_.IsReconcileMode = false
table_.InstanceInteracted = -1
table_.ContextPopupColumn = -1
table_.ReorderColumn = -1; table_.ReorderColumnDstOrder = -1; table_.ResizedColumn = -1; table_.LastResizedColumn = -1
table_.AutoFitSingleColumn = -1
table_.HoveredColumnBody = -1; table_.HoveredColumnBorder = -1
for n = 0, columns_count - 1 do
local column = table_.Columns[n]
if temp_data.OldColumnsData and n < temp_data.OldColumnsDataSize then
TableColumnCopy(column, temp_data.OldColumnsData[n])
else
local width_auto = column.WidthAuto
TableColumnCopy(column, ImGuiTableColumn())
column.WidthAuto = width_auto
column.IsPreserveWidthAuto = true -- Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker
column.IsEnabled = true; column.IsUserEnabled = true; column.IsUserEnabledNextFrame = true
column.DisplayOrder = n
end
table_.DisplayOrderToIndex[n] = column.DisplayOrder
end
end
-- Load settings
if table_.IsSettingsRequestLoad then
ImGui.TableLoadSettings(table_)
end
-- Disable output until user calls TableNextRow() or TableNextColumn() leading to the TableUpdateLayout() call..
-- This is not strictly necessary but will reduce cases were "out of table" output will be misleading to the user.
-- Because we cannot safely assert in EndTable() when no rows have been created, this seems like our best option.
inner_window.SkipItems = true
-- Clear names
-- At this point the .NameOffset field of each column will be invalid until TableUpdateLayout() or the first call to TableSetupColumn()
if #table_.ColumnsNames > 0 then
table.clear(table_.ColumnsNames)
end
return true
end
function ImGui.TableBeginInitMemory(table_, columns_count)
table_.RawData = true
table_.Columns = {}
table_.DisplayOrderToIndex = {}
table_.RowCellData = {}
for n = 0, columns_count - 1 do
table_.Columns[n] = ImGuiTableColumn()
table_.Columns[n].WidthAuto = 0.0
table_.DisplayOrderToIndex[n] = 0
table_.RowCellData[n] = { BgColor = 0, Column = 0 }
end
table_.ColumnsSize = columns_count
table_.EnabledMaskByDisplayOrder = {}
table_.EnabledMaskByIndex = {}
table_.VisibleMaskByIndex = {}
end
-- Apply queued resizing/reordering/hiding requests
function ImGui.TableApplyQueuedRequests(table_)
-- Handle resizing request
-- (We process this in the TableBegin() of the first instance of each table)
-- FIXME-TABLE: Contains columns if our work area doesn't allow for scrolling?
if table_.InstanceCurrent == 0 then
if table_.ResizedColumn ~= -1 and table_.ResizedColumnNextWidth ~= 3.4028234663853e+38 --[[FLT_MAX]] then
ImGui.TableSetColumnWidth(table_.ResizedColumn, table_.ResizedColumnNextWidth)
end
table_.LastResizedColumn = table_.ResizedColumn
table_.ResizedColumnNextWidth = 3.4028234663853e+38 --[[FLT_MAX]]
table_.ResizedColumn = -1
-- Process auto-fit for single column, which is a special case for stretch columns and fixed columns with FixedSame policy.
-- FIXME-TABLE: Would be nice to redistribute available stretch space accordingly to other weights, instead of giving it all to siblings.
if table_.AutoFitSingleColumn ~= -1 then
ImGui.TableSetColumnWidth(table_.AutoFitSingleColumn, table_.Columns[table_.AutoFitSingleColumn].WidthAuto)
table_.AutoFitSingleColumn = -1
end
end
-- Handle reordering request
-- Note: we don't clear ReorderColumn after handling the request (FIXME: clarify why or add a test).
if table_.InstanceCurrent == 0 then
table_.LastHeldHeaderColumn = table_.HeldHeaderColumn
table_.HeldHeaderColumn = -1
if table_.ReorderColumn ~= -1 and table_.ReorderColumnDstOrder ~= -1 then
ImGui.TableSetColumnDisplayOrder(table_, table_.ReorderColumn, table_.ReorderColumnDstOrder)
table_.ReorderColumnDstOrder = -1
end
-- Release
local g = GImGui
if g.ActiveId == 0 then -- FIXME: Need to revisit. See 38f5e5a.
table_.ReorderColumn = -1
end
--if (table.HeldHeaderColumn == -1 and table.ReorderColumn ~= -1)
-- table.ReorderColumn = -1
end
-- Handle display order / visibility reset requests
if table_.IsResetDisplayOrderRequest then
for n = 0, table_.ColumnsCount - 1 do
table_.Columns[n].DisplayOrder = n
table_.DisplayOrderToIndex[n] = n
end
table_.IsResetDisplayOrderRequest = false
table_.IsSettingsDirty = true
end
if table_.IsResetVisibilityRequest then
for n = 0, table_.ColumnsSize - 1 do
local column = table_.Columns[n]
local v = band(column.Flags, 2 --[[ImGuiTableColumnFlags.DefaultHide]]) == 0
column.IsUserEnabled = v; column.IsUserEnabledNextFrame = v
end
table_.IsResetVisibilityRequest = false
table_.IsSettingsDirty = true
end
end
-- Apply immediately. See TableQueueSetColumnDisplayOrder() for additional checks/constraints.
function ImGui.TableSetColumnDisplayOrder(table_, column_n, dst_order)
assert(column_n >= 0 and column_n < table_.ColumnsCount)
assert(dst_order >= 0 and dst_order < table_.ColumnsCount)
local src_column = table_.Columns[column_n]
local src_order = src_column.DisplayOrder
if src_order == dst_order then
return
end
local reorder_dir = (dst_order < src_order) and -1 or 1
src_column.DisplayOrder = dst_order
local order_n = src_order + reorder_dir
while order_n ~= dst_order + reorder_dir do
local c = table_.Columns[table_.DisplayOrderToIndex[order_n]]
c.DisplayOrder = c.DisplayOrder - reorder_dir
order_n = order_n + reorder_dir
end
--IM_ASSERT(dst_column.DisplayOrder == dst_order - reorder_dir)
-- Display order is stored in both columns.IndexDisplayOrder and table.DisplayOrder[]. Rebuild later from the former.
-- FIXME-OPT: If this is called multiple times we'd effectively have a O(N^2) thing going on.
for n = 0, table_.ColumnsCount - 1 do
table_.DisplayOrderToIndex[table_.Columns[n].DisplayOrder] = n
end
table_.IsSettingsDirty = true
end
local function TableGetMaxDisplayOrderAllowed(table_, src_order, dst_order)
dst_order = ImClamp(dst_order, 0, table_.ColumnsCount - 1)
if src_order == dst_order then
return dst_order
end
-- Cannot cross over the frozen column limit when interactively reordering.
-- TableSetupScrollFreeze() enforce a display order range for frozen columns. Reordering across the frozen column barrier is illegal and will be undone.
if table_.FreezeColumnsRequest > 0 then
dst_order = (src_order < table_.FreezeColumnsRequest) and ImMin(dst_order, table_.FreezeColumnsRequest - 1) or ImMax(dst_order, table_.FreezeColumnsRequest)
end
-- Cannot cross over a column with the ImGuiTableColumnFlags.NoReorder flag.
local reorder_dir = (src_order < dst_order) and 1 or -1
local order_n = src_order
while (src_order < dst_order and order_n <= dst_order) or (dst_order < src_order and order_n >= dst_order) do
if band(table_.Columns[table_.DisplayOrderToIndex[order_n]].Flags, 64 --[[ImGuiTableColumnFlags.NoReorder]]) ~= 0 then
dst_order = (order_n == src_order) and src_order or (order_n - reorder_dir)
break
end
order_n = order_n + reorder_dir
end
return dst_order
end
-- Reorder requested by user interaction.
function ImGui.TableQueueSetColumnDisplayOrder(table_, column_n, dst_order)
local src_order = table_.Columns[column_n].DisplayOrder
table_.ReorderColumn = column_n
table_.ReorderColumnDstOrder = -1
dst_order = TableGetMaxDisplayOrderAllowed(table_, src_order, dst_order)
if table_.IsLayoutLocked and dst_order == src_order then -- We allow calling the function before layout w/ reconcile so don't early out.
return
end
table_.ReorderColumnDstOrder = dst_order
end
-- Adjust flags: default width mode + stretch columns are not allowed when auto extending
local function TableSetupColumnFlags(table_, column, column_n, flags_in)
local flags = flags_in
-- Sizing Policy
if band(flags, 24 --[[ImGuiTableColumnFlags.WidthMask_]]) == 0 then
local table_sizing_policy = band(table_.Flags, 57344 --[[ImGuiTableFlags.SizingMask_]])
if table_sizing_policy == 8192 --[[ImGuiTableFlags.SizingFixedFit]] or table_sizing_policy == 16384 --[[ImGuiTableFlags.SizingFixedSame]] then
flags = bor(flags, 16 --[[ImGuiTableColumnFlags.WidthFixed]])
else
flags = bor(flags, 8 --[[ImGuiTableColumnFlags.WidthStretch]])
end
else
assert(ImIsPowerOfTwo(band(flags, 24 --[[ImGuiTableColumnFlags.WidthMask_]]))) -- Check that only 1 of each set is used.
end
-- Resize
if band(table_.Flags, 1 --[[ImGuiTableFlags.Resizable]]) == 0 then
flags = bor(flags, 32 --[[ImGuiTableColumnFlags.NoResize]])
end
-- Sorting
if band(flags, 1024 --[[ImGuiTableColumnFlags.NoSortAscending]]) ~= 0 and band(flags, 2048 --[[ImGuiTableColumnFlags.NoSortDescending]]) ~= 0 then
flags = bor(flags, 512 --[[ImGuiTableColumnFlags.NoSort]])
end
-- Indentation
if band(flags, 196608 --[[ImGuiTableColumnFlags.IndentMask_]]) == 0 then
flags = bor(flags, (column_n == 0) and 65536 --[[ImGuiTableColumnFlags.IndentEnable]] or 131072 --[[ImGuiTableColumnFlags.IndentDisable]])
end
-- Alignment
--if (flags & ImGuiTableColumnFlags.AlignMask_) == 0 then
-- flags = bor(flags, ImGuiTableColumnFlags.AlignCenter)
--IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiTableColumnFlags.AlignMask_)) -- Check that only 1 of each set is used.
-- Preserve status flags
column.Flags = bor(flags, band(column.Flags, 251658240 --[[ImGuiTableColumnFlags.StatusMask_]]))
-- Build an ordered list of available sort directions
column.SortDirectionsAvailCount = 0; column.SortDirectionsAvailMask = 0; column.SortDirectionsAvailList = 0
if band(table_.Flags, 8 --[[ImGuiTableFlags.Sortable]]) ~= 0 then
local count, mask, list = 0, 0, 0
local Asc, Desc = 1 --[[ImGuiSortDirection.Ascending]], 2 --[[ImGuiSortDirection.Descending]]
if band(flags, 16384 --[[ImGuiTableColumnFlags.PreferSortAscending]]) ~= 0 and band(flags, 1024 --[[ImGuiTableColumnFlags.NoSortAscending]]) == 0 then mask = bor(mask, lshift(1, Asc)); list = bor(list, lshift(Asc, lshift(count, 1))); count = count + 1 end
if band(flags, 32768 --[[ImGuiTableColumnFlags.PreferSortDescending]]) ~= 0 and band(flags, 2048 --[[ImGuiTableColumnFlags.NoSortDescending]]) == 0 then mask = bor(mask, lshift(1, Desc)); list = bor(list, lshift(Desc, lshift(count, 1))); count = count + 1 end
if band(flags, 16384 --[[ImGuiTableColumnFlags.PreferSortAscending]]) == 0 and band(flags, 1024 --[[ImGuiTableColumnFlags.NoSortAscending]]) == 0 then mask = bor(mask, lshift(1, Asc)); list = bor(list, lshift(Asc, lshift(count, 1))); count = count + 1 end
if band(flags, 32768 --[[ImGuiTableColumnFlags.PreferSortDescending]]) == 0 and band(flags, 2048 --[[ImGuiTableColumnFlags.NoSortDescending]]) == 0 then mask = bor(mask, lshift(1, Desc)); list = bor(list, lshift(Desc, lshift(count, 1))); count = count + 1 end
if band(table_.Flags, 134217728 --[[ImGuiTableFlags.SortTristate]]) ~= 0 or count == 0 then mask = bor(mask, lshift(1, 0 --[[ImGuiSortDirection.None]])); count = count + 1 end
column.SortDirectionsAvailList = band(list, 0xFF)
column.SortDirectionsAvailMask = band(mask, 0xFF)
column.SortDirectionsAvailCount = count
ImGui.TableFixColumnSortDirection(table_, column)
end
end
-- Layout columns for the frame. This is in essence the followup to BeginTable() and this is our largest function.
-- Runs on the first call to TableNextRow(), to give a chance for TableSetupColumn() and other TableSetupXXXXX() functions to be called first.
-- FIXME-TABLE: Our width (and therefore our WorkRect) will be minimal in the first frame for _WidthAuto columns.
-- Increase feedback side-effect with widgets relying on WorkRect.Max.x... Maybe provide a default distribution for _WidthAuto columns?
function ImGui.TableUpdateLayout(table_)
local g = GImGui
local temp_data = table_.TempData
assert(table_.IsLayoutLocked == false)
local columns_count = table_.ColumnsCount
-- Reconcile moved columns
if temp_data.ReconcileColumnsRequests.Size > 0 then
ImGui.TableReconcileColumns(table_)
end
if temp_data.OldColumnsRawData then
temp_data.OldColumnsRawData = nil
temp_data.OldColumnsData = nil
temp_data.OldColumnsDataSize = 0
end
-- Apply columns settings
if table_.IsSettingsRequestLoad then
ImGui.TableLoadSettingsForColumns(table_)
end
if table_.IsInitializing or table_.IsSettingsRequestLoad then
for n = 0, table_.ColumnsSize - 1 do
local column = table_.Columns[n]
local init_flags
if table_.IsSettingsRequestLoad then
init_flags = column.IsLoadedSettings and bnot(table_.SettingsLoadedFlags) or bnot(0)
else
init_flags = column.IsJustCreated and bnot(0) or 0
end
ImGui.TableInitColumnDefaults(table_, column, n, init_flags)
end
ImGui.TableFixDisplayOrder(table_) -- Call even for non _Reorderable table as we loaded .ini data.
table_.IsSettingsRequestLoad = false
end
-- Apply queued resizing/reordering/hiding requests
ImGui.TableApplyQueuedRequests(table_)
-- Handle DPI/font resize
-- This is designed to facilitate DPI changes with the assumption that e.g. style.CellPadding has been scaled as well.
-- It will also react to changing fonts with mixed results. It doesn't need to be perfect but merely provide a decent transition.
-- FIXME-DPI: Provide consistent standards for reference size. Perhaps using g.CurrentDpiScale would be more self explanatory.
-- This is will lead us to non-rounded WidthRequest in columns, which should work but is a poorly tested path.
local new_ref_scale_unit = g.FontSize -- g.Font.GetCharAdvance('A') ?
if table_.RefScale ~= 0.0 and table_.RefScale ~= new_ref_scale_unit then
local scale_factor = new_ref_scale_unit / table_.RefScale
for n = 0, columns_count - 1 do
table_.Columns[n].WidthRequest = table_.Columns[n].WidthRequest * scale_factor
end
end
table_.RefScale = new_ref_scale_unit
local table_sizing_policy = band(table_.Flags, 57344 --[[ImGuiTableFlags.SizingMask_]])
table_.IsDefaultDisplayOrder = true; table_.IsDefaultVisibility = true
table_.ColumnsEnabledCount = 0
BitArrayClearAll(table_.EnabledMaskByIndex)
BitArrayClearAll(table_.EnabledMaskByDisplayOrder)
table_.LeftMostEnabledColumn = -1
table_.MinColumnWidth = mathMax(1.0, g.Style.FramePadding.x * 1.0) -- g.Style.ColumnsMinSpacing -- FIXME-TABLE
-- [Part 1] Apply/lock Enabled and Order states. Calculate auto/ideal width for columns. Count fixed/stretch columns.
-- Process columns in their visible orders as we are building the Prev/Next indices.
local count_fixed = 0 -- Number of columns that have fixed sizing policies
local count_stretch = 0 -- Number of columns that have stretch sizing policies
local prev_visible_column_idx = -1
local has_auto_fit_request = false
local has_resizable = false
local stretch_sum_width_auto = 0.0
local fixed_max_width_auto = 0.0
for order_n = 0, columns_count - 1 do
local column_n = table_.DisplayOrderToIndex[order_n]
local column = table_.Columns[column_n]
-- Clear column setup if not submitted by user. Currently we make it mandatory to call TableSetupColumn() every frame.
-- It would easily work without but we're not ready to guarantee it since e.g. names need resubmission anyway.
-- We take a slight shortcut but in theory we could be calling TableSetupColumn() here with dummy values, it should yield the same effect.
if table_.DeclColumnsCount <= column_n then
TableSetupColumnFlags(table_, column, column_n, 0 --[[ImGuiTableColumnFlags.None]])
column.NameOffset = -1
column.ID = 0; column.UserData = 0
column.InitStretchWeightOrWidth = -1.0
end
-- Update Enabled state, mark settings and sort specs dirty
if band(table_.Flags, 4 --[[ImGuiTableFlags.Hideable]]) == 0 or band(column.Flags, 128 --[[ImGuiTableColumnFlags.NoHide]]) ~= 0 then
column.IsUserEnabledNextFrame = true
end
if column.IsUserEnabled ~= column.IsUserEnabledNextFrame then
column.IsUserEnabled = column.IsUserEnabledNextFrame
table_.IsSettingsDirty = true
end
column.IsEnabled = column.IsUserEnabled and band(column.Flags, 1 --[[ImGuiTableColumnFlags.Disabled]]) == 0
column.IsJustCreated = false
if column.IsEnabled ~= (band(column.Flags, 2 --[[ImGuiTableColumnFlags.DefaultHide]]) == 0) then
table_.IsDefaultVisibility = false
end
if column_n ~= order_n then
table_.IsDefaultDisplayOrder = false
end
if column.SortOrder ~= -1 and not column.IsEnabled then
table_.IsSortSpecsDirty = true
end
if column.SortOrder > 0 and band(table_.Flags, 67108864 --[[ImGuiTableFlags.SortMulti]]) == 0 then
table_.IsSortSpecsDirty = true
end
-- Auto-fit unsized columns
local start_auto_fit
if band(column.Flags, 16 --[[ImGuiTableColumnFlags.WidthFixed]]) ~= 0 then
start_auto_fit = column.WidthRequest < 0.0
else
start_auto_fit = column.StretchWeight < 0.0
end
if start_auto_fit then
column.AutoFitQueue = lshift(1, 3) - 1 -- Fit for three frames
column.CannotSkipItemsQueue = lshift(1, 3) - 1
end
if not column.IsEnabled then
column.IndexWithinEnabledSet = -1
else
-- Mark as enabled and link to previous/next enabled column
column.PrevEnabledColumn = prev_visible_column_idx
column.NextEnabledColumn = -1
if prev_visible_column_idx ~= -1 then
table_.Columns[prev_visible_column_idx].NextEnabledColumn = column_n
else
table_.LeftMostEnabledColumn = column_n
end
column.IndexWithinEnabledSet = table_.ColumnsEnabledCount
table_.ColumnsEnabledCount = table_.ColumnsEnabledCount + 1
BitArraySet(table_.EnabledMaskByIndex, column_n)
BitArraySet(table_.EnabledMaskByDisplayOrder, column.DisplayOrder)
prev_visible_column_idx = column_n
assert(column.IndexWithinEnabledSet <= column.DisplayOrder)
-- Calculate ideal/auto column width (that's the width required for all contents to be visible without clipping)
-- Combine width from regular rows + width from headers unless requested not to.
if not column.IsPreserveWidthAuto and table_.InstanceCurrent == 0 then
column.WidthAuto = ImGui.TableGetColumnWidthAuto(table_, column)
end
-- Non-resizable columns keep their requested width (apply user value regardless of IsPreserveWidthAuto)
local column_is_resizable = band(column.Flags, 32 --[[ImGuiTableColumnFlags.NoResize]]) == 0
if column_is_resizable then
has_resizable = true
end
if band(column.Flags, 16 --[[ImGuiTableColumnFlags.WidthFixed]]) ~= 0 and column.InitStretchWeightOrWidth > 0.0 and not column_is_resizable then
column.WidthAuto = column.InitStretchWeightOrWidth
end
if column.AutoFitQueue ~= 0x00 then
has_auto_fit_request = true
end
if band(column.Flags, 8 --[[ImGuiTableColumnFlags.WidthStretch]]) ~= 0 then
stretch_sum_width_auto = stretch_sum_width_auto + column.WidthAuto
count_stretch = count_stretch + 1
else
fixed_max_width_auto = mathMax(fixed_max_width_auto, column.WidthAuto)
count_fixed = count_fixed + 1
end
end
end
if band(table_.Flags, 8 --[[ImGuiTableFlags.Sortable]]) ~= 0 and table_.SortSpecsCount == 0 and band(table_.Flags, 134217728 --[[ImGuiTableFlags.SortTristate]]) == 0 then
table_.IsSortSpecsDirty = true
end
table_.RightMostEnabledColumn = prev_visible_column_idx
assert(table_.LeftMostEnabledColumn >= 0 and table_.RightMostEnabledColumn >= 0)
-- [Part 2] Disable child window clipping while fitting columns. This is not strictly necessary but makes it possible to avoid
-- the column fitting having to wait until the first visible frame of the child container (may or not be a good thing). Also see #6510.
-- FIXME-TABLE: for always auto-resizing columns may not want to do that all the time.
if has_auto_fit_request and table_.OuterWindow ~= table_.InnerWindow then
table_.InnerWindow.SkipItems = false
end
if has_auto_fit_request then
table_.IsSettingsDirty = true
end
-- [Part 3] Fix column flags and record a few extra information.
local sum_width_requests = 0.0 -- Sum of all width for fixed and auto-resize columns, excluding width contributed by Stretch columns but including spacing/padding.
local stretch_sum_weights = 0.0 -- Sum of all weights for stretch columns.
table_.LeftMostStretchedColumn = -1; table_.RightMostStretchedColumn = -1
for column_n = 0, columns_count - 1 do
if BitTest(table_.EnabledMaskByIndex, column_n) then
local column = table_.Columns[column_n]
local column_is_resizable = band(column.Flags, 32 --[[ImGuiTableColumnFlags.NoResize]]) == 0
if band(column.Flags, 16 --[[ImGuiTableColumnFlags.WidthFixed]]) ~= 0 then
-- Apply same widths policy
local width_auto = column.WidthAuto
if table_sizing_policy == 16384 --[[ImGuiTableFlags.SizingFixedSame]] and (column.AutoFitQueue ~= 0x00 or not column_is_resizable) then
width_auto = fixed_max_width_auto
end
-- Apply automatic width
-- Latch initial size for fixed columns and update it constantly for auto-resizing column (unless clipped!)
if column.AutoFitQueue ~= 0x00 then
column.WidthRequest = width_auto
elseif band(column.Flags, 16 --[[ImGuiTableColumnFlags.WidthFixed]]) ~= 0 and not column_is_resizable and column.IsRequestOutput then
column.WidthRequest = width_auto
end
-- FIXME-TABLE: Increase minimum size during init frame to avoid biasing auto-fitting widgets
-- (e.g. TextWrapped) too much. Otherwise what tends to happen is that TextWrapped would output a very
-- large height (= first frame scrollbar display very off + clipper would skip lots of items).
-- This is merely making the side-effect less extreme, but doesn't properly fixes it.
-- FIXME: Move this to .WidthGiven to avoid temporary lossyness?
-- FIXME: This break IsPreserveWidthAuto from not flickering if the stored WidthAuto was smaller.
if column.AutoFitQueue > 0x01 and table_.IsInitializing and not column.IsPreserveWidthAuto then
column.WidthRequest = mathMax(column.WidthRequest, table_.MinColumnWidth * 4.0) -- FIXME-TABLE: Another constant/scale?
end
sum_width_requests = sum_width_requests + column.WidthRequest
else
-- Initialize stretch weight
if column.AutoFitQueue ~= 0x00 or column.StretchWeight < 0.0 or not column_is_resizable then
if column.InitStretchWeightOrWidth > 0.0 then
column.StretchWeight = column.InitStretchWeightOrWidth
elseif table_sizing_policy == 24576 --[[ImGuiTableFlags.SizingStretchProp]] then
column.StretchWeight = (column.WidthAuto / stretch_sum_width_auto) * count_stretch
else
column.StretchWeight = 1.0
end
end
stretch_sum_weights = stretch_sum_weights + column.StretchWeight
if table_.LeftMostStretchedColumn == -1 or table_.Columns[table_.LeftMostStretchedColumn].DisplayOrder > column.DisplayOrder then
table_.LeftMostStretchedColumn = column_n
end
if table_.RightMostStretchedColumn == -1 or table_.Columns[table_.RightMostStretchedColumn].DisplayOrder < column.DisplayOrder then
table_.RightMostStretchedColumn = column_n
end
end
column.IsPreserveWidthAuto = false
sum_width_requests = sum_width_requests + table_.CellPaddingX * 2.0
end
end
table_.ColumnsEnabledFixedCount = count_fixed
table_.ColumnsStretchSumWeights = stretch_sum_weights
-- [Part 4] Apply final widths based on requested widths
local work_rect = Rect(table_.WorkRect)
local width_spacings = (table_.OuterPaddingX * 2.0) + (table_.CellSpacingX1 + table_.CellSpacingX2) * (table_.ColumnsEnabledCount - 1)
local width_removed = (table_.HasScrollbarYPrev and not table_.InnerWindow.ScrollbarY) and g.Style.ScrollbarSize or 0.0 -- To synchronize decoration width of synced tables with mismatching scrollbar state (#5920)
local width_avail = mathMax(1.0, ((band(table_.Flags, 16777216 --[[ImGuiTableFlags.ScrollX]]) ~= 0 and table_.InnerWidth == 0.0) and table_.InnerClipRect:GetWidth() or work_rect:GetWidth()) - width_removed)
local width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests
local width_remaining_for_stretched_columns = width_avail_for_stretched_columns
table_.ColumnsGivenWidth = width_spacings + (table_.CellPaddingX * 2.0) * table_.ColumnsEnabledCount
for column_n = 0, columns_count - 1 do
if BitTest(table_.EnabledMaskByIndex, column_n) then
local column = table_.Columns[column_n]
-- Allocate width for stretched/weighted columns (StretchWeight gets converted into WidthRequest)
if band(column.Flags, 8 --[[ImGuiTableColumnFlags.WidthStretch]]) ~= 0 then
local weight_ratio = column.StretchWeight / stretch_sum_weights
column.WidthRequest = IM_TRUNC(mathMax(width_avail_for_stretched_columns * weight_ratio, table_.MinColumnWidth) + 0.01)
width_remaining_for_stretched_columns = width_remaining_for_stretched_columns - column.WidthRequest
end
-- [Resize Rule 1] The right-most Visible column is not resizable if there is at least one Stretch column
-- See additional comments in TableSetColumnWidth().
if column.NextEnabledColumn == -1 and table_.LeftMostStretchedColumn ~= -1 then
column.Flags = bor(column.Flags, 1073741824 --[[ImGuiTableColumnFlags.NoDirectResize_]])
end
-- Assign final width, record width in case we will need to shrink
column.WidthGiven = ImTrunc(mathMax(column.WidthRequest, table_.MinColumnWidth))
table_.ColumnsGivenWidth = table_.ColumnsGivenWidth + column.WidthGiven
end
end
-- [Part 5] Redistribute stretch remainder width due to rounding (remainder width is < 1.0 * number of Stretch column).
-- Using right-to-left distribution (more likely to match resizing cursor).
if width_remaining_for_stretched_columns >= 1.0 and band(table_.Flags, 524288 --[[ImGuiTableFlags.PreciseWidths]]) == 0 then
local order_n = columns_count - 1
while stretch_sum_weights > 0.0 and width_remaining_for_stretched_columns >= 1.0 and order_n >= 0 do
if BitTest(table_.EnabledMaskByDisplayOrder, order_n) then
local column = table_.Columns[table_.DisplayOrderToIndex[order_n]]
if band(column.Flags, 8 --[[ImGuiTableColumnFlags.WidthStretch]]) ~= 0 then
column.WidthRequest = column.WidthRequest + 1.0
column.WidthGiven = column.WidthGiven + 1.0
width_remaining_for_stretched_columns = width_remaining_for_stretched_columns - 1.0
end
end
order_n = order_n - 1
end
end
-- Determine if table is hovered which will be used to flag columns as hovered.
-- - In principle we'd like to use the equivalent of IsItemHovered(ImGuiHoveredFlags.AllowWhenBlockedByActiveItem),
-- but because our item is partially submitted at this point we use ItemHoverable() and a workaround (temporarily
-- clear ActiveId, which is equivalent to the change provided by _AllowWhenBLockedByActiveItem).
-- - This allows columns to be marked as hovered when e.g. clicking a button inside the column, or using drag and drop.
local table_instance = ImGui.TableGetInstanceData(table_, table_.InstanceCurrent)
table_instance.HoveredRowLast = table_instance.HoveredRowNext
table_instance.HoveredRowNext = -1
table_.HoveredColumnBody = -1; table_.HoveredColumnBorder = -1
local mouse_hit_rect = ImRect(table_.OuterRect.Min.x, table_.OuterRect.Min.y, table_.OuterRect.Max.x, mathMax(table_.OuterRect.Max.y, table_.OuterRect.Min.y + table_instance.LastOuterHeight))
local backup_active_id = g.ActiveId
g.ActiveId = 0
local is_hovering_table = ImGui.ItemHoverable(mouse_hit_rect, 0, 0 --[[ImGuiItemFlags.None]])
g.ActiveId = backup_active_id
-- Determine skewed MousePos.x to support angled headers.
local mouse_skewed_x = g.IO.MousePos.x
if table_.AngledHeadersHeight > 0.0 then
if g.IO.MousePos.y >= table_.OuterRect.Min.y and g.IO.MousePos.y <= table_.OuterRect.Min.y + table_.AngledHeadersHeight then
mouse_skewed_x = mouse_skewed_x + ImTrunc((table_.OuterRect.Min.y + table_.AngledHeadersHeight - g.IO.MousePos.y) * table_.AngledHeadersSlope)
end
end
-- [Part 6] Setup final position, offset, skip/clip states and clipping rectangles, detect hovered column
-- Process columns in their visible orders as we are comparing the visible order and adjusting host_clip_rect while looping.
local has_at_least_one_column_requesting_output = false
local offset_x_frozen = (table_.FreezeColumnsCount > 0)
local offset_x = ((table_.FreezeColumnsCount > 0) and table_.OuterRect.Min.x or work_rect.Min.x) + table_.OuterPaddingX - table_.CellSpacingX1
local host_clip_rect = Rect(table_.InnerClipRect)
--host_clip_rect.Max.x += table.CellPaddingX + table.CellSpacingX2
BitArrayClearAll(table_.VisibleMaskByIndex)
for order_n = 0, columns_count - 1 do
local column_n = table_.DisplayOrderToIndex[order_n]
local column = table_.Columns[column_n]
-- Initial nav layer: using FreezeRowsCount, NOT FreezeRowsRequest, so Header line changes layer when frozen
column.NavLayerCurrent = (table_.FreezeRowsCount > 0) and 1 --[[ImGuiNavLayer.Menu]] or table_.NavLayer
if offset_x_frozen and table_.FreezeColumnsCount == order_n then
offset_x = offset_x + work_rect.Min.x - table_.OuterRect.Min.x
offset_x_frozen = false
end
-- Clear status flags
column.Flags = band(column.Flags, bnot(251658240 --[[ImGuiTableColumnFlags.StatusMask_]]))
if not BitTest(table_.EnabledMaskByDisplayOrder, order_n) then
-- Hidden column: clear a few fields and we are done with it for the remainder of the function.
-- We set a zero-width clip rect but set Min.y/Max.y properly to not interfere with the clipper.
column.MinX = offset_x; column.MaxX = offset_x; column.WorkMinX = offset_x
column.ClipRect.Min.x = offset_x; column.ClipRect.Max.x = offset_x
column.WidthGiven = 0.0
column.ClipRect.Min.y = work_rect.Min.y
column.ClipRect.Max.y = 3.4028234663853e+38 --[[FLT_MAX]]
column.ClipRect:ClipWithFull(host_clip_rect)
column.IsVisibleX = false; column.IsVisibleY = false; column.IsRequestOutput = false
column.IsSkipItems = true
column.ItemWidth = 1.0
else
-- Lock start position
column.MinX = offset_x
-- Lock width based on start position and minimum/maximum width for this position
column.WidthMax = ImGui.TableCalcMaxColumnWidth(table_, column_n)
column.WidthGiven = mathMin(column.WidthGiven, column.WidthMax)
column.WidthGiven = mathMax(column.WidthGiven, mathMin(column.WidthRequest, table_.MinColumnWidth))
column.MaxX = offset_x + column.WidthGiven + table_.CellSpacingX1 + table_.CellSpacingX2 + table_.CellPaddingX * 2.0
-- Lock other positions
-- - ClipRect.Min.x: Because merging draw commands doesn't compare min boundaries, we make ClipRect.Min.x match left bounds to be consistent regardless of merging.
-- - ClipRect.Max.x: using WorkMaxX instead of MaxX (aka including padding) makes things more consistent when resizing down, tho slightly detrimental to visibility in very-small column.
-- - ClipRect.Max.x: using MaxX makes it easier for header to receive hover highlight with no discontinuity and display sorting arrow.
-- - FIXME-TABLE: We want equal width columns to have equal (ClipRect.Max.x - WorkMinX) width, which means ClipRect.max.x cannot stray off host_clip_rect.Max.x else right-most column may appear shorter.
local previous_instance_work_min_x = column.WorkMinX
column.WorkMinX = column.MinX + table_.CellPaddingX + table_.CellSpacingX1
column.WorkMaxX = column.MaxX - table_.CellPaddingX - table_.CellSpacingX2 -- Expected max
column.ItemWidth = ImTrunc(column.WidthGiven * 0.65)
column.ClipRect.Min.x = column.MinX
column.ClipRect.Min.y = work_rect.Min.y
column.ClipRect.Max.x = column.MaxX --column.WorkMaxX
column.ClipRect.Max.y = 3.4028234663853e+38 --[[FLT_MAX]]
column.ClipRect:ClipWithFull(host_clip_rect)
-- Mark column as Clipped (not in sight)
-- Note that scrolling tables (where inner_window ~= outer_window) handle Y clipped earlier in BeginTable() so IsVisibleY really only applies to non-scrolling tables.
-- FIXME-TABLE: Because InnerClipRect.Max.y is conservatively ==outer_window.ClipRect.Max.y, we never can mark columns _Above_ the scroll line as not IsVisibleY.
-- Taking advantage of LastOuterHeight would yield good results there...
-- FIXME-TABLE: Y clipping is disabled because it effectively means not submitting will reduce contents width which is fed to outer_window.DC.CursorMaxPos.x,
-- and this may be used (e.g. typically by outer_window using AlwaysAutoResize or outer_window's horizontal scrollbar, but could be something else).
-- Possible solution to preserve last known content width for clipped column. Test 'table_reported_size' fails when enabling Y clipping and window is resized small.
column.IsVisibleX = (column.ClipRect.Max.x > column.ClipRect.Min.x)
column.IsVisibleY = true -- (column.ClipRect.Max.y > column.ClipRect.Min.y)
local is_visible = column.IsVisibleX --and column.IsVisibleY
if is_visible then
BitArraySet(table_.VisibleMaskByIndex, column_n)
end
-- Mark column as requesting output from user. Note that fixed + non-resizable sets are auto-fitting at all times and therefore always request output.
column.IsRequestOutput = is_visible or column.AutoFitQueue ~= 0 or column.CannotSkipItemsQueue ~= 0
-- Mark column as SkipItems (ignoring all items/layout)
-- (table.HostSkipItems is a copy of inner_window.SkipItems before we cleared it above in Part 2)
column.IsSkipItems = not column.IsEnabled or table_.HostSkipItems
if column.IsSkipItems then
assert(not is_visible)
end
if column.IsRequestOutput and not column.IsSkipItems then
has_at_least_one_column_requesting_output = true
end
-- Update status flags
column.Flags = bor(column.Flags, 16777216 --[[ImGuiTableColumnFlags.IsEnabled]])
if is_visible then
column.Flags = bor(column.Flags, 33554432 --[[ImGuiTableColumnFlags.IsVisible]])
end
if column.SortOrder ~= -1 then
column.Flags = bor(column.Flags, 67108864 --[[ImGuiTableColumnFlags.IsSorted]])
end
-- Detect hovered column
if is_hovering_table and mouse_skewed_x >= column.ClipRect.Min.x and mouse_skewed_x < column.ClipRect.Max.x then
column.Flags = bor(column.Flags, 134217728 --[[ImGuiTableColumnFlags.IsHovered]])
table_.HoveredColumnBody = column_n
end
-- Alignment
-- FIXME-TABLE: This align based on the whole column width, not per-cell, and therefore isn't useful in
-- many cases (to be able to honor this we might be able to store a log of cells width, per row, for
-- visible rows, but nav/programmatic scroll would have visible artifacts.)
--if (column.Flags & ImGuiTableColumnFlags.AlignRight)
-- column.WorkMinX = ImMax(column.WorkMinX, column.MaxX - column.ContentWidthRowsUnfrozen)
--else if (column.Flags & ImGuiTableColumnFlags.AlignCenter)
-- column.WorkMinX = ImLerp(column.WorkMinX, ImMax(column.StartX, column.MaxX - column.ContentWidthRowsUnfrozen), 0.5)
-- Reset content width variables
if table_.InstanceCurrent == 0 then
column.ContentMaxXFrozen = column.WorkMinX
column.ContentMaxXUnfrozen = column.WorkMinX
column.ContentMaxXHeadersUsed = column.WorkMinX
column.ContentMaxXHeadersIdeal = column.WorkMinX
else
-- As we store an absolute value to make per-cell updates faster, we need to offset values used for width computation.
local offset_from_previous_instance = column.WorkMinX - previous_instance_work_min_x
column.ContentMaxXFrozen = column.ContentMaxXFrozen + offset_from_previous_instance
column.ContentMaxXUnfrozen = column.ContentMaxXUnfrozen + offset_from_previous_instance
column.ContentMaxXHeadersUsed = column.ContentMaxXHeadersUsed + offset_from_previous_instance
column.ContentMaxXHeadersIdeal = column.ContentMaxXHeadersIdeal + offset_from_previous_instance
end
-- Don't decrement auto-fit counters until container window got a chance to submit its items
if table_.HostSkipItems == false and table_.InstanceCurrent == 0 then
column.AutoFitQueue = rshift(column.AutoFitQueue, 1)
column.CannotSkipItemsQueue = rshift(column.CannotSkipItemsQueue, 1)
end
if order_n < table_.FreezeColumnsCount then
host_clip_rect.Min.x = ImClamp(column.MaxX + TABLE_BORDER_SIZE, host_clip_rect.Min.x, host_clip_rect.Max.x)
end
offset_x = offset_x + column.WidthGiven + table_.CellSpacingX1 + table_.CellSpacingX2 + table_.CellPaddingX * 2.0
end
end
-- In case the table is visible (e.g. decorations) but all columns clipped, we keep a column visible.
-- Else if give no chance to a clipper-savvy user to submit rows and therefore total contents height used by scrollbar.
if has_at_least_one_column_requesting_output == false then
table_.Columns[table_.LeftMostEnabledColumn].IsRequestOutput = true
table_.Columns[table_.LeftMostEnabledColumn].IsSkipItems = false
end
-- [Part 7] Detect/store when we are hovering the unused space after the right-most column (so e.g. context menus can react on it)
-- Clear Resizable flag if none of our column are actually resizable (either via an explicit _NoResize flag, either
-- because of using _WidthAuto/_WidthStretch). This will hide the resizing option from the context menu.
local unused_x1 = mathMax(table_.WorkRect.Min.x, table_.Columns[table_.RightMostEnabledColumn].ClipRect.Max.x)
if is_hovering_table and table_.HoveredColumnBody == -1 then
if mouse_skewed_x >= unused_x1 then
table_.HoveredColumnBody = columns_count
end
end
if has_resizable == false and band(table_.Flags, 1 --[[ImGuiTableFlags.Resizable]]) ~= 0 then
table_.Flags = band(table_.Flags, bnot(1 --[[ImGuiTableFlags.Resizable]]))
end
table_.IsActiveIdAliveBeforeTable = (g.ActiveIdIsAlive ~= 0)
-- [Part 8] Lock actual OuterRect/WorkRect right-most position.
-- This is done late to handle the case of fixed-columns tables not claiming more widths that they need.
-- Because of this we are careful with uses of WorkRect and InnerClipRect before this point.
if table_.RightMostStretchedColumn ~= -1 then
table_.Flags = band(table_.Flags, bnot(65536 --[[ImGuiTableFlags.NoHostExtendX]]))
end
if band(table_.Flags, 65536 --[[ImGuiTableFlags.NoHostExtendX]]) ~= 0 then
table_.OuterRect.Max.x = unused_x1; table_.WorkRect.Max.x = unused_x1
table_.InnerClipRect.Max.x = mathMin(table_.InnerClipRect.Max.x, unused_x1)
end
ImRect_Copy(table_.InnerWindow.ParentWorkRect, table_.WorkRect)
table_.BorderX1 = table_.InnerClipRect.Min.x
table_.BorderX2 = table_.InnerClipRect.Max.x
-- Setup window's WorkRect.Max.y for GetContentRegionAvail(). Other values will be updated in each TableBeginCell() call.
local window_content_max_y
if band(table_.Flags, 131072 --[[ImGuiTableFlags.NoHostExtendY]]) ~= 0 then
window_content_max_y = table_.OuterRect.Max.y
else
window_content_max_y = mathMax(table_.InnerWindow.ContentRegionRect.Max.y, (band(table_.Flags, 33554432 --[[ImGuiTableFlags.ScrollY]]) ~= 0) and 0.0 or table_.OuterRect.Max.y)
end
table_.InnerWindow.WorkRect.Max.y = ImClamp(window_content_max_y - g.Style.CellPadding.y, table_.InnerWindow.WorkRect.Min.y, table_.InnerWindow.WorkRect.Max.y)
-- [Part 9] Allocate draw channels and setup background cliprect
ImGui.TableSetupDrawChannels(table_)
-- [Part 10] Hit testing on borders
if band(table_.Flags, 1 --[[ImGuiTableFlags.Resizable]]) ~= 0 then
ImGui.TableUpdateBorders(table_)
end
table_instance.LastTopHeadersRowHeight = 0.0
table_.IsLayoutLocked = true
table_.IsUsingHeaders = false
-- Highlight header
table_.HighlightColumnHeader = -1
if table_.IsContextPopupOpen and table_.ContextPopupColumn ~= -1 and table_.InstanceInteracted == table_.InstanceCurrent then
table_.HighlightColumnHeader = table_.ContextPopupColumn
elseif band(table_.Flags, 268435456 --[[ImGuiTableFlags.HighlightHoveredColumn]]) ~= 0 and table_.HoveredColumnBody ~= -1 and table_.HoveredColumnBody ~= columns_count and table_.HoveredColumnBorder == -1 then
if g.ActiveId == 0 or (table_.IsActiveIdInTable or g.DragDropActive) then
table_.HighlightColumnHeader = table_.HoveredColumnBody
end
end
-- [Part 11] Default context menu
-- - To append to this menu: you can call TableBeginContextMenuPopup()/.../EndPopup().
-- - To modify or replace this: set table.DisableDefaultContextMenu = true, then call TableBeginContextMenuPopup()/.../EndPopup().
-- - You may call TableDrawDefaultContextMenu() with selected flags to display specific sections of the default menu,
-- e.g. TableDrawDefaultContextMenu(table, table.Flags & ~ImGuiTableFlags.Hideable) will display everything EXCEPT columns visibility options.
if table_.DisableDefaultContextMenu == false and ImGui.TableBeginContextMenuPopup(table_) then
ImGui.TableDrawDefaultContextMenu(table_, table_.Flags)
ImGui.EndPopup()
end
-- [Part 12] Sanitize and build sort specs before we have a chance to use them for display.
-- This path will only be exercised when sort specs are modified before header rows (e.g. init or visibility change)
if table_.IsSortSpecsDirty and band(table_.Flags, 8 --[[ImGuiTableFlags.Sortable]]) ~= 0 then
ImGui.TableSortSpecsBuild(table_)
end
-- [Part 13] Setup inner window decoration size (for scrolling / nav tracking to properly take account of frozen rows/columns)
if table_.FreezeColumnsRequest > 0 then
table_.InnerWindow.DecoInnerSizeX1 = table_.Columns[table_.DisplayOrderToIndex[table_.FreezeColumnsRequest - 1]].MaxX - table_.OuterRect.Min.x -- FIXME-FROZEN
end
if table_.FreezeRowsRequest > 0 then
table_.InnerWindow.DecoInnerSizeY1 = table_instance.LastFrozenHeight
end
table_instance.LastFrozenHeight = 0.0
local inner_window = table_.InnerWindow
local bs = g.BoxSelectState
if bs.Window == inner_window and bs.UnclipMode then
ImGui.TableApplyExternalUnclipRect(table_, bs.UnclipRect)
end
-- Initial state
if band(table_.Flags, 1048576 --[[ImGuiTableFlags.NoClip]]) ~= 0 then
table_.DrawSplitter:SetCurrentChannel(inner_window.DrawList, TABLE_DRAW_CHANNEL_NOCLIP)
else
inner_window.DrawList:PushClipRect(inner_window.InnerClipRect.Min, inner_window.InnerClipRect.Max, false) -- FIXME: use table.InnerClipRect?
end
end
-- When starting a BeginMultiSelect() after table has been layout we update IsRequestOutput fields.
function ImGui.TableApplyExternalUnclipRect(table_, rect)
if rect:IsInverted() then
return
end
for column_n = 0, table_.ColumnsCount - 1 do
local column = table_.Columns[column_n]
if not column.IsRequestOutput then
if rect:Overlaps(ImRect(column.MinX, table_.WorkRect.Min.y, column.MaxX, 3.4028234663853e+38 --[[FLT_MAX]])) then
column.IsRequestOutput = true
end
end
end
end
-- Process hit-testing on resizing borders. Actual size change will be applied in EndTable()
-- - Set table.HoveredColumnBorder with a short delay/timer to reduce visual feedback noise.
function ImGui.TableUpdateBorders(table_)
local g = GImGui
assert(band(table_.Flags, 1 --[[ImGuiTableFlags.Resizable]]) ~= 0)
-- At this point OuterRect height may be zero or under actual final height, so we rely on temporal coherency and
-- use the final height from last frame. Because this is only affecting _interaction_ with columns, it is not
-- really problematic (whereas the actual visual will be displayed in EndTable() and using the current frame height).
-- Actual columns highlight/render will be performed in EndTable() and not be affected.
local table_instance = ImGui.TableGetInstanceData(table_, table_.InstanceCurrent)
local hit_half_width = ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale)
local hit_y1 = ((table_.FreezeRowsCount >= 1) and table_.OuterRect.Min.y or table_.WorkRect.Min.y) + table_.AngledHeadersHeight
local hit_y2_body = mathMax(table_.OuterRect.Max.y, hit_y1 + table_instance.LastOuterHeight - table_.AngledHeadersHeight)
local hit_y2_head = hit_y1 + table_instance.LastTopHeadersRowHeight
for order_n = 0, table_.ColumnsCount - 1 do
repeat
if not BitTest(table_.EnabledMaskByDisplayOrder, order_n) then
break
end
local column_n = table_.DisplayOrderToIndex[order_n]
local column = table_.Columns[column_n]
if band(column.Flags, bor(32 --[[ImGuiTableColumnFlags.NoResize]], 1073741824 --[[ImGuiTableColumnFlags.NoDirectResize_]])) ~= 0 then
break
end
-- ImGuiTableFlags.NoBordersInBodyUntilResize will be honored in TableDrawBorders()
local border_y2_hit = (band(table_.Flags, 2048 --[[ImGuiTableFlags.NoBordersInBody]]) ~= 0) and hit_y2_head or hit_y2_body
if band(table_.Flags, 2048 --[[ImGuiTableFlags.NoBordersInBody]]) ~= 0 and table_.IsUsingHeaders == false then
break
end
if not column.IsVisibleX and table_.LastResizedColumn ~= column_n then
break
end
local column_id = ImGui.TableGetColumnResizeID(table_, column_n, table_.InstanceCurrent)
local hit_rect = ImRect(column.MaxX - hit_half_width, hit_y1, column.MaxX + hit_half_width, border_y2_hit)
ImGui.ItemAdd(hit_rect, column_id, nil, 2 --[[ImGuiItemFlags.NoNav]])
--ImGui.GetForegroundDrawList():AddRect(hit_rect.Min, hit_rect.Max, IM_COL32(255, 0, 0, 100))
local pressed, hovered, held = ImGui.ButtonBehavior(hit_rect, column_id, bor(2048 --[[ImGuiButtonFlags.FlattenChildren]], 16 --[[ImGuiButtonFlags.PressedOnClick]], 256 --[[ImGuiButtonFlags.PressedOnDoubleClick]], 262144 --[[ImGuiButtonFlags.NoNavFocus]]))
if pressed and ImGui.IsMouseDoubleClicked(0) then
ImGui.TableSetColumnWidthAutoSingle(table_, column_n)
ImGui.ClearActiveID()
held = false
end
if held then
if table_.LastResizedColumn == -1 then
table_.ResizeLockMinContentsX2 = (table_.RightMostEnabledColumn ~= -1) and table_.Columns[table_.RightMostEnabledColumn].MaxX or -3.4028234663853e+38 --[[FLT_MAX]]
end
table_.ResizedColumn = column_n
table_.InstanceInteracted = table_.InstanceCurrent
end
if (hovered and g.HoveredIdTimer > TABLE_RESIZE_SEPARATOR_FEEDBACK_TIMER) or held then
table_.HoveredColumnBorder = column_n
ImGui.SetMouseCursor(4 --[[ImGuiMouseCursor.ResizeEW]])
end
until true
end
end
function ImGui.EndTable()
local g = GImGui
local table_ = g.CurrentTable
IM_ASSERT_USER_ERROR_RET(table_ ~= nil, "EndTable() call should only be done while in BeginTable() scope!")
-- This assert would be very useful to catch a common error... unfortunately it would probably trigger in some
-- cases, and for consistency user may sometimes output empty tables (and still benefit from e.g. outer border)
--IM_ASSERT(table.IsLayoutLocked, "Table unused: never called TableNextRow(), is that the intent?")
-- If the user never got to call TableNextRow() or TableNextColumn(), we call layout ourselves to ensure all our
-- code paths are consistent (instead of just hoping that TableBegin/TableEnd will work), get borders drawn, etc.
if not table_.IsLayoutLocked then
ImGui.TableUpdateLayout(table_)
end
local flags = table_.Flags
local inner_window = table_.InnerWindow
local outer_window = table_.OuterWindow
local temp_data = table_.TempData
assert(inner_window == g.CurrentWindow and inner_window.ID == temp_data.WindowID)
assert(outer_window == inner_window or outer_window == inner_window.ParentWindow)
if table_.IsInsideRow then
ImGui.TableEndRow(table_)
end
-- Context menu in columns body
if band(flags, 32 --[[ImGuiTableFlags.ContextMenuInBody]]) ~= 0 then
if table_.HoveredColumnBody ~= -1 and not ImGui.IsAnyItemHovered() and ImGui.IsMouseReleased(1 --[[ImGuiMouseButton.Right]]) then
ImGui.TableOpenContextMenu(table_.HoveredColumnBody)
end
end
-- Finalize table height
local table_instance = ImGui.TableGetInstanceData(table_, table_.InstanceCurrent)
ImVec2_Copy(inner_window.DC.PrevLineSize, temp_data.HostBackupPrevLineSize)
ImVec2_Copy(inner_window.DC.CurrLineSize, temp_data.HostBackupCurrLineSize)
ImVec2_Copy(inner_window.DC.CursorMaxPos, temp_data.HostBackupCursorMaxPos)
local inner_content_max_y = ImCeil(table_.RowPosY2) -- Rounding final position is important as we currently don't round row height ('Demo->Tables->Outer Size' demo uses non-integer heights)
assert(table_.RowPosY2 == inner_window.DC.CursorPos.y)
if inner_window ~= outer_window then
inner_window.DC.CursorMaxPos.y = inner_content_max_y
elseif band(flags, 131072 --[[ImGuiTableFlags.NoHostExtendY]]) == 0 then
local v = mathMax(table_.OuterRect.Max.y, inner_content_max_y) -- Patch OuterRect/InnerRect height
table_.OuterRect.Max.y = v; table_.InnerRect.Max.y = v
end
table_.WorkRect.Max.y = mathMax(table_.WorkRect.Max.y, table_.OuterRect.Max.y)
table_instance.LastOuterHeight = table_.OuterRect:GetHeight()
-- Setup inner scrolling range
-- FIXME: This ideally should be done earlier, in BeginTable() SetNextWindowContentSize call, just like writing to inner_window.DC.CursorMaxPos.y,
-- but since the later is likely to be impossible to do we'd rather update both axes together.
if band(table_.Flags, 16777216 --[[ImGuiTableFlags.ScrollX]]) ~= 0 then
local outer_padding_for_border = (band(table_.Flags, 1024 --[[ImGuiTableFlags.BordersOuterV]]) ~= 0) and TABLE_BORDER_SIZE or 0.0
local max_pos_x = inner_window.DC.CursorMaxPos.x
if table_.RightMostEnabledColumn ~= -1 then
max_pos_x = mathMax(max_pos_x, table_.Columns[table_.RightMostEnabledColumn].WorkMaxX + table_.CellPaddingX + table_.OuterPaddingX - outer_padding_for_border)
end
if table_.ResizedColumn ~= -1 then
max_pos_x = mathMax(max_pos_x, table_.ResizeLockMinContentsX2)
end
inner_window.DC.CursorMaxPos.x = max_pos_x + table_.TempData.AngledHeadersExtraWidth
end
-- Pop clipping rect
if band(flags, 1048576 --[[ImGuiTableFlags.NoClip]]) == 0 then
inner_window.DrawList:PopClipRect()
end
ImRect_CopyFromV4(inner_window.ClipRect, inner_window.DrawList._ClipRectStack:back())
-- Draw borders
if band(flags, 1920 --[[ImGuiTableFlags.Borders]]) ~= 0 then
ImGui.TableDrawBorders(table_)
end
-- Flatten channels and merge draw calls
local splitter = table_.DrawSplitter
splitter:SetCurrentChannel(inner_window.DrawList, 0)
if band(table_.Flags, 1048576 --[[ImGuiTableFlags.NoClip]]) == 0 then
ImGui.TableMergeDrawChannels(table_)
end
splitter:Merge(inner_window.DrawList)
-- Update ColumnsAutoFitWidth to get us ahead for host using our size to auto-resize without waiting for next BeginTable()
local auto_fit_width_for_fixed = 0.0
local auto_fit_width_for_stretched = 0.0
local auto_fit_width_for_stretched_min = 0.0
for column_n = 0, table_.ColumnsCount - 1 do
if BitTest(table_.EnabledMaskByIndex, column_n) then
local column = table_.Columns[column_n]
local column_width_request
if band(column.Flags, 16 --[[ImGuiTableColumnFlags.WidthFixed]]) ~= 0 and band(column.Flags, 32 --[[ImGuiTableColumnFlags.NoResize]]) == 0 then
column_width_request = column.WidthRequest
else
column_width_request = ImGui.TableGetColumnWidthAuto(table_, column)
end
if band(column.Flags, 16 --[[ImGuiTableColumnFlags.WidthFixed]]) ~= 0 then
auto_fit_width_for_fixed = auto_fit_width_for_fixed + column_width_request
else
auto_fit_width_for_stretched = auto_fit_width_for_stretched + column_width_request
end
if band(column.Flags, 8 --[[ImGuiTableColumnFlags.WidthStretch]]) ~= 0 and band(column.Flags, 32 --[[ImGuiTableColumnFlags.NoResize]]) ~= 0 then
auto_fit_width_for_stretched_min = mathMax(auto_fit_width_for_stretched_min, column_width_request / (column.StretchWeight / table_.ColumnsStretchSumWeights))
end
end
end
local width_spacings = (table_.OuterPaddingX * 2.0) + (table_.CellSpacingX1 + table_.CellSpacingX2) * (table_.ColumnsEnabledCount - 1)
table_.ColumnsAutoFitWidth = width_spacings + (table_.CellPaddingX * 2.0) * table_.ColumnsEnabledCount + auto_fit_width_for_fixed + mathMax(auto_fit_width_for_stretched, auto_fit_width_for_stretched_min)
-- Update scroll
if band(table_.Flags, 16777216 --[[ImGuiTableFlags.ScrollX]]) == 0 and inner_window ~= outer_window then
inner_window.Scroll.x = 0.0
elseif table_.LastResizedColumn ~= -1 and table_.ResizedColumn == -1 and inner_window.ScrollbarX and table_.InstanceInteracted == table_.InstanceCurrent then
-- When releasing a column being resized, scroll to keep the resulting column in sight
local neighbor_width_to_keep_visible = table_.MinColumnWidth + table_.CellPaddingX * 2.0
local column = table_.Columns[table_.LastResizedColumn]
if column.MaxX < table_.InnerClipRect.Min.x then
ImGui.SetScrollFromPosX(inner_window, column.MaxX - inner_window.Pos.x - neighbor_width_to_keep_visible, 1.0)
elseif column.MaxX > table_.InnerClipRect.Max.x then
ImGui.SetScrollFromPosX(inner_window, column.MaxX - inner_window.Pos.x + neighbor_width_to_keep_visible, 1.0)
end
end
-- Apply resizing/dragging at the end of the frame
if table_.ResizedColumn ~= -1 and table_.InstanceCurrent == table_.InstanceInteracted then
local column = table_.Columns[table_.ResizedColumn]
local new_x2 = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale))
local new_width = ImTrunc(new_x2 - column.MinX - table_.CellSpacingX1 - table_.CellPaddingX * 2.0)
table_.ResizedColumnNextWidth = new_width
end
table_.IsActiveIdInTable = (g.ActiveIdIsAlive ~= 0 and table_.IsActiveIdAliveBeforeTable == false)
-- Pop from id stack
IM_ASSERT_USER_ERROR(inner_window.IDStack:back() == table_instance.TableInstanceID, "Mismatching PushID/PopID!")
IM_ASSERT_USER_ERROR(outer_window.DC.ItemWidthStack.Size >= temp_data.HostBackupItemWidthStackSize, "Too many PopItemWidth!")
if table_.InstanceCurrent > 0 then
ImGui.PopID()
end
ImGui.PopID()
-- Restore window data that we modified
local backup_outer_max_pos = V2(outer_window.DC.CursorMaxPos)
ImRect_Copy(inner_window.WorkRect, temp_data.HostBackupWorkRect)
ImRect_Copy(inner_window.ParentWorkRect, temp_data.HostBackupParentWorkRect)
inner_window.SkipItems = table_.HostSkipItems
ImVec2_Copy(outer_window.DC.CursorPos, table_.OuterRect.Min)
outer_window.DC.ItemWidth = temp_data.HostBackupItemWidth
outer_window.DC.ItemWidthStack:resize(temp_data.HostBackupItemWidthStackSize)
outer_window.DC.ColumnsOffset.x = temp_data.HostBackupColumnsOffset
-- Layout in outer window
-- (FIXME: To allow auto-fit and allow desirable effect of SameLine() we dissociate 'used' vs 'ideal' size by overriding
-- CursorPosPrevLine and CursorMaxPos manually. That should be a more general layout feature, see same problem e.g. #3414)
if inner_window ~= outer_window then
local backup_nav_layers_active_mask = inner_window.DC.NavLayersActiveMask
inner_window.DC.NavLayersActiveMask = bor(inner_window.DC.NavLayersActiveMask, lshift(1, table_.NavLayer)) -- So empty table don't appear to navigate differently.
g.CurrentTable = nil -- To avoid error recovery recursing
ImGui.EndChild()
g.CurrentTable = table_
inner_window.DC.NavLayersActiveMask = backup_nav_layers_active_mask
else
inner_window.DC.TreeDepth = inner_window.DC.TreeDepth - 1
ImGui.ItemSize(table_.OuterRect:GetSize())
ImGui.ItemAdd(table_.OuterRect, 0)
end
-- Override declared contents width/height to enable auto-resize while not needlessly adding a scrollbar
if band(table_.Flags, 65536 --[[ImGuiTableFlags.NoHostExtendX]]) ~= 0 then
-- FIXME-TABLE: Could we remove this section?
-- ColumnsAutoFitWidth may be one frame ahead here since for Fixed+NoResize is calculated from latest contents
assert(band(table_.Flags, 16777216 --[[ImGuiTableFlags.ScrollX]]) == 0)
outer_window.DC.CursorMaxPos.x = mathMax(backup_outer_max_pos.x, table_.OuterRect.Min.x + table_.ColumnsAutoFitWidth)
elseif temp_data.UserOuterSize.x <= 0.0 then
-- Some references for this: #7651 + tests "table_reported_size", "table_reported_size_outer" equivalent Y block, #9352
-- - FIXME-TABLE: Would make sense to pre-compute expected scrollbar visibility/sizes to generally save a frame of feedback? See broken test in 'table_reported_size_outer'
local outer_content_max_x = table_.OuterRect.Min.x + table_.ColumnsAutoFitWidth
local decoration_size = table_.TempData.AngledHeadersExtraWidth + ((inner_window ~= outer_window) and inner_window.ScrollbarSizes.x or 0.0)
outer_window.DC.IdealMaxPos.x = mathMax(outer_window.DC.IdealMaxPos.x, outer_content_max_x + decoration_size - temp_data.UserOuterSize.x)
outer_window.DC.CursorMaxPos.x = mathMax(backup_outer_max_pos.x, mathMin(table_.OuterRect.Max.x, outer_content_max_x + decoration_size))
else
outer_window.DC.CursorMaxPos.x = mathMax(backup_outer_max_pos.x, table_.OuterRect.Max.x)
end
if temp_data.UserOuterSize.y <= 0.0 then
-- (same comment as above)
local outer_content_size_y = (inner_window == outer_window) and (inner_content_max_y - table_.InnerRect.Min.y) or (inner_content_max_y - inner_window.DC.CursorStartPos.y)
local outer_content_max_y = table_.OuterRect.Min.y + outer_content_size_y
local decoration_size = (inner_window ~= outer_window) and inner_window.ScrollbarSizes.y or 0.0
outer_window.DC.IdealMaxPos.y = mathMax(outer_window.DC.IdealMaxPos.y, outer_content_max_y + decoration_size - temp_data.UserOuterSize.y)
outer_window.DC.CursorMaxPos.y = mathMax(backup_outer_max_pos.y, mathMin(table_.OuterRect.Max.y, outer_content_max_y + decoration_size))
else
-- OuterRect.Max.y may already have been pushed downward from the initial value (unless ImGuiTableFlags.NoHostExtendY is set)
outer_window.DC.CursorMaxPos.y = mathMax(backup_outer_max_pos.y, table_.OuterRect.Max.y)
end
-- Save settings
if table_.IsSettingsDirty then
ImGui.TableSaveSettings(table_)
end
table_.IsInitializing = false
-- Clear or restore current table, if any
assert(g.CurrentWindow == outer_window and g.CurrentTable == table_)
assert(g.TablesTempDataStacked > 0)
g.TablesTempDataStacked = g.TablesTempDataStacked - 1
temp_data = (g.TablesTempDataStacked > 0) and g.TablesTempData.Data[g.TablesTempDataStacked] or nil
g.CurrentTable = (temp_data and (temp_data.WindowID == outer_window.ID)) and g.Tables:GetByIndex(temp_data.TableIndex) or nil
if g.CurrentTable then
g.CurrentTable.TempData = temp_data
g.CurrentTable.DrawSplitter = temp_data.DrawSplitter
end
outer_window.DC.CurrentTableIdx = g.CurrentTable and g.Tables:GetIndex(g.CurrentTable) or -1
ImGui.NavUpdateCurrentWindowIsScrollPushableX()
end
-- Called in TableUpdateLayout() when initializing/after loading settings.
-- 'init_mask' specify which fields to initialize.
--- @param column_n int # index of `column` (C++: Columns.index_from_ptr(column))
function ImGui.TableInitColumnDefaults(table_, column, column_n, init_mask)
local flags = column.Flags
if band(init_mask, 1 --[[ImGuiTableFlags.Resizable]]) ~= 0 then
local init_width_or_weight = column.InitStretchWeightOrWidth
column.WidthRequest = (band(flags, 16 --[[ImGuiTableColumnFlags.WidthFixed]]) ~= 0 and init_width_or_weight > 0.0) and init_width_or_weight or -1.0
column.StretchWeight = (init_width_or_weight > 0.0 and band(flags, 8 --[[ImGuiTableColumnFlags.WidthStretch]]) ~= 0) and init_width_or_weight or -1.0
if init_width_or_weight > 0.0 then -- Disable auto-fit if an explicit width/weight has been specified
column.AutoFitQueue = 0x00
end
end
if band(init_mask, 2 --[[ImGuiTableFlags.Reorderable]]) ~= 0 then
column.DisplayOrder = (band(table_.Flags, 2 --[[ImGuiTableFlags.Reorderable]]) ~= 0) and -1 or column_n
end
if band(init_mask, 4 --[[ImGuiTableFlags.Hideable]]) ~= 0 then
local v = band(flags, 2 --[[ImGuiTableColumnFlags.DefaultHide]]) == 0
column.IsUserEnabled = v; column.IsUserEnabledNextFrame = v
end
if band(init_mask, 8 --[[ImGuiTableFlags.Sortable]]) ~= 0 then
-- Multiple columns using _DefaultSort will be reassigned unique SortOrder values when building the sort specs.
column.SortOrder = (band(flags, 4 --[[ImGuiTableColumnFlags.DefaultSort]]) ~= 0) and 0 or -1
if band(flags, 4 --[[ImGuiTableColumnFlags.DefaultSort]]) ~= 0 then
column.SortDirection = (band(flags, 32768 --[[ImGuiTableColumnFlags.PreferSortDescending]]) ~= 0) and 2 --[[ImGuiSortDirection.Descending]] or 1 --[[ImGuiSortDirection.Ascending]]
else
column.SortDirection = 0 --[[ImGuiSortDirection.None]]
end
end
end
-- See "COLUMNS SIZING POLICIES" comments at the top of this file
-- If (init_width_or_weight <= 0.0) it is ignored
local function TableSetupColumnApply(table_, idx, id, name_offset, flags, init_width_or_weight, user_data)
local column = table_.Columns[idx]
-- Assert when passing a width or weight if policy is entirely left to default, to avoid storing width into weight and vice-versa.
-- Give a grace to users of ImGuiTableFlags.ScrollX.
if table_.IsDefaultSizingPolicy and band(flags, 24 --[[ImGuiTableColumnFlags.WidthMask_]]) == 0 and band(flags, 16777216 --[[ImGuiTableFlags.ScrollX]]) == 0 then
IM_ASSERT_USER_ERROR_RET(init_width_or_weight <= 0.0, "TableSetupColumn(): can only specify width/weight if sizing policy is set explicitly in either Table or Column.")
end
-- When passing a width automatically enforce WidthFixed policy
-- (whereas TableSetupColumnFlags would default to WidthAuto if table is not resizable)
if band(flags, 24 --[[ImGuiTableColumnFlags.WidthMask_]]) == 0 and init_width_or_weight > 0.0 then
if band(table_.Flags, 57344 --[[ImGuiTableFlags.SizingMask_]]) == 8192 --[[ImGuiTableFlags.SizingFixedFit]] or band(table_.Flags, 57344 --[[ImGuiTableFlags.SizingMask_]]) == 16384 --[[ImGuiTableFlags.SizingFixedSame]] then
flags = bor(flags, 16 --[[ImGuiTableColumnFlags.WidthFixed]])
end
end
if band(flags, 262144 --[[ImGuiTableColumnFlags.AngledHeader]]) ~= 0 then
flags = bor(flags, 4096 --[[ImGuiTableColumnFlags.NoHeaderLabel]])
table_.AngledHeadersCount = table_.AngledHeadersCount + 1
end
TableSetupColumnFlags(table_, column, idx, flags)
column.ID = id
column.UserData = user_data
column.NameOffset = name_offset
column.InitStretchWeightOrWidth = init_width_or_weight
end
--- @param label string?
--- @param flags? ImGuiTableColumnFlags
--- @param init_width_or_weight? float
--- @param user_data? ImGuiID
function ImGui.TableSetupColumn(label, flags, init_width_or_weight, user_data)
if flags == nil then flags = 0 end
if init_width_or_weight == nil then init_width_or_weight = 0.0 end
if user_data == nil then user_data = 0 end
local g = GImGui
local table_ = g.CurrentTable
IM_ASSERT_USER_ERROR_RET(table_ ~= nil, "Call should only be done while in BeginTable() scope!")
IM_ASSERT_USER_ERROR_RET(table_.DeclColumnsCount < table_.ColumnsCount, "TableSetupColumn(): called too many times!")
IM_ASSERT_USER_ERROR_RET(table_.IsLayoutLocked == false, "TableSetupColumn(): need to call before first row!") -- Table layout is locked when submitting a row or when calling BeginMultiSelect() with box-select.
assert(band(flags, 251658240 --[[ImGuiTableColumnFlags.StatusMask_]]) == 0, "Illegal to pass StatusMask values to TableSetupColumn()")
-- Store name
-- FIXME: If we recorded the number of \n in names we could compute header row height
local name_offset = -1
if label ~= nil and label ~= "" then
name_offset = #table_.ColumnsNames
table_.ColumnsNames[name_offset + 1] = label
end
local column_id = (label ~= nil and label ~= "") and ImHashStr(label) or 0
-- When ID changed or a column moved: defer the request until layout where we will process full reconcile.
local column_idx = table_.DeclColumnsCount
table_.DeclColumnsCount = table_.DeclColumnsCount + 1
local column = table_.Columns[column_idx]
-- If topology change goes into reconcile mode
if table_.IsReconcileMode == false and column.ID ~= column_id and not table_.IsNewTable then
table_.IsReconcileMode = true
end
-- Fast/common path
if table_.IsReconcileMode == false then
TableSetupColumnApply(table_, column_idx, column_id, name_offset, flags, init_width_or_weight, user_data)
column.IsNeedReconcileSrc = false; column.IsNeedReconcileDst = false
local old = table_.TempData.OldColumnsData
if old and column_idx < table_.TempData.OldColumnsDataSize then
old[column_idx].IsNeedReconcileSrc = false
end
return
end
-- Reconcile path: defer applying data to TableUpdateLayout() -> TableReconcileMovedColumns() -> TableSetupColumnApply().
-- On column count change: search the previous columns:
-- - the live Columns[] array is truncated on shrink, so a moved column may only exist in old data.
-- - the live Columns[] array may already be partially reset.
local reconcile_data = {
ID = column_id, NameOffset = name_offset, Flags = flags, InitWidthOrWeight = init_width_or_weight, UserData = user_data,
ColumnNewIdx = column_idx, ColumnOldIdx = -1, ColumnOldData = ImGuiTableColumn(),
}
table_.TempData.ReconcileColumnsRequests:push_back(reconcile_data)
column.NameOffset = name_offset -- Allow TableGetColumnName() to work before layout
column.IsNeedReconcileSrc = true; column.IsNeedReconcileDst = true
end
-- NB: This was written to be similar to the logic in TableLoadSettingsForColumns().
function ImGui.TableReconcileColumns(table_)
local temp_data = table_.TempData
IMGUI_DEBUG_LOG_TABLE("[table] Reconcile columns for table 0x%08X\n", table_.ID)
local dst_columns = table_.Columns
local src_columns, src_count
if temp_data.OldColumnsData == nil then
src_columns, src_count = table_.Columns, table_.ColumnsSize
else
src_columns, src_count = temp_data.OldColumnsData, temp_data.OldColumnsDataSize
end
-- Find matches for named columns.
local matches = 0
local reconcile_requests = temp_data.ReconcileColumnsRequests
for _, reconcile_data in reconcile_requests:iter() do
if reconcile_data.ID ~= 0 then
for src_n = 0, src_count - 1 do
local src_column = src_columns[src_n]
if src_column.ID == reconcile_data.ID and src_column.IsNeedReconcileSrc then
local dst_column = dst_columns[reconcile_data.ColumnNewIdx]
assert(src_column.IsNeedReconcileSrc and dst_column.IsNeedReconcileDst)
src_column.IsNeedReconcileSrc = false; dst_column.IsNeedReconcileDst = false
reconcile_data.ColumnOldIdx = src_n
reconcile_data.ColumnOldData = TableColumnClone(src_column)
matches = matches + 1
break
end
end
end
end
-- Remaining entries are matched sequentially.
local dst_idx = 1 -- index in reconcile array (1-based)
if matches ~= reconcile_requests.Size then
for src_n = 0, src_count - 1 do
local src_column = src_columns[src_n]
if src_column.IsNeedReconcileSrc then
while dst_idx <= reconcile_requests.Size and reconcile_requests.Data[dst_idx].ColumnOldIdx ~= -1 do
dst_idx = dst_idx + 1
end
if dst_idx > reconcile_requests.Size then
break
end
local reconcile_data = reconcile_requests.Data[dst_idx]
local dst_column = dst_columns[reconcile_data.ColumnNewIdx]
assert(src_column.IsNeedReconcileSrc and dst_column.IsNeedReconcileDst)
src_column.IsNeedReconcileSrc = false; dst_column.IsNeedReconcileDst = false
reconcile_data.ColumnOldIdx = src_n
reconcile_data.ColumnOldData = TableColumnClone(src_column)
end
end
end
-- Apply in the final pass. Because it is possible that src_columns == table.Columns we went through a temporary copy.
for _, reconcile_data in reconcile_requests:iter() do
TableColumnCopy(table_.Columns[reconcile_data.ColumnNewIdx], reconcile_data.ColumnOldData) -- When old column was not found clear anyway with default-constructed data (which will set IsJustCreated=true)
TableSetupColumnApply(table_, reconcile_data.ColumnNewIdx, reconcile_data.ID, reconcile_data.NameOffset, reconcile_data.Flags, reconcile_data.InitWidthOrWeight, reconcile_data.UserData)
end
ImGui.TableFixDisplayOrder(table_)
table_.IsSettingsDirty = true -- FIXME-RECONCILE: Necessary?
table_.IsReconcileMode = false
reconcile_requests:resize(0) -- GC-ed once in NewFrame()
end
-- [Public]
function ImGui.TableSetupScrollFreeze(columns, rows)
local g = GImGui
local table_ = g.CurrentTable
IM_ASSERT_USER_ERROR_RET(table_ ~= nil, "Call should only be done while in BeginTable() scope!")
assert(table_.IsLayoutLocked == false, "TableSetupColumn(): need to call before first row!")
assert(columns >= 0 and columns < 512 --[[IMGUI_TABLE_MAX_COLUMNS]])
assert(rows >= 0 and rows < 128) -- Arbitrary limit
table_.FreezeColumnsRequest = (band(table_.Flags, 16777216 --[[ImGuiTableFlags.ScrollX]]) ~= 0) and mathMin(columns, table_.ColumnsCount) or 0
table_.FreezeColumnsCount = (table_.InnerWindow.Scroll.x ~= 0.0) and table_.FreezeColumnsRequest or 0
table_.FreezeRowsRequest = (band(table_.Flags, 33554432 --[[ImGuiTableFlags.ScrollY]]) ~= 0) and rows or 0
table_.FreezeRowsCount = (table_.InnerWindow.Scroll.y ~= 0.0) and table_.FreezeRowsRequest or 0
table_.IsUnfrozenRows = (table_.FreezeRowsCount == 0) -- Make sure this is set before TableUpdateLayout() so ImGuiListClipper can benefit from it.
end
-------------------------------------------------------------------------------
-- [SECTION] Tables: Simple accessors
-------------------------------------------------------------------------------
-- - TableGetColumnCount()
-- - TableGetColumnName()
-- - TableGetColumnName() [Internal]
-- - TableSetColumnEnabled()
-- - TableGetColumnFlags()
-- - TableGetCellBgRect() [Internal]
-- - TableGetColumnResizeID() [Internal]
-- - TableGetHoveredColumn() [Internal]
-- - TableGetHoveredRow() [Internal]
-- - TableSetBgColor()
-------------------------------------------------------------------------------
-- [Internal] Small optimization to avoid calls to PopClipRect/SetCurrentChannel/PushClipRect in sequences,
-- they would meddle many times with the underlying ImDrawCmd.
-- Instead, we do a preemptive overwrite of clipping rectangle _without_ altering the command-buffer and let
-- the subsequent single call to SetCurrentChannel() does it things once.
@native
function ImGui.SetWindowClipRectBeforeSetChannel(window, clip_rect)
-- [Luau] scalar copies, no temporary ImVec4: the clip rect stack top is a uniquely owned ImVec4 (PushClipRect always pushes a fresh one), so overwrite it in place
local min, max = clip_rect.Min, clip_rect.Max
local x1, y1, x2, y2 = min.x, min.y, max.x, max.y
local wcr = window.ClipRect
wcr.Min.x = x1; wcr.Min.y = y1; wcr.Max.x = x2; wcr.Max.y = y2
local draw_list = window.DrawList
local hcr = draw_list._CmdHeader.ClipRect
hcr.x = x1; hcr.y = y1; hcr.z = x2; hcr.w = y2
local stack = draw_list._ClipRectStack
local top = stack.Data[stack.Size]
if top ~= nil then
top.x = x1; top.y = y1; top.z = x2; top.w = y2
else
stack.Data[stack.Size] = ImVec4(x1, y1, x2, y2)
end
end
function ImGui.TableGetColumnCount()
local g = GImGui
local table_ = g.CurrentTable
return table_ and table_.ColumnsCount or 0
end
-- Overloads: TableGetColumnName(column_n?) / TableGetColumnName(table, column_n)
function ImGui.TableGetColumnName(a, b)
local table_, column_n
if type(a) == "table" then
table_, column_n = a, b
else
local g = GImGui
table_ = g.CurrentTable
if not table_ then
return nil
end
column_n = a or -1
if column_n < 0 then
column_n = table_.CurrentColumn
end
end
if table_.IsLayoutLocked == false and column_n >= table_.DeclColumnsCount then
return "" -- NameOffset is invalid at this point
end
local column = table_.Columns[column_n]
if column.NameOffset == -1 then
return ""
end
return table_.ColumnsNames[column.NameOffset + 1]
end
-- Change user accessible enabled/disabled state of a column (often perceived as "showing/hiding" from users point of view)
-- Note that end-user can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags.ContextMenuInBody)
-- - Require table to have the ImGuiTableFlags.Hideable flag because we are manipulating user accessible state.
-- - Request will be applied during next layout, which happens on the first call to TableNextRow() after BeginTable().
-- - For the getter you can test (TableGetColumnFlags() & ImGuiTableColumnFlags.IsEnabled) ~= 0.
-- - Alternative: the ImGuiTableColumnFlags.Disabled is an overriding/master disable flag which will also hide the column from context menu.
function ImGui.TableSetColumnEnabled(column_n, enabled)
local g = GImGui
local table_ = g.CurrentTable
IM_ASSERT_USER_ERROR_RET(table_ ~= nil, "Call should only be done while in BeginTable() scope!")
assert(band(table_.Flags, 4 --[[ImGuiTableFlags.Hideable]]) ~= 0) -- See comments above
if column_n < 0 then
column_n = table_.CurrentColumn
end
assert(column_n >= 0 and column_n < table_.ColumnsCount)
local column = table_.Columns[column_n]
column.IsUserEnabledNextFrame = enabled
end
-- We allow querying for an extra column in order to poll the IsHovered state of the right-most section
function ImGui.TableGetColumnFlags(column_n)
if column_n == nil then column_n = -1 end
local g = GImGui
local table_ = g.CurrentTable
if not table_ then
return 0 --[[ImGuiTableColumnFlags.None]]
end
if column_n < 0 then
column_n = table_.CurrentColumn
end
if column_n == table_.ColumnsCount then
return (table_.HoveredColumnBody == column_n) and 134217728 --[[ImGuiTableColumnFlags.IsHovered]] or 0 --[[ImGuiTableColumnFlags.None]]
end
return table_.Columns[column_n].Flags
end
-- Return the cell rectangle based on currently known height.
-- - Important: we generally don't know our row height until the end of the row, so Max.y will be incorrect in many situations.
-- The only case where this is correct is if we provided a min_row_height to TableNextRow() and don't go below it, or in TableEndRow() when we locked that height.
-- - Important: if ImGuiTableFlags.PadOuterX is set but ImGuiTableFlags.PadInnerX is not set, the outer-most left and right
-- columns report a small offset so their CellBgRect can extend up to the outer border.
-- FIXME: But the rendering code in TableEndRow() nullifies that with clamping required for scrolling.
function ImGui.TableGetCellBgRect(table_, column_n)
local column = table_.Columns[column_n]
local x1 = column.MinX
local x2 = column.MaxX
--if column.PrevEnabledColumn == -1 then
-- x1 = x1 - table.OuterPaddingX
--if column.NextEnabledColumn == -1 then
-- x2 = x2 + table.OuterPaddingX
x1 = mathMax(x1, table_.WorkRect.Min.x)
x2 = mathMin(x2, table_.WorkRect.Max.x)
return ImRect(x1, table_.RowPosY1, x2, table_.RowPosY2)
end
-- Return the resizing ID for the right-side of the given column.
function ImGui.TableGetColumnResizeID(table_, column_n, instance_no)
if instance_no == nil then instance_no = 0 end
assert(column_n >= 0 and column_n < table_.ColumnsCount)
local instance_id = ImGui.TableGetInstanceID(table_, instance_no)
return band(instance_id + 1 + column_n, 0xFFFFFFFF) -- FIXME: #6140: still not ideal
end
-- Return -1 when table is not hovered. return columns_count if hovering the unused space at the right of the right-most visible column.
function ImGui.TableGetHoveredColumn()
local g = GImGui
local table_ = g.CurrentTable
if not table_ then
return -1
end
return table_.HoveredColumnBody
end
-- Return -1 when table is not hovered. Return maxrow+1 if in table but below last submitted row.
-- *IMPORTANT* Unlike TableGetHoveredColumn(), this has a one frame latency in updating the value.
-- This difference with is the reason why this is not public yet.
function ImGui.TableGetHoveredRow()
local g = GImGui
local table_ = g.CurrentTable
if not table_ then
return -1
end
local table_instance = ImGui.TableGetInstanceData(table_, table_.InstanceCurrent)
return table_instance.HoveredRowLast
end
function ImGui.TableSetBgColor(target, color, column_n)
if column_n == nil then column_n = -1 end
local g = GImGui
local table_ = g.CurrentTable
IM_ASSERT_USER_ERROR_RET(table_ ~= nil, "Call should only be done while in BeginTable() scope!")
assert(target ~= 0 --[[ImGuiTableBgTarget.None]])
if color == 16777216 --[[IM_COL32_DISABLE]] then
color = 0
end
-- We cannot draw neither the cell or row background immediately as we don't know the row height at this point in time.
if target == 3 --[[ImGuiTableBgTarget.CellBg]] then
if table_.RowPosY1 > table_.InnerClipRect.Max.y then -- Discard
return
end
if column_n == -1 then
column_n = table_.CurrentColumn
end
if not BitTest(table_.VisibleMaskByIndex, column_n) then
return
end
if table_.RowCellDataCurrent < 0 or table_.RowCellData[table_.RowCellDataCurrent].Column ~= column_n then
table_.RowCellDataCurrent = table_.RowCellDataCurrent + 1
end
local cell_data = table_.RowCellData[table_.RowCellDataCurrent]
cell_data.BgColor = color
cell_data.Column = column_n
elseif target == 1 --[[ImGuiTableBgTarget.RowBg0]] or target == 2 --[[ImGuiTableBgTarget.RowBg1]] then
if table_.RowPosY1 > table_.InnerClipRect.Max.y then -- Discard
return
end
assert(column_n == -1)
local bg_idx = (target == 2 --[[ImGuiTableBgTarget.RowBg1]]) and 1 or 0
table_.RowBgColor[bg_idx] = color
else
assert(false)
end
end
-------------------------------------------------------------------------------
-- [SECTION] Tables: Row changes
-------------------------------------------------------------------------------
-- - TableGetRowIndex()
-- - TableNextRow()
-- - TableBeginRow() [Internal]
-- - TableEndRow() [Internal]
-------------------------------------------------------------------------------
-- [Public] Note: for row coloring we use .RowBgColorCounter which is the same value without counting header rows
function ImGui.TableGetRowIndex()
local g = GImGui
local table_ = g.CurrentTable
if not table_ then
return 0
end
return table_.CurrentRow
end
-- [Public] Starts into the first cell of a new row
--- @param row_flags? ImGuiTableRowFlags
--- @param row_min_height? float
function ImGui.TableNextRow(row_flags, row_min_height)
if row_flags == nil then row_flags = 0 end
if row_min_height == nil then row_min_height = 0.0 end
local g = GImGui
local table_ = g.CurrentTable
if not table_.IsLayoutLocked then
ImGui.TableUpdateLayout(table_)
end
if table_.IsInsideRow then
ImGui.TableEndRow(table_)
end
table_.LastRowFlags = table_.RowFlags
table_.RowFlags = row_flags
table_.RowCellPaddingY = g.Style.CellPadding.y
table_.RowMinHeight = row_min_height
ImGui.TableBeginRow(table_)
-- We honor min_row_height requested by user, but cannot guarantee per-row maximum height,
-- because that would essentially require a unique clipping rectangle per-cell.
table_.RowPosY2 = mathMax(table_.RowPosY2 + table_.RowCellPaddingY * 2.0, table_.RowPosY1 + row_min_height) -- [Luau] inline ImMax
-- Disable output until user calls TableNextColumn()
table_.InnerWindow.SkipItems = true
end
-- [Internal] Only called by TableNextRow()
function ImGui.TableBeginRow(table_)
local window = table_.InnerWindow
assert(not table_.IsInsideRow)
-- New row
table_.CurrentRow = table_.CurrentRow + 1
table_.CurrentColumn = -1
table_.RowBgColor[0] = 16777216 --[[IM_COL32_DISABLE]]; table_.RowBgColor[1] = 16777216 --[[IM_COL32_DISABLE]]
table_.RowCellDataCurrent = -1
table_.IsInsideRow = true
-- Begin frozen rows
local next_y1 = table_.RowPosY2
if table_.CurrentRow == 0 and table_.FreezeRowsCount > 0 then
next_y1 = table_.OuterRect.Min.y
window.DC.CursorPos.y = next_y1
end
table_.RowPosY1 = next_y1; table_.RowPosY2 = next_y1
table_.RowTextBaseline = 0.0
local dc = window.DC -- [Luau] cache
table_.RowIndentOffsetX = dc.Indent.x - table_.HostIndentX -- Lock indent
dc.PrevLineTextBaseOffset = 0.0
dc.CursorPosPrevLine.x = dc.CursorPos.x
dc.CursorPosPrevLine.y = dc.CursorPos.y + table_.RowCellPaddingY -- This allows users to call SameLine() to share LineSize between columns.
dc.PrevLineSize.x = 0.0; dc.PrevLineSize.y = 0.0 -- This allows users to call SameLine() to share LineSize between columns, and to call it from first column too.
dc.CurrLineSize.x = 0.0; dc.CurrLineSize.y = 0.0
dc.IsSameLine = false; dc.IsSetPos = false
dc.CursorMaxPos.y = next_y1
-- Making the header BG color non-transparent will allow us to overlay it multiple times when handling smooth dragging.
if band(table_.RowFlags, 1 --[[ImGuiTableRowFlags.Headers]]) ~= 0 then
ImGui.TableSetBgColor(1 --[[ImGuiTableBgTarget.RowBg0]], ImGui.GetColorU32(48 --[[ImGuiCol.TableHeaderBg]]))
if table_.CurrentRow == 0 then
table_.IsUsingHeaders = true
end
end
end
-- [Luau] scratch vectors for TableEndRow() row background (never escape)
local TableEndRow_RowMin, TableEndRow_RowMax = ImVec2(), ImVec2()
-- [Internal] Called by TableNextRow()
function ImGui.TableEndRow(table_)
local g = GImGui
local window = g.CurrentWindow
assert(window == table_.InnerWindow)
assert(table_.IsInsideRow)
if table_.CurrentColumn ~= -1 then
ImGui.TableEndCell(table_)
table_.CurrentColumn = -1
end
-- Logging
if g.LogEnabled then
ImGui.LogRenderedText(nil, "|")
end
-- Position cursor at the bottom of our row so it can be used for e.g. clipping calculation. However it is
-- likely that the next call to TableBeginCell() will reposition the cursor to take account of vertical padding.
window.DC.CursorPos.y = table_.RowPosY2
-- Row background fill
local bg_y1 = table_.RowPosY1
local bg_y2 = table_.RowPosY2
local unfreeze_rows_actual = (table_.CurrentRow + 1 == table_.FreezeRowsCount)
local unfreeze_rows_request = (table_.CurrentRow + 1 == table_.FreezeRowsRequest)
local table_instance = (table_.InstanceCurrent == 0) and table_.InstanceDataFirst or ImGui.TableGetInstanceData(table_, table_.InstanceCurrent) -- [Luau] inline common case
if band(table_.RowFlags, 1 --[[ImGuiTableRowFlags.Headers]]) ~= 0 and (table_.CurrentRow == 0 or band(table_.LastRowFlags, 1 --[[ImGuiTableRowFlags.Headers]]) ~= 0) then
table_instance.LastTopHeadersRowHeight = table_instance.LastTopHeadersRowHeight + bg_y2 - bg_y1
end
local is_visible = (bg_y2 >= table_.InnerClipRect.Min.y and bg_y1 <= table_.InnerClipRect.Max.y)
if is_visible then
-- Update data for TableGetHoveredRow()
if table_.HoveredColumnBody ~= -1 and g.IO.MousePos.y >= bg_y1 and g.IO.MousePos.y < bg_y2 and table_instance.HoveredRowNext < 0 then
table_instance.HoveredRowNext = table_.CurrentRow
end
-- Decide of background color for the row
local bg_col0 = 0
local bg_col1 = 0
if table_.RowBgColor[0] ~= 16777216 --[[IM_COL32_DISABLE]] then
bg_col0 = table_.RowBgColor[0]
elseif band(table_.Flags, 64 --[[ImGuiTableFlags.RowBg]]) ~= 0 then
bg_col0 = ImGui.GetColorU32((band(table_.RowBgColorCounter, 1) ~= 0) and 52 --[[ImGuiCol.TableRowBgAlt]] or 51 --[[ImGuiCol.TableRowBg]])
end
if table_.RowBgColor[1] ~= 16777216 --[[IM_COL32_DISABLE]] then
bg_col1 = table_.RowBgColor[1]
end
-- Decide of top border color
local top_border_col = 0
local border_size = TABLE_BORDER_SIZE
if table_.CurrentRow > 0 and band(table_.Flags, 128 --[[ImGuiTableFlags.BordersInnerH]]) ~= 0 then
top_border_col = (band(table_.LastRowFlags, 1 --[[ImGuiTableRowFlags.Headers]]) ~= 0) and table_.BorderColorStrong or table_.BorderColorLight
end
local draw_cell_bg_color = table_.RowCellDataCurrent >= 0
local draw_strong_bottom_border = unfreeze_rows_actual
if bor(bg_col0, bg_col1, top_border_col) ~= 0 or draw_strong_bottom_border or draw_cell_bg_color then
-- In theory we could call SetWindowClipRectBeforeSetChannel() but since we know TableEndRow() is
-- always followed by a change of clipping rectangle we perform the smallest overwrite possible here.
if band(table_.Flags, 1048576 --[[ImGuiTableFlags.NoClip]]) == 0 then
-- [Luau] scalar copy, no temporary ImVec4
local src, dst = table_.Bg0ClipRectForDrawCmd, window.DrawList._CmdHeader.ClipRect
dst.x = src.Min.x; dst.y = src.Min.y; dst.z = src.Max.x; dst.w = src.Max.y
end
table_.DrawSplitter:SetCurrentChannel(window.DrawList, TABLE_DRAW_CHANNEL_BG0)
end
-- Draw row background
-- We soft/cpu clip this so all backgrounds and borders can share the same clipping rectangle
if bg_col0 ~= 0 or bg_col1 ~= 0 then
-- [Luau] row_rect = ImRect(WorkRect.Min.x, bg_y1, WorkRect.Max.x, bg_y2); row_rect:ClipWith(BgClipRect), in scratch vectors (only read by AddRectFilled)
local bg_clip_rect = table_.BgClipRect
local row_min, row_max = TableEndRow_RowMin, TableEndRow_RowMax
row_min.x = mathMax(table_.WorkRect.Min.x, bg_clip_rect.Min.x); row_min.y = mathMax(bg_y1, bg_clip_rect.Min.y)
row_max.x = mathMin(table_.WorkRect.Max.x, bg_clip_rect.Max.x); row_max.y = mathMin(bg_y2, bg_clip_rect.Max.y)
if bg_col0 ~= 0 and row_min.y < row_max.y then
window.DrawList:AddRectFilled(row_min, row_max, bg_col0)
end
if bg_col1 ~= 0 and row_min.y < row_max.y then
window.DrawList:AddRectFilled(row_min, row_max, bg_col1)
end
end
-- Draw cell background color
if draw_cell_bg_color then
for cell_n = 0, table_.RowCellDataCurrent do
local cell_data = table_.RowCellData[cell_n]
-- As we render the BG here we need to clip things (for layout we would not)
-- FIXME: This cancels the OuterPadding addition done by TableGetCellBgRect(), need to keep it while rendering correctly while scrolling.
local column = table_.Columns[cell_data.Column]
local cell_bg_rect = ImGui.TableGetCellBgRect(table_, cell_data.Column)
cell_bg_rect:ClipWith(table_.BgClipRect)
cell_bg_rect.Min.x = mathMax(cell_bg_rect.Min.x, column.ClipRect.Min.x) -- So that first column after frozen one gets clipped when scrolling
cell_bg_rect.Max.x = mathMin(cell_bg_rect.Max.x, column.MaxX)
if cell_bg_rect.Min.y < cell_bg_rect.Max.y then
window.DrawList:AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data.BgColor)
end
end
end
-- Draw top border
if top_border_col ~= 0 and bg_y1 >= table_.BgClipRect.Min.y and bg_y1 < table_.BgClipRect.Max.y then
window.DrawList:AddLineH(table_.BorderX1, table_.BorderX2, bg_y1, top_border_col, border_size)
end
-- Draw bottom border at the row unfreezing mark (always strong)
if draw_strong_bottom_border and bg_y2 >= table_.BgClipRect.Min.y and bg_y2 < table_.BgClipRect.Max.y then
window.DrawList:AddLineH(table_.BorderX1, table_.BorderX2, bg_y2, table_.BorderColorStrong, border_size)
end
end
-- End frozen rows (when we are past the last frozen row line, teleport cursor and alter clipping rectangle)
-- - We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark
-- end of row and get the new cursor position.
if unfreeze_rows_request then
assert(table_.FreezeRowsRequest > 0)
for column_n = 0, table_.ColumnsCount - 1 do
table_.Columns[column_n].NavLayerCurrent = table_.NavLayer
end
local y0 = mathMax(table_.RowPosY2 + 1, table_.InnerClipRect.Min.y)
table_instance.LastFrozenHeight = y0 - table_.OuterRect.Min.y
if unfreeze_rows_actual then
assert(table_.IsUnfrozenRows == false)
table_.IsUnfrozenRows = true
-- BgClipRect starts as table.InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
local min_y = mathMin(y0, table_.InnerClipRect.Max.y)
table_.BgClipRect.Min.y = min_y; table_.Bg2ClipRectForDrawCmd.Min.y = min_y
table_.BgClipRect.Max.y = table_.InnerClipRect.Max.y; table_.Bg2ClipRectForDrawCmd.Max.y = table_.InnerClipRect.Max.y
table_.Bg2DrawChannelCurrent = table_.Bg2DrawChannelUnfrozen
assert(table_.Bg2ClipRectForDrawCmd.Min.y <= table_.Bg2ClipRectForDrawCmd.Max.y)
local row_height = table_.RowPosY2 - table_.RowPosY1
table_.RowPosY2 = table_.WorkRect.Min.y + table_.RowPosY2 - table_.OuterRect.Min.y
window.DC.CursorPos.y = table_.RowPosY2
table_.RowPosY1 = table_.RowPosY2 - row_height
for column_n = 0, table_.ColumnsCount - 1 do
local column = table_.Columns[column_n]
column.DrawChannelCurrent = column.DrawChannelUnfrozen
column.ClipRect.Min.y = table_.Bg2ClipRectForDrawCmd.Min.y
end
-- Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect.Min.y
ImGui.SetWindowClipRectBeforeSetChannel(window, table_.Columns[0].ClipRect)
table_.DrawSplitter:SetCurrentChannel(window.DrawList, table_.Columns[0].DrawChannelCurrent)
end
end
if band(table_.RowFlags, 1 --[[ImGuiTableRowFlags.Headers]]) == 0 then
table_.RowBgColorCounter = table_.RowBgColorCounter + 1
end
table_.IsInsideRow = false
end
-------------------------------------------------------------------------------
-- [SECTION] Tables: Columns changes
-------------------------------------------------------------------------------
-- - TableGetColumnIndex()
-- - TableSetColumnIndex()
-- - TableNextColumn()
-- - TableBeginCell() [Internal]
-- - TableEndCell() [Internal]
-------------------------------------------------------------------------------
function ImGui.TableGetColumnIndex()
local g = GImGui
local table_ = g.CurrentTable
if not table_ then
return 0
end
return table_.CurrentColumn
end
-- [Public] Append into a specific column
function ImGui.TableSetColumnIndex(column_n)
local g = GImGui
local table_ = g.CurrentTable
if not table_ then
return false
end
if table_.CurrentColumn ~= column_n then
if table_.CurrentColumn ~= -1 then
ImGui.TableEndCell(table_)
end
if not (column_n >= 0 and column_n < table_.ColumnsCount) then
IM_ASSERT_USER_ERROR(false, "TableSetColumnIndex() invalid column index!")
return false
end
ImGui.TableBeginCell(table_, column_n)
end
-- Return whether the column is visible. User may choose to skip submitting items based on this return value,
-- however they shouldn't skip submitting for columns that may have the tallest contribution to row height.
return table_.Columns[column_n].IsRequestOutput
end
-- [Public] Append into the next column, wrap and create a new row when already on last column
@native
function ImGui.TableNextColumn()
local g = GImGui
local table_ = g.CurrentTable
if not table_ then
return false
end
local current_column = table_.CurrentColumn
local next_column
if table_.IsInsideRow and current_column + 1 < table_.ColumnsCount then
if current_column ~= -1 then
ImGui.TableEndCell(table_)
end
next_column = current_column + 1
else
ImGui.TableNextRow()
next_column = 0
end
ImGui.TableBeginCell(table_, next_column)
-- Return whether the column is visible. User may choose to skip submitting items based on this return value,
-- however they shouldn't skip submitting for columns that may have the tallest contribution to row height.
return table_.Columns[next_column].IsRequestOutput
end
-- [Internal] Called by TableSetColumnIndex()/TableNextColumn()
-- This is called very frequently, so we need to be mindful of unnecessary overhead.
-- FIXME-TABLE FIXME-OPT: Could probably shortcut some things for non-active or clipped columns.
@native
function ImGui.TableBeginCell(table_, column_n)
local g = GImGui
local column = table_.Columns[column_n]
local window = table_.InnerWindow
local dc = window.DC -- [Luau] cache
table_.CurrentColumn = column_n
-- Start position is roughly ~~ CellRect.Min + CellPadding + Indent
local start_x = column.WorkMinX
if band(column.Flags, 65536 --[[ImGuiTableColumnFlags.IndentEnable]]) ~= 0 then
start_x = start_x + table_.RowIndentOffsetX -- ~~ += window.DC.Indent.x - table.HostIndentX, except we locked it for the row.
end
local start_y = table_.RowPosY1 + table_.RowCellPaddingY
dc.CursorPos.x = start_x
dc.CursorPos.y = start_y
dc.CursorMaxPos.x = start_x
dc.ColumnsOffset.x = start_x - window.Pos.x - dc.Indent.x -- FIXME-WORKRECT
dc.CursorPosPrevLine.x = start_x -- PrevLine.y is preserved. This allows users to call SameLine() to share LineSize between columns.
dc.CurrLineTextBaseOffset = table_.RowTextBaseline
dc.NavLayerCurrent = column.NavLayerCurrent
-- Note how WorkRect.Max.y is only set once during layout
local work_rect = window.WorkRect
work_rect.Min.y = start_y
work_rect.Min.x = column.WorkMinX
work_rect.Max.x = column.WorkMaxX
dc.ItemWidth = column.ItemWidth
local skip_items = column.IsSkipItems
window.SkipItems = skip_items
if skip_items then
g.LastItemData.ID = 0
g.LastItemData.StatusFlags = 0
end
-- Also see TablePushColumnChannel()
-- [Luau] inline the SetCurrentChannel() early-out (same channel) to skip the method call
local splitter = table_.DrawSplitter
if band(table_.Flags, 1048576 --[[ImGuiTableFlags.NoClip]]) ~= 0 then
-- FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed.
if splitter._Current ~= TABLE_DRAW_CHANNEL_NOCLIP then
splitter:SetCurrentChannel(window.DrawList, TABLE_DRAW_CHANNEL_NOCLIP)
end
--IM_ASSERT(table.DrawSplitter._Current == TABLE_DRAW_CHANNEL_NOCLIP)
else
-- FIXME-TABLE: Could avoid this if draw channel is dummy channel?
ImGui.SetWindowClipRectBeforeSetChannel(window, column.ClipRect)
local channel = column.DrawChannelCurrent
if splitter._Current ~= channel then
splitter:SetCurrentChannel(window.DrawList, channel)
end
end
-- Logging
if g.LogEnabled and not column.IsSkipItems then
ImGui.LogRenderedText(window.DC.CursorPos, "|")
g.LogLinePosY = 3.4028234663853e+38 --[[FLT_MAX]]
end
end
-- [Internal] Called by TableNextRow()/TableSetColumnIndex()/TableNextColumn()
@native
function ImGui.TableEndCell(table_)
local column = table_.Columns[table_.CurrentColumn]
local window = table_.InnerWindow
local dc = window.DC -- [Luau] cache; mathMax() inlined as math.max below
if dc.IsSetPos then
ImGui.ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
end
-- Report maximum position so we can infer content size per column.
local cursor_max_pos = dc.CursorMaxPos
local x = cursor_max_pos.x
if band(table_.RowFlags, 1 --[[ImGuiTableRowFlags.Headers]]) ~= 0 then
column.ContentMaxXHeadersUsed = mathMax(column.ContentMaxXHeadersUsed, x) -- Useful in case user submit contents in header row that is not a TableHeader() call
elseif table_.IsUnfrozenRows then
column.ContentMaxXUnfrozen = mathMax(column.ContentMaxXUnfrozen, x)
else
column.ContentMaxXFrozen = mathMax(column.ContentMaxXFrozen, x)
end
if column.IsEnabled then
table_.RowPosY2 = mathMax(table_.RowPosY2, cursor_max_pos.y + table_.RowCellPaddingY)
end
column.ItemWidth = dc.ItemWidth
-- Propagate text baseline for the entire row
-- FIXME-TABLE: Here we propagate text baseline from the last line of the cell.. instead of the first one.
table_.RowTextBaseline = mathMax(table_.RowTextBaseline, dc.PrevLineTextBaseOffset)
end
-------------------------------------------------------------------------------
-- [SECTION] Tables: Columns width management
-------------------------------------------------------------------------------
-- - TableGetMaxColumnWidth() [Internal]
-- - TableGetColumnWidthAuto() [Internal]
-- - TableSetColumnWidth()
-- - TableSetColumnWidthAutoSingle() [Internal]
-- - TableSetColumnWidthAutoAll() [Internal]
-- - TableUpdateColumnsWeightFromWidth() [Internal]
-------------------------------------------------------------------------------
-- Note that actual columns widths are computed in TableUpdateLayout().
-------------------------------------------------------------------------------
-- Maximum column content width given current layout. Use column.MinX so this value differs on a per-column basis.
function ImGui.TableCalcMaxColumnWidth(table_, column_n)
local column = table_.Columns[column_n]
local max_width = 3.4028234663853e+38 --[[FLT_MAX]]
local min_column_distance = table_.MinColumnWidth + table_.CellPaddingX * 2.0 + table_.CellSpacingX1 + table_.CellSpacingX2
if band(table_.Flags, 16777216 --[[ImGuiTableFlags.ScrollX]]) ~= 0 then
-- Frozen columns can't reach beyond visible width else scrolling will naturally break.
-- (we use DisplayOrder as within a set of multiple frozen column reordering is possible)
if column.DisplayOrder < table_.FreezeColumnsRequest then
max_width = (table_.InnerClipRect.Max.x - (table_.FreezeColumnsRequest - column.DisplayOrder) * min_column_distance) - column.MinX
max_width = max_width - table_.OuterPaddingX - table_.CellPaddingX - table_.CellSpacingX2
end
elseif band(table_.Flags, 262144 --[[ImGuiTableFlags.NoKeepColumnsVisible]]) == 0 then
-- If horizontal scrolling if disabled, we apply a final lossless shrinking of columns in order to make
-- sure they are all visible. Because of this we also know that all of the columns will always fit in
-- table.WorkRect and therefore in table.InnerRect (because ScrollX is off)
-- FIXME-TABLE: This is solved incorrectly but also quite a difficult problem to fix as we also want ClipRect width to match.
-- See "table_width_distrib" and "table_width_keep_visible" tests
max_width = table_.WorkRect.Max.x - (table_.ColumnsEnabledCount - column.IndexWithinEnabledSet - 1) * min_column_distance - column.MinX
--max_width = max_width - table.CellSpacingX1
max_width = max_width - table_.CellSpacingX2
max_width = max_width - table_.CellPaddingX * 2.0
max_width = max_width - table_.OuterPaddingX
end
return max_width
end
-- Note this is meant to be stored in column.WidthAuto, please generally use the WidthAuto field
function ImGui.TableGetColumnWidthAuto(table_, column)
local content_width_body = mathMax(column.ContentMaxXFrozen, column.ContentMaxXUnfrozen) - column.WorkMinX
local content_width_headers = column.ContentMaxXHeadersIdeal - column.WorkMinX
local width_auto = content_width_body
if band(column.Flags, 8192 --[[ImGuiTableColumnFlags.NoHeaderWidth]]) == 0 then
width_auto = mathMax(width_auto, content_width_headers)
end
-- Non-resizable fixed columns preserve their requested width
if band(column.Flags, 16 --[[ImGuiTableColumnFlags.WidthFixed]]) ~= 0 and column.InitStretchWeightOrWidth > 0.0 then
if band(table_.Flags, 1 --[[ImGuiTableFlags.Resizable]]) == 0 or band(column.Flags, 32 --[[ImGuiTableColumnFlags.NoResize]]) ~= 0 then
width_auto = column.InitStretchWeightOrWidth
end
end
return mathMax(width_auto, table_.MinColumnWidth)
end
-- 'width' = inner column width, without padding
function ImGui.TableSetColumnWidth(column_n, width)
local g = GImGui
local table_ = g.CurrentTable
assert(table_ ~= nil and table_.IsLayoutLocked == false)
assert(column_n >= 0 and column_n < table_.ColumnsCount)
local column_0 = table_.Columns[column_n]
local column_0_width = width
-- Apply constraints early
-- Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded)
assert(table_.MinColumnWidth > 0.0)
local min_width = table_.MinColumnWidth
local max_width = mathMax(min_width, column_0.WidthMax) -- Don't use TableCalcMaxColumnWidth() here as it would rely on MinX from last instance (#7933)
column_0_width = ImClamp(column_0_width, min_width, max_width)
if column_0.WidthGiven == column_0_width or column_0.WidthRequest == column_0_width then
return
end
--IMGUI_DEBUG_PRINT("TableSetColumnWidth(%d, %.1f->%.1f)\n", column_0_idx, column_0.WidthGiven, column_0_width)
local column_1 = (column_0.NextEnabledColumn ~= -1) and table_.Columns[column_0.NextEnabledColumn] or nil
-- In this surprisingly not simple because of how we support mixing Fixed and multiple Stretch columns.
-- - All fixed: easy.
-- - All stretch: easy.
-- - One or more fixed + one stretch: easy.
-- - One or more fixed + more than one stretch: tricky.
-- Qt when manual resize is enabled only supports a single _trailing_ stretch column, we support more cases here.
-- When forwarding resize from Wn| to Fn+1| we need to be considerate of the _NoResize flag on Fn+1.
-- FIXME-TABLE: Find a way to rewrite all of this so interactions feel more consistent for the user.
-- Scenarios:
-- - F1 F2 F3 resize from F1| or F2| --> ok: alter .WidthRequested of Fixed column. Subsequent columns will be offset.
-- - F1 F2 F3 resize from F3| --> ok: alter .WidthRequested of Fixed column. If active, ScrollX extent can be altered.
-- - F1 F2 W3 resize from F1| or F2| --> ok: alter .WidthRequested of Fixed column. If active, ScrollX extent can be altered, but it doesn't make much sense as the Stretch column will always be minimal size.
-- - F1 F2 W3 resize from W3| --> ok: no-op (disabled by Resize Rule 1)
-- - W1 W2 W3 resize from W1| or W2| --> ok
-- - W1 W2 W3 resize from W3| --> ok: no-op (disabled by Resize Rule 1)
-- - W1 F2 F3 resize from F3| --> ok: no-op (disabled by Resize Rule 1)
-- - W1 F2 resize from F2| --> ok: no-op (disabled by Resize Rule 1)
-- - W1 W2 F3 resize from W1| or W2| --> ok
-- - W1 F2 W3 resize from W1| or F2| --> ok
-- - F1 W2 F3 resize from W2| --> ok
-- - F1 W3 F2 resize from W3| --> ok
-- - W1 F2 F3 resize from W1| --> ok: equivalent to resizing |F2. F3 will not move.
-- - W1 F2 F3 resize from F2| --> ok
-- All resizes from a Wx columns are locking other columns.
-- Possible improvements:
-- - W1 W2 W3 resize W1| --> to not be stuck, both W2 and W3 would stretch down. Seems possible to fix. Would be most beneficial to simplify resize of all-weighted columns.
-- - W3 F1 F2 resize W3| --> to not be stuck past F1|, both F1 and F2 would need to stretch down, which would be lossy or ambiguous. Seems hard to fix.
-- [Resize Rule 1] Can't resize from right of right-most visible column if there is any Stretch column. Implemented in TableUpdateLayout().
-- If we have all Fixed columns OR resizing a Fixed column that doesn't come after a Stretch one, we can do an offsetting resize.
-- This is the preferred resize path
if band(column_0.Flags, 16 --[[ImGuiTableColumnFlags.WidthFixed]]) ~= 0 then
if not column_1 or table_.LeftMostStretchedColumn == -1 or table_.Columns[table_.LeftMostStretchedColumn].DisplayOrder >= column_0.DisplayOrder then
column_0.WidthRequest = column_0_width
table_.IsSettingsDirty = true
return
end
end
-- We can also use previous column if there's no next one (this is used when doing an auto-fit on the right-most stretch column)
if column_1 == nil then
column_1 = (column_0.PrevEnabledColumn ~= -1) and table_.Columns[column_0.PrevEnabledColumn] or nil
end
if column_1 == nil then
return
end
-- Resizing from right-side of a Stretch column before a Fixed column forward sizing to left-side of fixed column.
-- (old_a + old_b == new_a + new_b) --> (new_a == old_a + old_b - new_b)
local column_1_width = mathMax(column_1.WidthRequest - (column_0_width - column_0.WidthRequest), min_width)
column_0_width = column_0.WidthRequest + column_1.WidthRequest - column_1_width
assert(column_0_width > 0.0 and column_1_width > 0.0)
column_0.WidthRequest = column_0_width
column_1.WidthRequest = column_1_width
if band(bor(column_0.Flags, column_1.Flags), 8 --[[ImGuiTableColumnFlags.WidthStretch]]) ~= 0 then
ImGui.TableUpdateColumnsWeightFromWidth(table_)
end
table_.IsSettingsDirty = true
end
-- Disable clipping then auto-fit, will take 2 frames
-- (we don't take a shortcut for unclipped columns to reduce inconsistencies when e.g. resizing multiple columns)
function ImGui.TableSetColumnWidthAutoSingle(table_, column_n)
-- Single auto width uses auto-fit
local column = table_.Columns[column_n]
if not column.IsEnabled then
return
end
column.CannotSkipItemsQueue = lshift(1, 0)
table_.AutoFitSingleColumn = column_n
end
function ImGui.TableSetColumnWidthAutoAll(table_)
for column_n = 0, table_.ColumnsCount - 1 do
local column = table_.Columns[column_n]
if column.IsEnabled or band(column.Flags, 8 --[[ImGuiTableColumnFlags.WidthStretch]]) ~= 0 then -- Cannot reset weight of hidden stretch column
column.CannotSkipItemsQueue = lshift(1, 0)
column.AutoFitQueue = lshift(1, 1)
end
end
end
function ImGui.TableUpdateColumnsWeightFromWidth(table_)
assert(table_.LeftMostStretchedColumn ~= -1 and table_.RightMostStretchedColumn ~= -1)
-- Measure existing quantities
local visible_weight = 0.0
local visible_width = 0.0
for column_n = 0, table_.ColumnsCount - 1 do
local column = table_.Columns[column_n]
if column.IsEnabled and band(column.Flags, 8 --[[ImGuiTableColumnFlags.WidthStretch]]) ~= 0 then
assert(column.StretchWeight > 0.0)
visible_weight = visible_weight + column.StretchWeight
visible_width = visible_width + column.WidthRequest
end
end
assert(visible_weight > 0.0 and visible_width > 0.0)
-- Apply new weights
for column_n = 0, table_.ColumnsCount - 1 do
local column = table_.Columns[column_n]
if column.IsEnabled and band(column.Flags, 8 --[[ImGuiTableColumnFlags.WidthStretch]]) ~= 0 then
column.StretchWeight = (column.WidthRequest / visible_width) * visible_weight
assert(column.StretchWeight > 0.0)
end
end
end
-------------------------------------------------------------------------------
-- [SECTION] Tables: Drawing
-------------------------------------------------------------------------------
-- - TablePushBackgroundChannel() [Internal]
-- - TablePopBackgroundChannel() [Internal]
-- - TableSetupDrawChannels() [Internal]
-- - TableMergeDrawChannels() [Internal]
-- - TableGetColumnBorderCol() [Internal]
-- - TableDrawBorders() [Internal]
-------------------------------------------------------------------------------
-- FIXME: This could be abstracted and merged with PushColumnsBackground(), by creating a generic struct
-- with storage for backup cliprect + backup channel + storage for splitter pointer, new clip rect.
-- This would slightly simplify caller code.
-- Bg2 is used by Selectable (and possibly other widgets) to render to the background.
-- Unlike our Bg0/1 channel which we uses for RowBg/CellBg/Borders and where we guarantee all shapes to be CPU-clipped, the Bg2 channel being widgets-facing will rely on regular ClipRect.
function ImGui.TablePushBackgroundChannel()
local g = GImGui
local window = g.CurrentWindow
local table_ = g.CurrentTable
-- Optimization: avoid SetCurrentChannel() + PushClipRect()
ImRect_Copy(table_.HostBackupInnerClipRect, window.ClipRect)
ImGui.SetWindowClipRectBeforeSetChannel(window, table_.Bg2ClipRectForDrawCmd)
table_.DrawSplitter:SetCurrentChannel(window.DrawList, table_.Bg2DrawChannelCurrent)
end
function ImGui.TablePopBackgroundChannel()
local g = GImGui
local window = g.CurrentWindow
local table_ = g.CurrentTable
-- Optimization: avoid PopClipRect() + SetCurrentChannel()
ImGui.SetWindowClipRectBeforeSetChannel(window, table_.HostBackupInnerClipRect)
table_.DrawSplitter:SetCurrentChannel(window.DrawList, table_.Columns[table_.CurrentColumn].DrawChannelCurrent)
end
-- Also see TableBeginCell()
function ImGui.TablePushColumnChannel(column_n)
local g = GImGui
local table_ = g.CurrentTable
-- Optimization: avoid SetCurrentChannel() + PushClipRect()
if band(table_.Flags, 1048576 --[[ImGuiTableFlags.NoClip]]) ~= 0 then
return
end
local window = g.CurrentWindow
local column = table_.Columns[column_n]
ImGui.SetWindowClipRectBeforeSetChannel(window, column.ClipRect)
table_.DrawSplitter:SetCurrentChannel(window.DrawList, column.DrawChannelCurrent)
end
function ImGui.TablePopColumnChannel()
local g = GImGui
local table_ = g.CurrentTable
-- Optimization: avoid PopClipRect() + SetCurrentChannel()
if band(table_.Flags, 1048576 --[[ImGuiTableFlags.NoClip]]) ~= 0 or (table_.CurrentColumn == -1) then -- Calling TreePop() after TableNextRow() is supported.
return
end
local window = g.CurrentWindow
local column = table_.Columns[table_.CurrentColumn]
ImGui.SetWindowClipRectBeforeSetChannel(window, column.ClipRect)
table_.DrawSplitter:SetCurrentChannel(window.DrawList, column.DrawChannelCurrent)
end
-- Allocate draw channels. Called by TableUpdateLayout()
-- - We allocate them following storage order instead of display order so reordering columns won't needlessly
-- increase overall dormant memory cost.
-- - We isolate headers draw commands in their own channels instead of just altering clip rects.
-- This is in order to facilitate merging of draw commands.
-- - After crossing FreezeRowsCount, all columns see their current draw channel changed to a second set of channels.
-- - We only use the dummy draw channel so we can push a null clipping rectangle into it without affecting other
-- channels, while simplifying per-row/per-cell overhead. It will be empty and discarded when merged.
-- - We allocate 1 or 2 background draw channels. This is because we know TablePushBackgroundChannel() is only used for
-- horizontal spanning. If we allowed vertical spanning we'd need one background draw channel per merge group (1-4).
-- Draw channel allocation (before merging):
-- - NoClip --> 2+D+1 channels: bg0/1 + bg2 + foreground (same clip rect == always 1 draw call)
-- - Clip --> 2+D+N channels
-- - FreezeRows --> 2+D+N*2 (unless scrolling value is zero)
-- - FreezeRows || FreezeColumns --> 3+D+N*2 (unless scrolling value is zero)
-- Where D is 1 if any column is clipped or hidden (dummy channel) otherwise 0.
function ImGui.TableSetupDrawChannels(table_)
local freeze_row_multiplier = (table_.FreezeRowsCount > 0) and 2 or 1
local channels_for_row = (band(table_.Flags, 1048576 --[[ImGuiTableFlags.NoClip]]) ~= 0) and 1 or table_.ColumnsEnabledCount
local channels_for_bg = 1 + 1 * freeze_row_multiplier
local masks_differ = false
for n = 0, table_.ColumnsCount - 1 do
if (table_.VisibleMaskByIndex[n] == true) ~= (table_.EnabledMaskByIndex[n] == true) then
masks_differ = true
break
end
end
local channels_for_dummy = (table_.ColumnsEnabledCount < table_.ColumnsCount or masks_differ) and 1 or 0
local channels_total = channels_for_bg + (channels_for_row * freeze_row_multiplier) + channels_for_dummy
table_.DrawSplitter:Split(table_.InnerWindow.DrawList, channels_total)
table_.DummyDrawChannel = (channels_for_dummy > 0) and (channels_total - 1) or -1
table_.Bg2DrawChannelCurrent = TABLE_DRAW_CHANNEL_BG2_FROZEN
table_.Bg2DrawChannelUnfrozen = (table_.FreezeRowsCount > 0) and (2 + channels_for_row) or TABLE_DRAW_CHANNEL_BG2_FROZEN
local draw_channel_current = 2
for column_n = 0, table_.ColumnsCount - 1 do
local column = table_.Columns[column_n]
if column.IsVisibleX and column.IsVisibleY then
column.DrawChannelFrozen = draw_channel_current
column.DrawChannelUnfrozen = draw_channel_current + ((table_.FreezeRowsCount > 0) and (channels_for_row + 1) or 0)
if band(table_.Flags, 1048576 --[[ImGuiTableFlags.NoClip]]) == 0 then
draw_channel_current = draw_channel_current + 1
end
else
column.DrawChannelFrozen = table_.DummyDrawChannel
column.DrawChannelUnfrozen = table_.DummyDrawChannel
end
column.DrawChannelCurrent = column.DrawChannelFrozen
end
-- Initial draw cmd starts with a BgClipRect that matches the one of its host, to facilitate merge draw commands by default.
-- All our cell highlight are manually clipped with BgClipRect. When unfreezing it will be made smaller to fit scrolling rect.
-- (This technically isn't part of setting up draw channels, but is reasonably related to be done here)
ImRect_Copy(table_.BgClipRect, table_.InnerClipRect)
ImRect_Copy(table_.Bg0ClipRectForDrawCmd, table_.OuterWindow.ClipRect)
ImRect_Copy(table_.Bg2ClipRectForDrawCmd, table_.HostClipRect)
assert(table_.BgClipRect.Min.y <= table_.BgClipRect.Max.y)
end
-- This function reorder draw channels based on matching clip rectangle, to facilitate merging them. Called by EndTable().
-- For simplicity we call it TableMergeDrawChannels() but in fact it only reorder channels + overwrite ClipRect,
-- actual merging is done by table.DrawSplitter:Merge() which is called right after TableMergeDrawChannels().
--
-- Columns where the contents didn't stray off their local clip rectangle can be merged. To achieve
-- this we merge their clip rect and make them contiguous in the channel list, so they can be merged
-- by the call to DrawSplitter:Merge() following to the call to this function.
-- We reorder draw commands by arranging them into a maximum of 4 distinct groups:
--
-- 1 group: 2 groups: 2 groups: 4 groups:
-- [ 0. ] no freeze [ 0. ] row freeze [ 01 ] col freeze [ 01 ] row+col freeze
-- [ .. ] or no scroll [ 2. ] and v-scroll [ .. ] and h-scroll [ 23 ] and v+h-scroll
--
-- Each column itself can use 1 channel (row freeze disabled) or 2 channels (row freeze enabled).
-- When the contents of a column didn't stray off its limit, we move its channels into the corresponding group
-- based on its position (within frozen rows/columns groups or not).
-- At the end of the operation our 1-4 groups will each have a ImDrawCmd using the same ClipRect.
-- This function assume that each column are pointing to a distinct draw channel,
-- otherwise merge_group.ChannelsCount will not match set bit count of merge_group.ChannelsMask.
--
-- Column channels will not be merged into one of the 1-4 groups in the following cases:
-- - The contents stray off its clipping rectangle (we only compare the MaxX value, not the MinX value).
-- Direct ImDrawList calls won't be taken into account by default, if you use them make sure the ImGui.* bounds
-- matches, by e.g. calling SetCursorScreenPos().
-- - The channel uses more than one draw command itself. We drop all our attempt at merging stuff here..
-- we could do better but it's going to be rare and probably not worth the hassle.
-- Columns for which the draw channel(s) haven't been merged with other will use their own ImDrawCmd.
--
-- This function is particularly tricky to understand.. take a breath.
-- (Lua: splitter channel n lives in splitter._Channels.Data[n + 1]; bit arrays are Lua sets)
local TableMergeDrawChannels_MergeGroups = {}
for n = 0, 3 do
TableMergeDrawChannels_MergeGroups[n] = { ClipRect = ImRect(), ChannelsCount = 0, ChannelsMask = {} }
end
local TableMergeDrawChannels_RemainingMask = {}
local TableMergeDrawChannels_DstTmp = {}
function ImGui.TableMergeDrawChannels(table_)
local splitter = table_.DrawSplitter
local channels = splitter._Channels.Data
local has_freeze_v = (table_.FreezeRowsCount > 0)
local has_freeze_h = (table_.FreezeColumnsCount > 0)
assert(splitter._Current == 0)
-- Track which groups we are going to attempt to merge, and which channels goes into each group.
local merge_group_mask = 0x00
-- [Luau] reuse file-level scratch storage (function is not re-entrant, nothing escapes)
local merge_groups = TableMergeDrawChannels_MergeGroups
for n = 0, 3 do
local merge_group = merge_groups[n]
local cr = merge_group.ClipRect
cr.Min.x = 0.0; cr.Min.y = 0.0; cr.Max.x = 0.0; cr.Max.y = 0.0
merge_group.ChannelsCount = 0
table.clear(merge_group.ChannelsMask)
end
local max_draw_channels = (4 + table_.ColumnsCount * 2)
local remaining_mask = TableMergeDrawChannels_RemainingMask
table.clear(remaining_mask)
-- 1. Scan channels and take note of those which can be merged
for column_n = 0, table_.ColumnsCount - 1 do
if BitTest(table_.VisibleMaskByIndex, column_n) then
local column = table_.Columns[column_n]
local merge_group_sub_count = has_freeze_v and 2 or 1
for merge_group_sub_n = 0, merge_group_sub_count - 1 do
repeat
local channel_no = (merge_group_sub_n == 0) and column.DrawChannelFrozen or column.DrawChannelUnfrozen
-- Don't attempt to merge if there are multiple draw calls within the column
local src_channel = channels[channel_no + 1]
local cmds = src_channel._CmdBuffer
if cmds.Size > 0 and cmds.Data[cmds.Size].ElemCount == 0 and cmds.Data[cmds.Size].UserCallback == nil then -- Equivalent of PopUnusedDrawCmd()
cmds:pop_back()
end
if cmds.Size ~= 1 then
break
end
-- Find out the width of this merge group and check if it will fit in our column
-- (note that we assume that rendering didn't stray on the left direction. we should need a CursorMinPos to detect it)
if band(column.Flags, 256 --[[ImGuiTableColumnFlags.NoClip]]) == 0 then
local content_max_x
if not has_freeze_v then
content_max_x = mathMax(column.ContentMaxXUnfrozen, column.ContentMaxXHeadersUsed) -- No row freeze
elseif merge_group_sub_n == 0 then
content_max_x = mathMax(column.ContentMaxXFrozen, column.ContentMaxXHeadersUsed) -- Row freeze: use width before freeze
else
content_max_x = column.ContentMaxXUnfrozen -- Row freeze: use width after freeze
end
if content_max_x > column.ClipRect.Max.x then
break
end
end
local merge_group_n = ((has_freeze_h and column_n < table_.FreezeColumnsCount) and 0 or 1) + ((has_freeze_v and merge_group_sub_n == 0) and 0 or 2)
assert(channel_no < max_draw_channels)
local merge_group = merge_groups[merge_group_n]
if merge_group.ChannelsCount == 0 then
local gcr = merge_group.ClipRect -- [Luau] in place (merge_groups are local to this call)
gcr.Min.x = 3.4028234663853e+38 --[[FLT_MAX]]; gcr.Min.y = 3.4028234663853e+38 --[[FLT_MAX]]; gcr.Max.x = -3.4028234663853e+38 --[[FLT_MAX]]; gcr.Max.y = -3.4028234663853e+38 --[[FLT_MAX]]
end
merge_group.ChannelsMask[channel_no] = true
merge_group.ChannelsCount = merge_group.ChannelsCount + 1
local cr = cmds.Data[1].ClipRect
-- [Luau] scalar version of merge_group.ClipRect:Add(ImRect(cr.x, cr.y, cr.z, cr.w))
local gmin, gmax = merge_group.ClipRect.Min, merge_group.ClipRect.Max
if gmin.x > cr.x then gmin.x = cr.x end
if gmin.y > cr.y then gmin.y = cr.y end
if gmax.x < cr.z then gmax.x = cr.z end
if gmax.y < cr.w then gmax.y = cr.w end
merge_group_mask = bor(merge_group_mask, lshift(1, merge_group_n))
until true
end
-- Invalidate current draw channel
-- (we don't clear DrawChannelFrozen/DrawChannelUnfrozen solely to facilitate debugging/later inspection of data)
column.DrawChannelCurrent = -1
end
end
-- 2. Rewrite channel list in our preferred order
if merge_group_mask ~= 0 then
-- We skip channel 0 (Bg0/Bg1) and 1 (Bg2 frozen) from the shuffling since they won't move - see channels allocation in TableSetupDrawChannels().
local LEADING_DRAW_CHANNELS = 2
local dst_tmp = TableMergeDrawChannels_DstTmp -- [Luau] scratch
table.clear(dst_tmp)
for n = LEADING_DRAW_CHANNELS, splitter._Count - 1 do
remaining_mask[n] = true
end
remaining_mask[table_.Bg2DrawChannelUnfrozen] = nil
assert(has_freeze_v == false or table_.Bg2DrawChannelUnfrozen ~= TABLE_DRAW_CHANNEL_BG2_FROZEN)
local remaining_count = splitter._Count - (has_freeze_v and (LEADING_DRAW_CHANNELS + 1) or LEADING_DRAW_CHANNELS)
--local host_rect = (table.InnerWindow == table.OuterWindow) and table.InnerClipRect or table.HostClipRect
local host_rect = table_.HostClipRect
for merge_group_n = 0, 3 do
local merge_channels_count = merge_groups[merge_group_n].ChannelsCount
if merge_channels_count ~= 0 then
local merge_group = merge_groups[merge_group_n]
local merge_clip_rect = Rect(merge_group.ClipRect)
-- Extend outer-most clip limits to match those of host, so draw calls can be merged even if
-- outer-most columns have some outer padding offsetting them from their parent ClipRect.
-- The principal cases this is dealing with are:
-- - On a same-window table (not scrolling = single group), all fitting columns ClipRect -> will extend and match host ClipRect -> will merge
-- - Columns can use padding and have left-most ClipRect.Min.x and right-most ClipRect.Max.x != from host ClipRect -> will extend and match host ClipRect -> will merge
-- FIXME-TABLE FIXME-WORKRECT: We are wasting a merge opportunity on tables without scrolling if column doesn't fit
-- within host clip rect, solely because of the half-padding difference between window.WorkRect and window.InnerClipRect.
if band(merge_group_n, 1) == 0 or not has_freeze_h then
merge_clip_rect.Min.x = mathMin(merge_clip_rect.Min.x, host_rect.Min.x)
end
if band(merge_group_n, 2) == 0 or not has_freeze_v then
merge_clip_rect.Min.y = mathMin(merge_clip_rect.Min.y, host_rect.Min.y)
end
if band(merge_group_n, 1) ~= 0 then
merge_clip_rect.Max.x = mathMax(merge_clip_rect.Max.x, host_rect.Max.x)
end
if band(merge_group_n, 2) ~= 0 and band(table_.Flags, 131072 --[[ImGuiTableFlags.NoHostExtendY]]) == 0 then
merge_clip_rect.Max.y = mathMax(merge_clip_rect.Max.y, host_rect.Max.y)
end
--ImGui.GetForegroundDrawList():AddRect(merge_group.ClipRect.Min, merge_group.ClipRect.Max, IM_COL32(255, 0, 0, 200), 0.0, 0, 1.0) -- [DEBUG]
--ImGui.GetForegroundDrawList():AddLine(merge_group.ClipRect.Min, merge_clip_rect.Min, IM_COL32(255, 100, 0, 200))
--ImGui.GetForegroundDrawList():AddLine(merge_group.ClipRect.Max, merge_clip_rect.Max, IM_COL32(255, 100, 0, 200))
remaining_count = remaining_count - merge_group.ChannelsCount
for n in pairs(merge_group.ChannelsMask) do
remaining_mask[n] = nil
end
local n = 0
while n < splitter._Count and merge_channels_count ~= 0 do
-- Copy + overwrite new clip rect
if merge_group.ChannelsMask[n] then
merge_group.ChannelsMask[n] = nil
merge_channels_count = merge_channels_count - 1
local channel = channels[n + 1]
assert(channel._CmdBuffer.Size == 1)
local dst = channel._CmdBuffer.Data[1].ClipRect -- [Luau] scalar copy, no temporary ImVec4
dst.x = merge_clip_rect.Min.x; dst.y = merge_clip_rect.Min.y; dst.z = merge_clip_rect.Max.x; dst.w = merge_clip_rect.Max.y
dst_tmp[#dst_tmp + 1] = channel
end
n = n + 1
end
end
-- Make sure Bg2DrawChannelUnfrozen appears in the middle of our groups (whereas Bg0/Bg1 and Bg2 frozen are fixed to 0 and 1)
if merge_group_n == 1 and has_freeze_v then
dst_tmp[#dst_tmp + 1] = channels[table_.Bg2DrawChannelUnfrozen + 1]
end
end
-- Append unmergeable channels that we didn't reorder at the end of the list
local n = 0
while n < splitter._Count and remaining_count ~= 0 do
if remaining_mask[n] then
dst_tmp[#dst_tmp + 1] = channels[n + 1]
remaining_count = remaining_count - 1
end
n = n + 1
end
assert(#dst_tmp == splitter._Count - LEADING_DRAW_CHANNELS)
for i = 1, #dst_tmp do
channels[LEADING_DRAW_CHANNELS + i] = dst_tmp[i]
end
table.clear(dst_tmp) -- [Luau] don't keep channel references alive in the scratch
end
end
local function TableGetColumnBorderCol(table_, order_n, column_n)
local is_hovered = (table_.HoveredColumnBorder == column_n)
local is_resized = (table_.ResizedColumn == column_n) and (table_.InstanceInteracted == table_.InstanceCurrent)
local is_frozen_separator = (table_.FreezeColumnsCount == order_n + 1)
if is_resized or is_hovered then
return ImGui.GetColorU32(is_resized and 30 --[[ImGuiCol.SeparatorActive]] or 29 --[[ImGuiCol.SeparatorHovered]])
end
if is_frozen_separator or band(table_.Flags, bor(2048 --[[ImGuiTableFlags.NoBordersInBody]], 4096 --[[ImGuiTableFlags.NoBordersInBodyUntilResize]])) ~= 0 then
return table_.BorderColorStrong
end
return table_.BorderColorLight
end
-- FIXME-TABLE: This is a mess, need to redesign how we render borders (as some are also done in TableEndRow)
function ImGui.TableDrawBorders(table_)
local inner_window = table_.InnerWindow
if not table_.OuterWindow.ClipRect:Overlaps(table_.OuterRect) then
return
end
local inner_drawlist = inner_window.DrawList
table_.DrawSplitter:SetCurrentChannel(inner_drawlist, TABLE_DRAW_CHANNEL_BG0)
inner_drawlist:PushClipRect(table_.Bg0ClipRectForDrawCmd.Min, table_.Bg0ClipRectForDrawCmd.Max, false)
-- Draw inner border and resizing feedback
local table_instance = ImGui.TableGetInstanceData(table_, table_.InstanceCurrent)
local border_size = TABLE_BORDER_SIZE
local draw_y1 = mathMax(table_.InnerRect.Min.y, ((table_.FreezeRowsCount >= 1) and table_.InnerRect.Min.y or table_.WorkRect.Min.y) + table_.AngledHeadersHeight) + ((band(table_.Flags, 256 --[[ImGuiTableFlags.BordersOuterH]]) ~= 0) and border_size or 0.0)
local draw_y2_body = table_.InnerRect.Max.y
local draw_y2_head = table_.IsUsingHeaders and mathMin(table_.InnerRect.Max.y, ((table_.FreezeRowsCount >= 1) and table_.InnerRect.Min.y or table_.WorkRect.Min.y) + table_instance.LastTopHeadersRowHeight) or draw_y1
if band(table_.Flags, 512 --[[ImGuiTableFlags.BordersInnerV]]) ~= 0 then
for order_n = 0, table_.ColumnsCount - 1 do
repeat
if not BitTest(table_.EnabledMaskByDisplayOrder, order_n) then
break
end
local column_n = table_.DisplayOrderToIndex[order_n]
local column = table_.Columns[column_n]
local is_hovered = (table_.HoveredColumnBorder == column_n)
local is_resized = (table_.ResizedColumn == column_n) and (table_.InstanceInteracted == table_.InstanceCurrent)
local is_resizable = band(column.Flags, bor(32 --[[ImGuiTableColumnFlags.NoResize]], 1073741824 --[[ImGuiTableColumnFlags.NoDirectResize_]])) == 0
local is_frozen_separator = (table_.FreezeColumnsCount == order_n + 1)
if column.MaxX > table_.InnerClipRect.Max.x and not is_resized then
break
end
-- Decide whether right-most column is visible
if column.NextEnabledColumn == -1 and not is_resizable then
if band(table_.Flags, 57344 --[[ImGuiTableFlags.SizingMask_]]) ~= 16384 --[[ImGuiTableFlags.SizingFixedSame]] or band(table_.Flags, 65536 --[[ImGuiTableFlags.NoHostExtendX]]) ~= 0 then
break
end
end
if column.MaxX <= column.ClipRect.Min.x then -- FIXME-TABLE FIXME-STYLE: Assume BorderSize==1, this is problematic if we want to increase the border size..
break
end
-- Draw in outer window so right-most column won't be clipped
local draw_y2 = draw_y2_head
if is_frozen_separator then
draw_y2 = draw_y2_body
elseif band(table_.Flags, 4096 --[[ImGuiTableFlags.NoBordersInBodyUntilResize]]) ~= 0 and (is_hovered or is_resized) then
draw_y2 = draw_y2_body
elseif band(table_.Flags, bor(4096 --[[ImGuiTableFlags.NoBordersInBodyUntilResize]], 2048 --[[ImGuiTableFlags.NoBordersInBody]])) == 0 then
draw_y2 = draw_y2_body
end
if draw_y2 > draw_y1 then
inner_drawlist:AddLineV(column.MaxX, draw_y1, draw_y2, TableGetColumnBorderCol(table_, order_n, column_n), border_size)
end
until true
end
end
-- Draw outer border
-- FIXME: could use AddRect or explicit VLine/HLine helper?
if band(table_.Flags, 1280 --[[ImGuiTableFlags.BordersOuter]]) ~= 0 then
-- Display outer border offset by 1 which is a simple way to display it without adding an extra draw call
-- (Without the offset, in outer_window it would be rendered behind cells, because child windows are above their
-- parent. In inner_window, it won't reach out over scrollbars. Another weird solution would be to display part
-- of it in inner window, and the part that's over scrollbars in the outer window..)
-- Either solution currently won't allow us to use a larger border size: the border would clipped.
local outer_border = table_.OuterRect
local outer_col = table_.BorderColorStrong
if band(table_.Flags, 1280 --[[ImGuiTableFlags.BordersOuter]]) == 1280 --[[ImGuiTableFlags.BordersOuter]] then
inner_drawlist:AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0, border_size)
elseif band(table_.Flags, 1024 --[[ImGuiTableFlags.BordersOuterV]]) ~= 0 then
inner_drawlist:AddLineV(outer_border.Min.x, outer_border.Min.y, outer_border.Max.y, outer_col, border_size)
inner_drawlist:AddLineV(outer_border.Max.x - border_size, outer_border.Min.y, outer_border.Max.y, outer_col, border_size)
elseif band(table_.Flags, 256 --[[ImGuiTableFlags.BordersOuterH]]) ~= 0 then
inner_drawlist:AddLineH(outer_border.Min.x, outer_border.Max.x, outer_border.Min.y, outer_col, border_size)
inner_drawlist:AddLineH(outer_border.Min.x, outer_border.Max.x, outer_border.Max.y - border_size, outer_col, border_size)
end
end
if band(table_.Flags, 128 --[[ImGuiTableFlags.BordersInnerH]]) ~= 0 and table_.RowPosY2 < table_.OuterRect.Max.y then
-- Draw bottom-most row border between it is above outer border.
local h_inset = (band(table_.Flags, 1024 --[[ImGuiTableFlags.BordersOuterV]]) ~= 0) and border_size or 0.0
local border_y = table_.RowPosY2
if border_y >= table_.BgClipRect.Min.y and border_y < table_.BgClipRect.Max.y then
inner_drawlist:AddLineH(table_.BorderX1 + h_inset, table_.BorderX2 - h_inset, border_y, table_.BorderColorLight, border_size)
end
end
inner_drawlist:PopClipRect()
end
-------------------------------------------------------------------------------
-- [SECTION] Tables: Sorting
-------------------------------------------------------------------------------
-- - TableGetSortSpecs()
-- - TableFixColumnSortDirection() [Internal]
-- - TableGetColumnNextSortDirection() [Internal]
-- - TableSetColumnSortDirection() [Internal]
-- - TableSortSpecsSanitize() [Internal]
-- - TableSortSpecsBuild() [Internal]
-------------------------------------------------------------------------------
-- Return nil if no sort specs (most often when ImGuiTableFlags.Sortable is not set)
-- When 'sort_specs.SpecsDirty == true' you should sort your data. It will be true when sorting specs have
-- changed since last call, or the first time. Make sure to set 'SpecsDirty = false' after sorting,
-- else you may wastefully sort your data every frame!
-- Lifetime: don't hold on this table over multiple frames or past any subsequent call to BeginTable()!
-- PORT: Specs is a 1-based Lua array.
function ImGui.TableGetSortSpecs()
local g = GImGui
local table_ = g.CurrentTable
if table_ == nil or band(table_.Flags, 8 --[[ImGuiTableFlags.Sortable]]) == 0 then
return nil
end
-- Require layout (in case TableHeadersRow() hasn't been called) as it may alter IsSortSpecsDirty in some paths.
if not table_.IsLayoutLocked then
ImGui.TableUpdateLayout(table_)
end
ImGui.TableSortSpecsBuild(table_)
return table_.SortSpecs
end
local function TableGetColumnAvailSortDirection(column, n)
assert(n < column.SortDirectionsAvailCount)
return band(rshift(column.SortDirectionsAvailList, lshift(n, 1)), 0x03)
end
-- Fix sort direction if currently set on a value which is unavailable (e.g. activating NoSortAscending/NoSortDescending)
function ImGui.TableFixColumnSortDirection(table_, column)
if column.SortOrder == -1 or band(column.SortDirectionsAvailMask, lshift(1, column.SortDirection)) ~= 0 then
return
end
column.SortDirection = TableGetColumnAvailSortDirection(column, 0)
table_.IsSortSpecsDirty = true
end
-- Calculate next sort direction that would be set after clicking the column
-- - If the PreferSortDescending flag is set, we will default to a Descending direction on the first click.
-- - Note that the PreferSortAscending flag is never checked, it is essentially the default and therefore a no-op.
function ImGui.TableGetColumnNextSortDirection(column)
assert(column.SortDirectionsAvailCount > 0)
if column.SortOrder == -1 then
return TableGetColumnAvailSortDirection(column, 0)
end
for n = 0, 2 do
if column.SortDirection == TableGetColumnAvailSortDirection(column, n) then
return TableGetColumnAvailSortDirection(column, (n + 1) % column.SortDirectionsAvailCount)
end
end
assert(false)
return 0 --[[ImGuiSortDirection.None]]
end
-- Note that the NoSortAscending/NoSortDescending flags are processed in TableSortSpecsSanitize(), and they may change/revert
-- the value of SortDirection. We could technically also do it here but it would be unnecessary and duplicate code.
function ImGui.TableSetColumnSortDirection(column_n, sort_direction, append_to_sort_specs)
local g = GImGui
local table_ = g.CurrentTable
if band(table_.Flags, 67108864 --[[ImGuiTableFlags.SortMulti]]) == 0 then
append_to_sort_specs = false
end
if band(table_.Flags, 134217728 --[[ImGuiTableFlags.SortTristate]]) == 0 then
assert(sort_direction ~= 0 --[[ImGuiSortDirection.None]])
end
local sort_order_max = 0
if append_to_sort_specs then
for other_column_n = 0, table_.ColumnsCount - 1 do
sort_order_max = mathMax(sort_order_max, table_.Columns[other_column_n].SortOrder)
end
end
local column = table_.Columns[column_n]
column.SortDirection = sort_direction
if column.SortDirection == 0 --[[ImGuiSortDirection.None]] then
column.SortOrder = -1
elseif column.SortOrder == -1 or not append_to_sort_specs then
column.SortOrder = append_to_sort_specs and (sort_order_max + 1) or 0
end
for other_column_n = 0, table_.ColumnsCount - 1 do
local other_column = table_.Columns[other_column_n]
if other_column ~= column and not append_to_sort_specs then
other_column.SortOrder = -1
end
ImGui.TableFixColumnSortDirection(table_, other_column)
end
table_.IsSettingsDirty = true
table_.IsSortSpecsDirty = true
end
function ImGui.TableSortSpecsSanitize(table_)
assert(band(table_.Flags, 8 --[[ImGuiTableFlags.Sortable]]) ~= 0)
-- Clear SortOrder from hidden column and verify that there's no gap or duplicate.
local sort_order_count = 0
local sort_order_set = {} -- C++: ImU64 sort_order_mask
local sort_order_dup = false
for column_n = 0, table_.ColumnsCount - 1 do
local column = table_.Columns[column_n]
if column.SortOrder ~= -1 and not column.IsEnabled then
column.SortOrder = -1
end
if column.SortOrder ~= -1 then
sort_order_count = sort_order_count + 1
if sort_order_set[column.SortOrder] then sort_order_dup = true end
sort_order_set[column.SortOrder] = true
assert(sort_order_count < 64)
end
end
-- need_fix_linearize: (1 << count) != (mask + 1) <=> the set of sort orders isn't exactly {0..count-1}
local need_fix_linearize = sort_order_dup
for n = 0, sort_order_count - 1 do
if not sort_order_set[n] then need_fix_linearize = true end
end
local need_fix_single_sort_order = (sort_order_count > 1) and band(table_.Flags, 67108864 --[[ImGuiTableFlags.SortMulti]]) == 0
if need_fix_linearize or need_fix_single_sort_order then
local fixed_mask = {}
for sort_n = 0, sort_order_count - 1 do
-- Fix: Rewrite sort order fields if needed so they have no gap or duplicate.
-- (e.g. SortOrder 0 disappeared, SortOrder 1..2 exists --> rewrite then as SortOrder 0..1)
local column_with_smallest_sort_order = -1
for column_n = 0, table_.ColumnsCount - 1 do
if not fixed_mask[column_n] and table_.Columns[column_n].SortOrder ~= -1 then
if column_with_smallest_sort_order == -1 or table_.Columns[column_n].SortOrder < table_.Columns[column_with_smallest_sort_order].SortOrder then
column_with_smallest_sort_order = column_n
end
end
end
assert(column_with_smallest_sort_order ~= -1)
fixed_mask[column_with_smallest_sort_order] = true
table_.Columns[column_with_smallest_sort_order].SortOrder = sort_n
-- Fix: Make sure only one column has a SortOrder if ImGuiTableFlags.MultiSortable is not set.
if need_fix_single_sort_order then
sort_order_count = 1
for column_n = 0, table_.ColumnsCount - 1 do
if column_n ~= column_with_smallest_sort_order then
table_.Columns[column_n].SortOrder = -1
end
end
break
end
end
end
-- Fallback default sort order (if no column with the ImGuiTableColumnFlags.DefaultSort flag)
if sort_order_count == 0 and band(table_.Flags, 134217728 --[[ImGuiTableFlags.SortTristate]]) == 0 then
for column_n = 0, table_.ColumnsCount - 1 do
local column = table_.Columns[column_n]
if column.IsEnabled and band(column.Flags, 512 --[[ImGuiTableColumnFlags.NoSort]]) == 0 then
sort_order_count = 1
column.SortOrder = 0
column.SortDirection = TableGetColumnAvailSortDirection(column, 0)
break
end
end
end
table_.SortSpecsCount = sort_order_count
end
function ImGui.TableSortSpecsBuild(table_)
local dirty = table_.IsSortSpecsDirty
if dirty then
ImGui.TableSortSpecsSanitize(table_)
local multi = table_.SortSpecsMulti
local want = (table_.SortSpecsCount <= 1) and 0 or table_.SortSpecsCount
for n = #multi + 1, want do multi[n] = ImGuiTableColumnSortSpecs() end
for n = #multi, want + 1, -1 do multi[n] = nil end
table_.SortSpecs.SpecsDirty = true -- Mark as dirty for user
table_.IsSortSpecsDirty = false -- Mark as not dirty for us
end
-- Write output
-- May be able to move all SortSpecs data from table (48 bytes) to ImGuiTableTempData if we decide to write it back on every BeginTable()
local sort_specs
if table_.SortSpecsCount == 0 then
sort_specs = nil
elseif table_.SortSpecsCount == 1 then
table_.SortSpecsSingleArray = table_.SortSpecsSingleArray or { table_.SortSpecsSingle }
sort_specs = table_.SortSpecsSingleArray
else
sort_specs = table_.SortSpecsMulti
end
if dirty and sort_specs ~= nil then
for column_n = 0, table_.ColumnsCount - 1 do
local column = table_.Columns[column_n]
if column.SortOrder ~= -1 then
assert(column.SortOrder < table_.SortSpecsCount)
local sort_spec = sort_specs[column.SortOrder + 1]
sort_spec.ColumnUserID = column.UserData
sort_spec.ColumnIndex = column_n
sort_spec.SortOrder = column.SortOrder
sort_spec.SortDirection = column.SortDirection
end
end
end
table_.SortSpecs.Specs = sort_specs
table_.SortSpecs.SpecsCount = table_.SortSpecsCount
end
-------------------------------------------------------------------------------
-- [SECTION] Tables: Headers
-------------------------------------------------------------------------------
-- - TableGetHeaderRowHeight() [Internal]
-- - TableGetHeaderAngledMaxLabelWidth() [Internal]
-- - TableHeadersRow()
-- - TableHeader()
-- - TableAngledHeadersRow()
-- - TableAngledHeadersRowEx() [Internal]
-------------------------------------------------------------------------------
function ImGui.TableGetHeaderRowHeight()
-- Caring for a minor edge case:
-- Calculate row height, for the unlikely case that some labels may be taller than others.
-- If we didn't do that, uneven header height would highlight but smaller one before the tallest wouldn't catch input for all height.
-- In your custom header row you may omit this all together and just call TableNextRow() without a height...
local g = GImGui
local table_ = g.CurrentTable
local row_height = g.FontSize
for column_n = 0, table_.ColumnsCount - 1 do
if BitTest(table_.EnabledMaskByIndex, column_n) then
if band(table_.Columns[column_n].Flags, 4096 --[[ImGuiTableColumnFlags.NoHeaderLabel]]) == 0 then
row_height = mathMax(row_height, ImGui.CalcTextSize(ImGui.TableGetColumnName(table_, column_n)).y)
end
end
end
return row_height + g.Style.CellPadding.y * 2.0
end
function ImGui.TableGetHeaderAngledMaxLabelWidth()
local g = GImGui
local table_ = g.CurrentTable
local width = 0.0
for column_n = 0, table_.ColumnsCount - 1 do
if BitTest(table_.EnabledMaskByIndex, column_n) then
if band(table_.Columns[column_n].Flags, 262144 --[[ImGuiTableColumnFlags.AngledHeader]]) ~= 0 then
width = mathMax(width, ImGui.CalcTextSize(ImGui.TableGetColumnName(table_, column_n), nil, true).x)
end
end
end
return width + g.Style.CellPadding.y * 2.0 -- Swap padding
end
-- [Public] This is a helper to output TableHeader() calls based on the column names declared in TableSetupColumn().
-- The intent is that advanced users willing to create customized headers would not need to use this helper
-- and can create their own! For example: TableHeader() may be preceded by Checkbox() or other custom widgets.
-- See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this.
-- This code is intentionally written to not make much use of internal functions, to give you better direction
-- if you need to write your own.
-- FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public.
function ImGui.TableHeadersRow()
local g = GImGui
local table_ = g.CurrentTable
IM_ASSERT_USER_ERROR_RET(table_ ~= nil, "Call should only be done while in BeginTable() scope!")
-- Call layout if not already done. This is automatically done by TableNextRow: we do it here _only_ to make
-- it easier to debug-step in TableUpdateLayout(). Your own version of this function doesn't need this.
if not table_.IsLayoutLocked then
ImGui.TableUpdateLayout(table_)
end
-- Open row
local row_height = ImGui.TableGetHeaderRowHeight()
ImGui.TableNextRow(1 --[[ImGuiTableRowFlags.Headers]], row_height)
local row_y1 = ImGui.GetCursorScreenPos().y
if table_.HostSkipItems then -- Merely an optimization, you may skip in your own code.
return
end
local columns_count = ImGui.TableGetColumnCount()
for column_n = 0, columns_count - 1 do
if ImGui.TableSetColumnIndex(column_n) or table_.LastHeldHeaderColumn == column_n then
-- Push an id to allow empty/unnamed headers. This is also idiomatic as it ensure there is a consistent ID path to access columns (for e.g. automation)
local name = (band(ImGui.TableGetColumnFlags(column_n), 4096 --[[ImGuiTableColumnFlags.NoHeaderLabel]]) ~= 0) and "" or ImGui.TableGetColumnName(column_n)
ImGui.PushID(column_n)
ImGui.TableHeader(name)
ImGui.PopID()
end
end
-- Allow opening popup from the right-most section after the last column.
local mouse_pos = ImGui.GetMousePos()
if ImGui.IsMouseReleased(1) and ImGui.TableGetHoveredColumn() == columns_count then
if mouse_pos.y >= row_y1 and mouse_pos.y < row_y1 + row_height then
ImGui.TableOpenContextMenu(columns_count) -- Will open a non-column-specific popup.
end
end
end
-- Emit a column header (text + optional sort order)
-- We cpu-clip text here so that all columns headers can be merged into a same draw call.
-- Note that because of how we cpu-clip and display sorting indicators, you _cannot_ use SameLine() after a TableHeader()
function ImGui.TableHeader(label)
local g = GImGui
local window = g.CurrentWindow
if window.SkipItems then
return
end
local table_ = g.CurrentTable
IM_ASSERT_USER_ERROR_RET(table_ ~= nil, "Call should only be done while in BeginTable() scope!")
assert(table_.CurrentColumn ~= -1)
local column_n = table_.CurrentColumn
local column = table_.Columns[column_n]
-- Label
if label == nil then
label = ""
end
local label_end = ImGui.FindRenderedTextEnd(label)
local label_size = ImGui.CalcTextSize(label, label_end, false)
local label_pos = V2(window.DC.CursorPos)
-- If we already got a row height, there's use that.
-- FIXME-TABLE: Padding problem if the correct outer-padding CellBgRect strays off our ClipRect?
-- [Luau] scalar version of: cell_r = TableGetCellBgRect(table_, column_n) (Min.x/Min.y/Max.x/Max.y -> cell_x1/cell_y1/cell_x2/cell_y2)
local cell_x1 = mathMax(column.MinX, table_.WorkRect.Min.x)
local cell_x2 = mathMin(column.MaxX, table_.WorkRect.Max.x)
local cell_y1, cell_y2 = table_.RowPosY1, table_.RowPosY2
local label_height = mathMax(label_size.y, table_.RowMinHeight - table_.RowCellPaddingY * 2.0)
-- Calculate ideal size for sort order arrow
local w_arrow = 0.0
local w_sort_text = 0.0
local sort_arrow = false
local sort_order_suf = ""
local ARROW_SCALE = 0.65
if band(table_.Flags, 8 --[[ImGuiTableFlags.Sortable]]) ~= 0 and band(column.Flags, 512 --[[ImGuiTableColumnFlags.NoSort]]) == 0 then
w_arrow = ImTrunc(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x)
if column.SortOrder ~= -1 then
sort_arrow = true
end
if column.SortOrder > 0 then
sort_order_suf = string.format("%d", column.SortOrder + 1)
w_sort_text = g.Style.ItemInnerSpacing.x + ImGui.CalcTextSize(sort_order_suf).x
end
end
-- We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considered for merging.
local max_pos_x = label_pos.x + label_size.x + w_sort_text + w_arrow
column.ContentMaxXHeadersUsed = mathMax(column.ContentMaxXHeadersUsed, sort_arrow and cell_x2 or mathMin(max_pos_x, cell_x2))
column.ContentMaxXHeadersIdeal = mathMax(column.ContentMaxXHeadersIdeal, max_pos_x)
-- Keep header highlighted when context menu is open.
local id = window:GetID(label)
local bb = ImRect(cell_x1, cell_y1, cell_x2, mathMax(cell_y2, cell_y1 + label_height + g.Style.CellPadding.y * 2.0))
ImGui.ItemSize(ImVec2(0.0, label_height)) -- Don't declare unclipped width, it'll be fed ContentMaxPosHeadersIdeal
if not ImGui.ItemAdd(bb, id) then
return
end
--GetForegroundDrawList():AddRect(cell_r.Min, cell_r.Max, IM_COL32(255, 0, 0, 255)) -- [DEBUG]
--GetForegroundDrawList():AddRect(bb.Min, bb.Max, IM_COL32(255, 0, 0, 255)) -- [DEBUG]
-- Using AllowOverlap mode because we cover the whole cell, and we want user to be able to submit subsequent items.
local highlight = (table_.HighlightColumnHeader == column_n)
local pressed, hovered, held = ImGui.ButtonBehavior(bb, id, 4096 --[[ImGuiButtonFlags.AllowOverlap]])
if held or hovered or highlight then
local col = ImGui.GetColorU32(held and 27 --[[ImGuiCol.HeaderActive]] or (hovered and 26 --[[ImGuiCol.HeaderHovered]] or 25 --[[ImGuiCol.Header]]))
--RenderFrame(bb.Min, bb.Max, col, false, 0.0)
ImGui.TableSetBgColor(3 --[[ImGuiTableBgTarget.CellBg]], col, table_.CurrentColumn)
else
-- Submit single cell bg color in the case we didn't submit a full header row
if band(table_.RowFlags, 1 --[[ImGuiTableRowFlags.Headers]]) == 0 then
ImGui.TableSetBgColor(3 --[[ImGuiTableBgTarget.CellBg]], ImGui.GetColorU32(48 --[[ImGuiCol.TableHeaderBg]]), table_.CurrentColumn)
end
end
ImGui.RenderNavCursor(bb, id, 2 --[[ImGuiNavRenderCursorFlags.Compact]], 0.0)
if held then
table_.HeldHeaderColumn = column_n
end
window.DC.CursorPos.y = window.DC.CursorPos.y - g.Style.ItemSpacing.y * 0.5
-- Drag and drop to re-order columns.
-- FIXME-TABLE: Scroll request while reordering a column and it lands out of the scrolling zone.
if held and band(table_.Flags, 2 --[[ImGuiTableFlags.Reorderable]]) ~= 0 and ImGui.IsMouseDragging(0) and not g.DragDropActive then
-- - While moving a column it will jump on the other side of the mouse, so we also test for MouseDelta.x
-- - We need to handle reordering across hidden columns.
-- In the configuration below, moving C to the right of E will lead to:
-- ... C [D] E ---> ... [D] E C (Column name/index)
-- ... 2 3 4 ... 2 3 4 (Display order)
-- - The other constraints are enforced by TableQueueSetColumnDisplayOrder() which might early out.
table_.InstanceInteracted = table_.InstanceCurrent
if g.IO.MouseDelta.x < 0.0 and g.IO.MousePos.x < cell_x1 then
local prev_column = (column.PrevEnabledColumn ~= -1) and table_.Columns[column.PrevEnabledColumn] or nil
if prev_column then
ImGui.TableQueueSetColumnDisplayOrder(table_, column_n, prev_column.DisplayOrder)
end
end
if g.IO.MouseDelta.x > 0.0 and g.IO.MousePos.x > cell_x2 then
local next_column = (column.NextEnabledColumn ~= -1) and table_.Columns[column.NextEnabledColumn] or nil
if next_column then
ImGui.TableQueueSetColumnDisplayOrder(table_, column_n, next_column.DisplayOrder)
end
end
end
-- Sort order arrow
-- - Only clip label for a visible arrow. Auto-fit still accounts for a possible arrow, but when
-- manually sized smaller we don't clip the label for the sake of an arrow that isn't displayed.
local ellipsis_max = mathMax(cell_x2 - (sort_arrow and (w_arrow + w_sort_text) or 0.0), label_pos.x)
if band(table_.Flags, 8 --[[ImGuiTableFlags.Sortable]]) ~= 0 and band(column.Flags, 512 --[[ImGuiTableColumnFlags.NoSort]]) == 0 then
if column.SortOrder ~= -1 then
local x = mathMax(cell_x1, cell_x2 - w_arrow - w_sort_text)
local y = label_pos.y
if column.SortOrder > 0 then
ImGui.PushStyleColor(0 --[[ImGuiCol.Text]], ImGui.GetColorU32(0 --[[ImGuiCol.Text]], 0.70))
ImGui.RenderText(ImVec2(x + g.Style.ItemInnerSpacing.x, y), sort_order_suf)
ImGui.PopStyleColor()
x = x + w_sort_text
end
ImGui.RenderArrow(window.DrawList, ImVec2(x, y), ImGui.GetColorU32(0 --[[ImGuiCol.Text]]), (column.SortDirection == 1 --[[ImGuiSortDirection.Ascending]]) and 2 --[[ImGuiDir.Up]] or 3 --[[ImGuiDir.Down]], ARROW_SCALE)
end
-- Handle clicking on column header to adjust Sort Order
if pressed and table_.ReorderColumn ~= column_n then
local sort_direction = ImGui.TableGetColumnNextSortDirection(column)
ImGui.TableSetColumnSortDirection(column_n, sort_direction, g.IO.KeyShift)
end
end
-- Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will
-- be merged into a single draw call.
--window.DrawList:AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE)
ImGui.RenderTextEllipsis(window.DrawList, label_pos, ImVec2(ellipsis_max, bb.Max.y), ellipsis_max, label, label_end, label_size)
local text_clipped = label_size.x > (ellipsis_max - label_pos.x)
if text_clipped and hovered and g.ActiveId == 0 then
ImGui.SetItemTooltip("%s", string.sub(label, 1, label_end - 1))
end
-- We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden
if ImGui.IsPopupOpenRequestForItem(0 --[[ImGuiPopupFlags.None]], id) then
ImGui.TableOpenContextMenu(column_n)
end
end
-- Unlike TableHeadersRow() it is not expected that you can reimplement or customize this with custom widgets.
-- FIXME: No hit-testing/button on the angled header.
function ImGui.TableAngledHeadersRow()
local g = GImGui
local table_ = g.CurrentTable
local temp_data = table_.TempData
temp_data.AngledHeadersRequests:resize(0)
-- Which column needs highlight?
local row_id = ImGui.GetID("##AngledHeaders")
local table_instance = ImGui.TableGetInstanceData(table_, table_.InstanceCurrent)
local highlight_column_n = (table_.LastHeldHeaderColumn ~= -1) and table_.LastHeldHeaderColumn or table_.HighlightColumnHeader
if highlight_column_n == -1 and table_.HoveredColumnBody ~= -1 then
if table_instance.HoveredRowLast == 0 and table_.HoveredColumnBorder == -1 and (g.ActiveId == 0 or g.ActiveId == row_id or (table_.IsActiveIdInTable or g.DragDropActive)) then
highlight_column_n = table_.HoveredColumnBody
end
end
-- Build up request
local col_header_bg = ImGui.GetColorU32(48 --[[ImGuiCol.TableHeaderBg]])
local col_text = ImGui.GetColorU32(0 --[[ImGuiCol.Text]])
for order_n = 0, table_.ColumnsCount - 1 do
if BitTest(table_.EnabledMaskByDisplayOrder, order_n) then
local column_n = table_.DisplayOrderToIndex[order_n]
local column = table_.Columns[column_n]
if band(column.Flags, 262144 --[[ImGuiTableColumnFlags.AngledHeader]]) ~= 0 then -- Note: can't rely on ImGuiTableColumnFlags.IsVisible test here.
temp_data.AngledHeadersRequests:push_back({ Index = column_n, TextColor = col_text, BgColor0 = col_header_bg, BgColor1 = (column_n == highlight_column_n) and ImGui.GetColorU32(25 --[[ImGuiCol.Header]]) or 0 })
end
end
end
-- Render row
ImGui.TableAngledHeadersRowEx(row_id, g.Style.TableAngledHeadersAngle, 0.0, temp_data.AngledHeadersRequests.Data, temp_data.AngledHeadersRequests.Size)
end
local function ImTextCountLines(text, text_begin, text_end)
local count = 0
local p = text_begin
while p < text_end do
local line_end = string.find(text, "\n", p, true)
if line_end == nil or line_end >= text_end then p = text_end else p = line_end + 1 end
count = count + 1
end
return count
end
-- Important: data must be fed left to right
--- @param data ImGuiTableHeaderData[] # 1-based array
--- @param data_count int
function ImGui.TableAngledHeadersRowEx(row_id, angle, max_label_width, data, data_count)
local g = GImGui
local table_ = g.CurrentTable
local window = g.CurrentWindow
local draw_list = window.DrawList
IM_ASSERT_USER_ERROR_RET(table_ ~= nil, "Call should only be done while in BeginTable() scope!")
assert(table_.CurrentRow == -1, "Must be first row")
if max_label_width == 0.0 then
max_label_width = ImGui.TableGetHeaderAngledMaxLabelWidth()
end
-- Angle argument expressed in (-IM_PI/2 .. +IM_PI/2) as it is easier to think about for user.
local flip_label = (angle < 0.0)
angle = angle - 3.141592653589793 --[[IM_PI]] * 0.5
local cos_a = ImCos(angle)
local sin_a = ImSin(angle)
local label_cos_a = flip_label and ImCos(angle + 3.141592653589793 --[[IM_PI]]) or cos_a
local label_sin_a = flip_label and ImSin(angle + 3.141592653589793 --[[IM_PI]]) or sin_a
local unit_right = ImVec2(cos_a, sin_a)
-- Calculate our base metrics and set angled headers data _before_ the first call to TableNextRow()
-- FIXME-STYLE: Would it be better for user to submit 'max_label_width' or 'row_height' ? One can be derived from the other.
local header_height = g.FontSize + g.Style.CellPadding.x * 2.0
local row_height = ImTrunc(ImAbs(ImRotate(ImVec2(max_label_width, flip_label and header_height or -header_height), cos_a, sin_a).y))
table_.AngledHeadersHeight = row_height
table_.AngledHeadersSlope = (sin_a ~= 0.0) and (cos_a / sin_a) or 0.0
local header_angled_vector = unit_right * (row_height / -sin_a) -- vector from bottom-left to top-left, and from bottom-right to top-right
-- Declare row, override and draw our own background
-- FIXME-TABLE: Generally broken when overlapping frozen columns limit.
ImGui.TableNextRow(1 --[[ImGuiTableRowFlags.Headers]], row_height)
ImGui.TableNextColumn()
local row_r = ImRect(table_.WorkRect.Min.x, table_.BgClipRect.Min.y, table_.WorkRect.Max.x, table_.RowPosY2)
table_.DrawSplitter:SetCurrentChannel(draw_list, TABLE_DRAW_CHANNEL_BG0)
local clip_rect_min_x = table_.BgClipRect.Min.x
if table_.FreezeColumnsCount > 0 then
clip_rect_min_x = ImMax(clip_rect_min_x, table_.Columns[table_.DisplayOrderToIndex[table_.FreezeColumnsCount - 1]].MaxX)
end
ImGui.TableSetBgColor(1 --[[ImGuiTableBgTarget.RowBg0]], 0) -- Cancel
ImGui.PushClipRect(table_.BgClipRect.Min, table_.BgClipRect.Max, false) -- Span all columns
draw_list:AddRectFilled(ImVec2(table_.BgClipRect.Min.x, row_r.Min.y), ImVec2(table_.BgClipRect.Max.x, row_r.Max.y), ImGui.GetColorU32(48 --[[ImGuiCol.TableHeaderBg]], 0.25)) -- FIXME-STYLE: Change row background with an arbitrary color.
ImGui.PushClipRect(ImVec2(clip_rect_min_x, table_.BgClipRect.Min.y), table_.BgClipRect.Max, true) -- Span all columns
ImGui.ButtonBehavior(row_r, row_id)
ImGui.KeepAliveID(row_id)
local ascent_scaled = g.FontBaked.Ascent * g.FontBakedScale -- FIXME: Standardize those scaling factors better
local line_off_for_ascent_x = (ImMax((g.FontSize - ascent_scaled) * 0.5, 0.0) / -sin_a) * (flip_label and -1.0 or 1.0)
local padding = g.Style.CellPadding -- We will always use swapped component
local align = g.Style.TableAngledHeadersTextAlign
-- Draw background and labels in first pass, then all borders.
local max_x = -3.4028234663853e+38 --[[FLT_MAX]]
for pass = 0, 1 do
for order_n = 0, data_count - 1 do
local request = data[order_n + 1]
local column_n = request.Index
local column = table_.Columns[column_n]
local bg_shape = {}
bg_shape[0] = ImVec2(column.MaxX, row_r.Max.y)
bg_shape[1] = ImVec2(column.MinX, row_r.Max.y)
bg_shape[2] = bg_shape[1] + header_angled_vector
bg_shape[3] = bg_shape[0] + header_angled_vector
if pass == 0 then
-- Draw shape
draw_list:AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request.BgColor0)
draw_list:AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request.BgColor1) -- Optional highlight
max_x = ImMax(max_x, bg_shape[3].x)
-- Draw label
-- - First draw at an offset where RenderTextXXX() function won't meddle with applying current ClipRect, then transform to final offset.
-- - Handle multiple lines manually, as we want each lines to follow on the horizontal border, rather than see a whole block rotated.
local label_name = ImGui.TableGetColumnName(table_, column_n)
local label_name_end = ImGui.FindRenderedTextEnd(label_name)
local line_off_step_x = (g.FontSize / -sin_a)
local label_lines = ImTextCountLines(label_name, 1, label_name_end)
-- Left<>Right alignment
local line_off_curr_x = flip_label and ((label_lines - 1) * line_off_step_x) or 0.0
local line_off_for_align_x = ImFloor(ImMax((((column.MaxX - column.MinX) - padding.x * 2.0) - (label_lines * line_off_step_x)), 0.0) * align.x)
line_off_curr_x = line_off_curr_x + line_off_for_align_x - line_off_for_ascent_x
-- Register header width
local w = column.WorkMinX + ImCeil(label_lines * line_off_step_x - line_off_for_align_x)
column.ContentMaxXHeadersUsed = w; column.ContentMaxXHeadersIdeal = w
local p = 1
while p < label_name_end do
local label_name_eol = string.find(label_name, "\n", p, true)
if label_name_eol == nil or label_name_eol > label_name_end then
label_name_eol = label_name_end
end
local line = string.sub(label_name, p, label_name_eol - 1)
-- FIXME: Individual line clipping for right-most column is broken for negative angles.
local label_size = ImGui.CalcTextSize(line)
local clip_width = max_label_width - padding.y -- Using padding.y*2.0f would be symmetrical but hide more text.
local clip_height = ImMin(label_size.y, column.ClipRect.Max.x - column.WorkMinX - line_off_curr_x)
local clip_r = ImRect(window.ClipRect.Min, window.ClipRect.Min + ImVec2(clip_width, clip_height))
local vtx_idx_begin = draw_list._VtxCurrentIdx
ImGui.PushStyleColor(0 --[[ImGuiCol.Text]], request.TextColor)
ImGui.RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, line, #line + 1, label_size)
ImGui.PopStyleColor()
local vtx_idx_end = draw_list._VtxCurrentIdx
-- Up<>Down alignment
local available_space = ImMax(clip_width - label_size.x + ImAbs(padding.x * cos_a) * 2.0 - ImAbs(padding.y * sin_a) * 2.0, 0.0)
local vertical_offset = available_space * align.y * (flip_label and -1.0 or 1.0)
-- Rotate and offset label
local pivot_in = ImVec2(window.ClipRect.Min.x - vertical_offset, window.ClipRect.Min.y + label_size.y)
local pivot_out = ImVec2(column.WorkMinX, row_r.Max.y)
line_off_curr_x = line_off_curr_x + (flip_label and -line_off_step_x or line_off_step_x)
pivot_out = pivot_out + unit_right * padding.y
if flip_label then
pivot_out = pivot_out + unit_right * (clip_width - ImMax(0.0, clip_width - label_size.x))
end
pivot_out.x = pivot_out.x + (flip_label and (line_off_curr_x + line_off_step_x) or line_off_curr_x)
ImGui.ShadeVertsTransformPos(draw_list, vtx_idx_begin, vtx_idx_end, pivot_in, label_cos_a, label_sin_a, pivot_out) -- Rotate and offset
--if g.IO.KeyShift then local fg_dl = GetForegroundDrawList(); vtx_idx_begin = fg_dl._VtxCurrentIdx; fg_dl:AddRect(clip_r.Min, clip_r.Max, IM_COL32(0, 255, 0, 255), 0.0, 0, 1.0); ShadeVertsTransformPos(fg_dl, vtx_idx_begin, fg_dl._VtxCurrentIdx, pivot_in, label_cos_a, label_sin_a, pivot_out) end
p = label_name_eol + 1
end
end
if pass == 1 then
-- Draw border
local border_size = TABLE_BORDER_SIZE
draw_list:AddLine(bg_shape[0] + ImVec2(border_size * 0.5, 0.0), bg_shape[3] + ImVec2(border_size * 0.5, 0.0), TableGetColumnBorderCol(table_, order_n, column_n), border_size)
end
end
end
ImGui.PopClipRect()
ImGui.PopClipRect()
table_.TempData.AngledHeadersExtraWidth = ImMax(0.0, max_x - table_.Columns[table_.RightMostEnabledColumn].MaxX)
end
-------------------------------------------------------------------------------
-- [SECTION] Tables: Context Menu
-------------------------------------------------------------------------------
-- - TableOpenContextMenu() [Internal]
-- - TableBeginContextMenuPopup() [Internal]
-- - TableDrawDefaultContextMenu() [Internal]
-------------------------------------------------------------------------------
-- Use -1 to open menu not specific to a given column.
function ImGui.TableOpenContextMenu(column_n)
if column_n == nil then column_n = -1 end
local g = GImGui
local table_ = g.CurrentTable
if column_n == -1 and table_.CurrentColumn ~= -1 then -- When called within a column automatically use this one (for consistency)
column_n = table_.CurrentColumn
end
if column_n == table_.ColumnsCount then -- To facilitate using with TableGetHoveredColumn()
column_n = -1
end
assert(column_n >= -1 and column_n < table_.ColumnsCount)
if band(table_.Flags, bor(1 --[[ImGuiTableFlags.Resizable]], 2 --[[ImGuiTableFlags.Reorderable]], 4 --[[ImGuiTableFlags.Hideable]])) ~= 0 then
table_.IsContextPopupOpen = true
table_.ContextPopupColumn = column_n
table_.InstanceInteracted = table_.InstanceCurrent
local context_menu_id = ImHashStr("##ContextMenu", nil, table_.ID)
ImGui.OpenPopupEx(context_menu_id, 0 --[[ImGuiPopupFlags.None]])
end
end
function ImGui.TableBeginContextMenuPopup(table_)
if not table_.IsContextPopupOpen or table_.InstanceCurrent ~= table_.InstanceInteracted then
return false
end
local context_menu_id = ImHashStr("##ContextMenu", nil, table_.ID)
if ImGui.BeginPopupEx(context_menu_id, bor(64 --[[ImGuiWindowFlags.AlwaysAutoResize]], 1 --[[ImGuiWindowFlags.NoTitleBar]], 256 --[[ImGuiWindowFlags.NoSavedSettings]])) then
return true
end
table_.IsContextPopupOpen = false
return false
end
-- FIXME: Copied from MenuItem() for the purpose of being able to pass _SelectOnRelease (#9312)
local function MenuItemForColumnReorder(label, selected, enabled)
local g = GImGui
local window = g.CurrentWindow
local label_size = ImGui.CalcTextSize(label, nil, true)
local offsets = window.DC.MenuColumns
local checkmark_w = IM_TRUNC(g.FontSize * 1.20)
local min_w = offsets:DeclColumns(0.0, label_size.x, 0.0, checkmark_w) -- Feedback for next frame
local stretch_w = ImMax(0.0, ImGui.GetContentRegionAvail().x - min_w)
local text_pos = ImVec2(window.DC.CursorPos.x, window.DC.CursorPos.y + window.DC.CurrLineTextBaseOffset)
local id = ImGui.GetID(label)
local selectable_flags = bor(8388608 --[[ImGuiSelectableFlags.SelectOnRelease]], 16777216 --[[ImGuiSelectableFlags.SpanAvailWidth]])
if g.ActiveId == id then
selectable_flags = bor(selectable_flags, 32 --[[ImGuiSelectableFlags.Highlight]]) -- Stays highlighted while dragging.
end
local has_been_moved = (g.ActiveId == id) and g.ActiveIdHasBeenEditedBefore -- But disable toggling once moved.
ImGui.BeginDisabled(not enabled) -- Don't use ImGuiSelectableFlags.Disabled so that Check mark is also affected.
local ret = ImGui.Selectable(label, false, selectable_flags, ImVec2(min_w, label_size.y)) and not has_been_moved -- Can't use IsMouseDragging(0) as button is released already.
if band(g.LastItemData.StatusFlags, 256 --[[ImGuiItemStatusFlags.Visible]]) ~= 0 and selected then
ImGui.RenderCheckMark(window.DrawList, text_pos + ImVec2(offsets.OffsetMark + stretch_w + g.FontSize * 0.40, g.FontSize * 0.134 * 0.5), ImGui.GetColorU32(0 --[[ImGuiCol.Text]]), g.FontSize * 0.866)
end
ImGui.EndDisabled()
return ret
end
-- Output context menu into current window (generally a popup)
-- FIXME-TABLE: Ideally this should be writable by the user. Full programmatic access to that data?
-- Sections to display are pulled from 'flags_for_section_to_display', which is typically == table.Flags.
-- - ImGuiTableFlags.Resizable -> display Sizing menu items
-- - ImGuiTableFlags.Reorderable -> display "Reset Order"
---- ImGuiTableFlags.Sortable -> display sorting options (disabled)
-- - ImGuiTableFlags.Hideable -> display columns visibility menu items
-- It means if you have a custom context menus you can call this section and omit some sections, and add your own.
function ImGui.TableDrawDefaultContextMenu(table_, flags_for_section_to_display)
local g = GImGui
local window = g.CurrentWindow
if window.SkipItems then
return
end
local want_separator = false
local context_column_n = (table_.ContextPopupColumn >= 0 and table_.ContextPopupColumn < table_.ColumnsCount) and table_.ContextPopupColumn or -1
local context_column = (context_column_n ~= -1) and table_.Columns[context_column_n] or nil
-- Sizing
if band(flags_for_section_to_display, 1 --[[ImGuiTableFlags.Resizable]]) ~= 0 then
if context_column ~= nil then
local can_resize = band(context_column.Flags, 32 --[[ImGuiTableColumnFlags.NoResize]]) == 0 and context_column.IsEnabled
if ImGui.MenuItem(ImGui.LocalizeGetMsg(2 --[[ImGuiLocKey.TableSizeOne]]), nil, false, can_resize) then -- "###SizeOne"
ImGui.TableSetColumnWidthAutoSingle(table_, context_column_n)
end
end
local size_all_desc
if table_.ColumnsEnabledFixedCount == table_.ColumnsEnabledCount and band(table_.Flags, 57344 --[[ImGuiTableFlags.SizingMask_]]) ~= 16384 --[[ImGuiTableFlags.SizingFixedSame]] then
size_all_desc = ImGui.LocalizeGetMsg(3 --[[ImGuiLocKey.TableSizeAllFit]]) -- "###SizeAll" All fixed
else
size_all_desc = ImGui.LocalizeGetMsg(4 --[[ImGuiLocKey.TableSizeAllDefault]]) -- "###SizeAll" All stretch or mixed
end
if ImGui.MenuItem(size_all_desc, nil) then
ImGui.TableSetColumnWidthAutoAll(table_)
end
want_separator = true
end
-- Reset Order/Visibility etc.
if band(flags_for_section_to_display, bor(2 --[[ImGuiTableFlags.Reorderable]], 4 --[[ImGuiTableFlags.Hideable]])) ~= 0 then
if ImGui.BeginMenu(ImGui.LocalizeGetMsg(5 --[[ImGuiLocKey.TableReset]])) then
--if ImGui.MenuItem(ImGui.LocalizeGetMsg(ImGuiLocKey.TableResetAll)) then -- FIXME: Hiding because altering Sort Order + requesting auto-fit simultaneously has a tendency to exhibit a sizing glitch.
-- table_.IsResetAllRequest = true
--end
if band(flags_for_section_to_display, 2 --[[ImGuiTableFlags.Reorderable]]) ~= 0 then
if ImGui.MenuItem(ImGui.LocalizeGetMsg(6 --[[ImGuiLocKey.TableResetOrder]]), nil, false, not table_.IsDefaultDisplayOrder) then -- PS: cannot be hidden because it would mess with drag reordering.
table_.IsResetDisplayOrderRequest = true
end
end
if band(flags_for_section_to_display, 4 --[[ImGuiTableFlags.Hideable]]) ~= 0 then
if ImGui.MenuItem(ImGui.LocalizeGetMsg(7 --[[ImGuiLocKey.TableResetVisibility]]), nil, false, not table_.IsDefaultVisibility) then
table_.IsResetVisibilityRequest = true
end
end
ImGui.EndMenu()
end
end
-- Sorting
-- (modify TableOpenContextMenu() to add _Sortable flag if enabling this)
-- #if 0
--if band(flags_for_section_to_display, ImGuiTableFlags.Sortable) ~= 0 and column ~= nil and band(column.Flags, ImGuiTableColumnFlags.NoSort) == 0 then
-- if want_separator then ImGui.Separator() end
-- want_separator = true
-- local append_to_sort_specs = g.IO.KeyShift
-- if ImGui.MenuItem("Sort in Ascending Order", nil, column.SortOrder ~= -1 and column.SortDirection == ImGuiSortDirection.Ascending, band(column.Flags, ImGuiTableColumnFlags.NoSortAscending) == 0) then
-- ImGui.TableSetColumnSortDirection(table_, column_n, ImGuiSortDirection.Ascending, append_to_sort_specs)
-- end
-- if ImGui.MenuItem("Sort in Descending Order", nil, column.SortOrder ~= -1 and column.SortDirection == ImGuiSortDirection.Descending, band(column.Flags, ImGuiTableColumnFlags.NoSortDescending) == 0) then
-- ImGui.TableSetColumnSortDirection(table_, column_n, ImGuiSortDirection.Descending, append_to_sort_specs)
-- end
--end
-- #endif
-- Hiding / Visibility
if band(flags_for_section_to_display, 4 --[[ImGuiTableFlags.Hideable]]) ~= 0 then
if want_separator then
ImGui.Separator()
end
want_separator = true
-- While reordering: we calculate min/max allowed range once here so we can avoid a O(N log N) in the loop (because the query itself does a sweep scan).
-- This assume that reordering constraints output a single range, otherwise would need to either call TableGetMaxDisplayOrderAllowed() for each item below, or cache this once per frame into columns.
local is_reordering = (g.ActiveId ~= 0 and g.ActiveIdWindow == g.CurrentWindow and table_.ReorderColumn ~= -1 and g.ActiveIdHasBeenEditedBefore) -- FIXME: This is a bit of a hack.
local reorder_src_order = is_reordering and table_.Columns[table_.ReorderColumn].DisplayOrder or -1
local reorder_min_order = is_reordering and TableGetMaxDisplayOrderAllowed(table_, reorder_src_order, 0) or 0
local reorder_max_order = is_reordering and TableGetMaxDisplayOrderAllowed(table_, reorder_src_order, table_.ColumnsCount - 1) or (table_.ColumnsCount - 1)
ImGui.PushItemFlag(16 --[[ImGuiItemFlags.AutoClosePopups]], false)
for order_n = 0, table_.ColumnsCount - 1 do
local column_n = table_.DisplayOrderToIndex[order_n]
local column = table_.Columns[column_n]
if band(column.Flags, 1 --[[ImGuiTableColumnFlags.Disabled]]) == 0 then
local name = ImGui.TableGetColumnName(table_, column_n)
if name == nil or name == "" then
name = "<Unknown>"
end
-- Make sure we can't hide the last active column
local menu_item_enabled = band(column.Flags, 128 --[[ImGuiTableColumnFlags.NoHide]]) == 0
if column.IsUserEnabled and table_.ColumnsEnabledCount <= 1 then
menu_item_enabled = false
end
if is_reordering and (column.DisplayOrder < reorder_min_order or column.DisplayOrder > reorder_max_order) then
menu_item_enabled = false
end
if MenuItemForColumnReorder(name, column.IsUserEnabled, menu_item_enabled) then
column.IsUserEnabledNextFrame = not column.IsUserEnabled
end
-- Drag to reorder
-- FIXME: It is currently not possible to reorder columns marked with ImGuiTableColumnFlags.NoHide.
if ImGui.IsItemActive() and ImGui.IsMouseDragging(0) and g.ActiveIdSource == 1 --[[ImGuiInputSource.Mouse]] and band(table_.Flags, 2 --[[ImGuiTableFlags.Reorderable]]) ~= 0 then
g.ActiveIdHasBeenEditedBefore = true -- Disable toggle in MenuItemForColumnReorder() + start dimming to display allowed reorder targets.
table_.ReorderColumn = column_n
if not ImGui.IsItemHovered() then
local reorder_dir = (g.IO.MousePos.y < (g.LastItemData.Rect.Min.y + g.LastItemData.Rect.Max.y) * 0.5) and -1 or 1
local reorder_amount = ((reorder_dir < 0) and (g.LastItemData.Rect.Min.y - g.IO.MousePos.y) or (g.IO.MousePos.y - g.LastItemData.Rect.Max.y)) / g.LastItemData.Rect:GetHeight()
local dst_order = column.DisplayOrder + ImTrunc(ImCeil(reorder_amount)) * reorder_dir -- Estimated target order, will be validated and clamped.
ImGui.TableQueueSetColumnDisplayOrder(table_, column_n, dst_order)
end
end
end
end
ImGui.PopItemFlag()
end
end
-------------------------------------------------------------------------------
-- [SECTION] Tables: Settings (.ini data)
-------------------------------------------------------------------------------
-- FIXME: The binding/finding/creating flow are too confusing.
-------------------------------------------------------------------------------
-- - TableSettingsInit() [Internal]
-- - TableSettingsCreate() [Internal]
-- - TableSettingsFindByID() [Internal]
-- - TableGetBoundSettings() [Internal]
-- - TableResetSettings()
-- - TableSaveSettings() [Internal]
-- - TableLoadSettings() [Internal]
-- - TableLoadSettingsForColumns() [Internal]
-- - TableSettingsHandler_ClearAll() [Internal]
-- - TableSettingsHandler_ApplyAll() [Internal]
-- - TableSettingsHandler_ReadOpen() [Internal]
-- - TableSettingsHandler_ReadLine() [Internal]
-- - TableSettingsHandler_WriteAll() [Internal]
-- - TableSettingsAddSettingsHandler() [Internal]
-------------------------------------------------------------------------------
-- [Init] 1: TableSettingsHandler_ReadXXXX() Load and parse .ini file into TableSettings.
-- [Main] 2: TableLoadSettings() When table is created, bind Table to TableSettings, serialize TableSettings data into Table.
-- [Main] 3: TableSaveSettings() When table properties are modified, serialize Table data into bound or new TableSettings, mark .ini as dirty.
-- [Main] 4: TableSettingsHandler_WriteAll() When .ini file is dirty (which can come from other source), save TableSettings into .ini file.
-------------------------------------------------------------------------------
-- Clear and initialize empty settings instance
local function TableSettingsInit(settings, id, columns_count, columns_count_max)
for k, v in pairs(ImGuiTableSettings()) do settings[k] = v end
for n = 0, columns_count_max - 1 do
settings.Columns[n] = ImGuiTableColumnSettings()
end
settings.ID = id
settings.ColumnsCount = columns_count
settings.ColumnsCountMax = columns_count_max
settings.WantApply = true
end
function ImGui.TableSettingsCreate(id, columns_count)
local g = GImGui
local settings = ImGui.TableSettingsFindByID(id)
if settings then
if settings.ColumnsCountMax >= columns_count then
TableSettingsInit(settings, id, columns_count, settings.ColumnsCountMax) -- Recycle
return settings
end
settings.ID = 0 -- Invalidate storage, we won't fit because of a count change
end
settings = ImGuiTableSettings()
g.SettingsTables:push_back(settings)
TableSettingsInit(settings, id, columns_count, columns_count)
return settings
end
-- Find existing settings
function ImGui.TableSettingsFindByID(id)
-- FIXME-OPT: Might want to store a lookup map for this?
local g = GImGui
for _, settings in g.SettingsTables:iter() do
if settings.ID == id then
return settings
end
end
return nil
end
local function SettingsTablesOffsetFromPtr(settings)
local g = GImGui
return g.SettingsTables:find_index(settings) or -1
end
-- Get settings for a given table, nil if none
function ImGui.TableGetBoundSettings(table_)
if table_.SettingsOffset == -1 then
return nil
end
local g = GImGui
local settings = g.SettingsTables.Data[table_.SettingsOffset]
assert(settings.ID == table_.ID)
return settings
end
-- Restore initial state of table (with or without saved settings)
function ImGui.TableResetSettings(table_)
table_.IsInitializing = true; table_.IsSettingsDirty = true
table_.IsResetAllRequest = false
table_.IsSettingsRequestLoad = false -- Don't reload from ini
table_.SettingsLoadedFlags = 0 --[[ImGuiTableFlags.None]] -- Mark as nothing loaded so our initialized data becomes authoritative
end
function ImGui.TableSaveSettings(table_)
table_.IsSettingsDirty = false
if band(table_.Flags, 16 --[[ImGuiTableFlags.NoSavedSettings]]) ~= 0 then
return
end
-- Bind or create settings data
local settings = ImGui.TableGetBoundSettings(table_)
if settings ~= nil and table_.ColumnsCount > settings.ColumnsCountMax then
settings.ID = 0 -- Invalidate storage, we won't fit because of a count change
settings = nil
end
if settings == nil then
settings = ImGui.TableSettingsCreate(table_.ID, table_.ColumnsCount)
table_.SettingsOffset = SettingsTablesOffsetFromPtr(settings)
end
settings.ColumnsCount = table_.ColumnsCount
settings.LastUsedDate = GImGui.SessionDate:Unpack()
-- Serialize ImGuiTable/ImGuiTableColumn into ImGuiTableSettings/ImGuiTableColumnSettings
assert(settings.ID == table_.ID)
assert(settings.ColumnsCount == table_.ColumnsCount and settings.ColumnsCountMax >= settings.ColumnsCount)
local save_ref_scale = false
settings.SaveFlags = 0 --[[ImGuiTableFlags.None]]
for n = 0, table_.ColumnsCount - 1 do
local column = table_.Columns[n]
local column_settings = settings.Columns[n]
local width_or_weight = (band(column.Flags, 8 --[[ImGuiTableColumnFlags.WidthStretch]]) ~= 0) and column.StretchWeight or column.WidthRequest
column_settings.ID = column.ID
column_settings.WidthOrWeight = width_or_weight
column_settings.Index = n
column_settings.DisplayOrder = column.DisplayOrder
column_settings.SortOrder = column.SortOrder
column_settings.SortDirection = column.SortDirection
column_settings.IsEnabled = column.IsUserEnabled and 1 or 0
column_settings.IsStretch = (band(column.Flags, 8 --[[ImGuiTableColumnFlags.WidthStretch]]) ~= 0) and 1 or 0
if band(column.Flags, 8 --[[ImGuiTableColumnFlags.WidthStretch]]) == 0 then
save_ref_scale = true
end
-- We skip saving some data in the .ini file when they are unnecessary to restore our state.
-- Note that fixed width where initial width was derived from auto-fit will always be saved as InitStretchWeightOrWidth will be 0.0f.
-- FIXME-TABLE: We don't have logic to easily compare SortOrder to DefaultSortOrder yet so it's always saved when present.
if width_or_weight ~= column.InitStretchWeightOrWidth then
settings.SaveFlags = bor(settings.SaveFlags, 1 --[[ImGuiTableFlags.Resizable]])
end
if column.DisplayOrder ~= n then
settings.SaveFlags = bor(settings.SaveFlags, 2 --[[ImGuiTableFlags.Reorderable]])
end
if column.SortOrder ~= -1 then
settings.SaveFlags = bor(settings.SaveFlags, 8 --[[ImGuiTableFlags.Sortable]], 2 --[[ImGuiTableFlags.Reorderable]]) -- Because SortOrder saving itself is gated, make sure every column is saved (#9519)
end
if column.IsUserEnabled ~= (band(column.Flags, 2 --[[ImGuiTableColumnFlags.DefaultHide]]) == 0) then
settings.SaveFlags = bor(settings.SaveFlags, 4 --[[ImGuiTableFlags.Hideable]])
end
end
settings.SaveFlags = band(settings.SaveFlags, table_.Flags)
settings.RefScale = save_ref_scale and table_.RefScale or 0.0
ImGui.MarkIniSettingsDirty()
end
function ImGui.TableLoadSettings(table_)
if band(table_.Flags, 16 --[[ImGuiTableFlags.NoSavedSettings]]) ~= 0 then
table_.IsSettingsRequestLoad = false -- Done
return
end
-- Bind settings
local settings
if table_.SettingsOffset == -1 then
settings = ImGui.TableSettingsFindByID(table_.ID)
if settings == nil then
return
end
if settings.ColumnsCount ~= table_.ColumnsCount then -- Allow settings if columns count changed. We could otherwise decide to return...
table_.IsSettingsDirty = true
end
table_.SettingsOffset = SettingsTablesOffsetFromPtr(settings)
else
settings = ImGui.TableGetBoundSettings(table_)
end
table_.SettingsLoadedFlags = settings.SaveFlags
table_.RefScale = settings.RefScale
settings.LastUsedDate = GImGui.SessionDate:Unpack()
-- TableUpdateLayout() will then call TableLoadSettingsForColumns() to apply the data.
end
-- NB: This was written to be similar to the logic in TableReconcileColumns().
function ImGui.TableLoadSettingsForColumns(table_)
for n = 0, table_.ColumnsSize - 1 do
table_.Columns[n].IsLoadedSettings = false
end
local settings = ImGui.TableGetBoundSettings(table_)
if settings == nil then
return
end
table_.SettingsLoadedFlags = bor(table_.SettingsLoadedFlags, 2 --[[ImGuiTableFlags.Reorderable]]) -- We handle above in code above.
-- Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn
local column_settings = settings.Columns
-- Fast path
local matches = 0
for n = 0, table_.ColumnsCount - 1 do
if n >= settings.ColumnsCount or column_settings[n].ID ~= table_.Columns[n].ID then
break
end
ImGui.TableLoadSettingsForColumn(table_.Columns[n], column_settings[n], settings.SaveFlags)
matches = matches + 1
end
if matches == settings.ColumnsCount then
return
end
local settings_start_n = matches -- Small optimization
for n = settings_start_n, settings.ColumnsCount - 1 do
column_settings[n].IsLoaded = false
end
-- Find matches for named columns
for c = 0, table_.ColumnsSize - 1 do
local column = table_.Columns[c]
if column.ID ~= 0 and not column.IsLoadedSettings then
for n = settings_start_n, settings.ColumnsCount - 1 do
if column_settings[n].ID == column.ID and not column_settings[n].IsLoaded then
ImGui.TableLoadSettingsForColumn(column, column_settings[n], settings.SaveFlags)
matches = matches + 1
break
end
end
end
end
-- Remaining entries are matched sequentially
local dst_idx = 0
for n = settings_start_n, settings.ColumnsCount - 1 do
if not column_settings[n].IsLoaded then
while dst_idx < table_.ColumnsCount and table_.Columns[dst_idx].IsLoadedSettings do
dst_idx = dst_idx + 1
end
if dst_idx >= table_.ColumnsCount then
break
end
ImGui.TableLoadSettingsForColumn(table_.Columns[dst_idx], column_settings[n], settings.SaveFlags)
dst_idx = dst_idx + 1
end
end
end
function ImGui.TableLoadSettingsForColumn(column, column_settings, load_flags)
column.IsLoadedSettings = true
column_settings.IsLoaded = true
if band(load_flags, 1 --[[ImGuiTableFlags.Resizable]]) ~= 0 then
if column_settings.IsStretch ~= 0 then
column.StretchWeight = column_settings.WidthOrWeight
else
column.WidthRequest = column_settings.WidthOrWeight
end
column.AutoFitQueue = 0x00
end
if band(load_flags, 2 --[[ImGuiTableFlags.Reorderable]]) ~= 0 then
column.DisplayOrder = column_settings.DisplayOrder
else
column.DisplayOrder = column_settings.Index -- Because default depends on previous Index, we need to set that up and cannot rely on TableInitColumnDefaults()
end
if band(load_flags, 4 --[[ImGuiTableFlags.Hideable]]) ~= 0 and column_settings.IsEnabled ~= -1 then
local v = (column_settings.IsEnabled == 1)
column.IsUserEnabled = v; column.IsUserEnabledNextFrame = v
end
column.SortOrder = column_settings.SortOrder
column.SortDirection = column_settings.SortDirection
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)
function ImGui.TableFixDisplayOrder(table_)
if band(table_.Flags, 2 --[[ImGuiTableFlags.Reorderable]]) == 0 then
for n = 0, table_.ColumnsCount - 1 do
table_.Columns[n].DisplayOrder = n
table_.DisplayOrderToIndex[n] = n
end
return
end
local fdo_columns = {}
for n = 0, table_.ColumnsCount - 1 do
fdo_columns[n + 1] = n
end
-- Sort by DisplayOrder and then Index
table.sort(fdo_columns, function(lhs_idx, rhs_idx) return TableFixDisplayOrderComparer(table_, lhs_idx, rhs_idx) end)
for n = 0, table_.ColumnsCount - 1 do
table_.Columns[fdo_columns[n + 1]].DisplayOrder = n
end
for n = 0, table_.ColumnsCount - 1 do
table_.DisplayOrderToIndex[table_.Columns[n].DisplayOrder] = n
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
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:Unpack()
end
end
end
local function ForEachTable(g, fn)
for i = 0, g.Tables:GetMapSize() - 1 do
local t = g.Tables:GetByIndex(i)
if t then fn(t) end
end
end
local function TableSettingsHandler_ClearAll(ctx, handler)
local g = ctx
ForEachTable(g, function(t) t.SettingsOffset = -1 end)
g.SettingsTables:clear()
end
-- Apply to existing windows (if any)
local function TableSettingsHandler_ApplyAll(ctx, handler)
local g = ctx
ForEachTable(g, function(t)
t.IsSettingsRequestLoad = true
t.SettingsOffset = -1
end)
end
local function TableSettingsHandler_ReadOpen(ctx, handler, name)
-- FIXME: As topology changes are allowed, strictly speaking a >= IMGUI_TABLE_MAX_COLUMNS tables stored in .ini file
-- that was emitted with a higher max count could still be meaningful in some unlikely cases.
-- We might want to use another MAX defined as (1<<(sizeof(ImGuiTableColumnIdx)*8-1))-1.
local id, columns_count = string.match(name, "^0x(%x+),(%-?%d+)")
if id == nil then
return nil
end
id = tonumber(id, 16); columns_count = tonumber(columns_count)
if columns_count <= 0 or columns_count >= 512 --[[IMGUI_TABLE_MAX_COLUMNS]] then
return nil
end
return ImGui.TableSettingsCreate(id, columns_count)
end
local function TableSettingsHandler_ReadLine(ctx, handler, settings, line)
-- "Column 0 UserID=0x42AD2D21 Width=100 Visible=1 Order=0 Sort=0v"
if settings == nil then return end
local f = string.match(line, "^RefScale=([%-%d%.eE+]+)")
if f then settings.RefScale = tonumber(f); return end
local n = string.match(line, "^LastUsed=(%d+)")
if n then settings.LastUsedDate = ImGuiPackedDate(tonumber(n)):Unpack(); return end
local column_n, rest = string.match(line, "^Column (%-?%d+)(.*)$")
if column_n then
column_n = tonumber(column_n)
if column_n < 0 or column_n >= settings.ColumnsCount then
return
end
local column = settings.Columns[column_n]
column.Index = column_n
local function take(pattern)
local a, b, r = string.match(rest, "^%s*" .. pattern .. "()")
if a == nil then return nil end
if r == nil then r = b; b = nil end
rest = string.sub(rest, r)
return a, b
end
take("UserID=0x(%x+)") -- FIXME-LEGACY: Removed 2025/11/12, was never properly set.
local v = take("Width=(%-?%d+)")
if v then column.WidthOrWeight = tonumber(v); column.IsStretch = 0; settings.SaveFlags = bor(settings.SaveFlags, 1 --[[ImGuiTableFlags.Resizable]]) end
v = take("Weight=([%-%d%.eE+]+)")
if v then column.WidthOrWeight = tonumber(v); column.IsStretch = 1; settings.SaveFlags = bor(settings.SaveFlags, 1 --[[ImGuiTableFlags.Resizable]]) end
v = take("Visible=(%-?%d+)")
if v then column.IsEnabled = tonumber(v); settings.SaveFlags = bor(settings.SaveFlags, 4 --[[ImGuiTableFlags.Hideable]]) end
v = take("Order=(%-?%d+)")
if v then column.DisplayOrder = tonumber(v); settings.SaveFlags = bor(settings.SaveFlags, 2 --[[ImGuiTableFlags.Reorderable]]) end
local c
v, c = take("Sort=(%-?%d+)(.)")
if v then column.SortOrder = tonumber(v); column.SortDirection = (c == "^") and 2 --[[ImGuiSortDirection.Descending]] or 1 --[[ImGuiSortDirection.Ascending]]; settings.SaveFlags = bor(settings.SaveFlags, 8 --[[ImGuiTableFlags.Sortable]]) end
v = take("ID=0x(%x+)")
if v then column.ID = tonumber(v, 16) end
end
end
local function TableSettingsHandler_WriteAll(ctx, handler, buf)
local g = ctx
for _, settings in g.SettingsTables:iter() do
if settings.ID ~= 0 then -- Skip ditched settings
-- TableSaveSettings() may clear some of those flags when we establish that the data can be stripped
-- (e.g. Order was unchanged)
local save_size = band(settings.SaveFlags, 1 --[[ImGuiTableFlags.Resizable]]) ~= 0
local save_visible = band(settings.SaveFlags, 4 --[[ImGuiTableFlags.Hideable]]) ~= 0
local save_order = band(settings.SaveFlags, 2 --[[ImGuiTableFlags.Reorderable]]) ~= 0
local save_sort = band(settings.SaveFlags, 8 --[[ImGuiTableFlags.Sortable]]) ~= 0
-- We need to save the [Table] entry even if all the bools are false, since this records a table with "default settings".
buf:appendf("[%s][0x%08X,%d]\n", handler.TypeName, settings.ID, settings.ColumnsCount)
if settings.RefScale ~= 0.0 then
buf:appendf("RefScale=%g\n", settings.RefScale)
end
for column_n = 0, settings.ColumnsCount - 1 do
local column = settings.Columns[column_n]
-- "Column 0 UserID=0x42AD2D21 Width=100 Visible=1 Order=0 Sort=0v"
local save_column = save_size or save_visible or save_order or (save_sort and column.SortOrder ~= -1)
if save_column then
buf:appendf("Column %-2d", column_n)
if save_size and column.IsStretch ~= 0 then buf:appendf(" Weight=%.4f", column.WidthOrWeight) end
if save_size and column.IsStretch == 0 then buf:appendf(" Width=%d", ImTrunc(column.WidthOrWeight)) end
if save_visible then buf:appendf(" Visible=%d", column.IsEnabled) end
if save_order then buf:appendf(" Order=%d", column.DisplayOrder) end
if save_sort and column.SortOrder ~= -1 then buf:appendf(" Sort=%d%s", column.SortOrder, (column.SortDirection == 1 --[[ImGuiSortDirection.Ascending]]) and "v" or "^") end
if column.ID ~= 0 then buf:appendf(" ID=0x%08X", column.ID) end
buf:append("\n")
end
end
if g.IO.ConfigIniSettingsSaveLastUsedDate and settings.LastUsedDate ~= 0 then
buf:appendf("LastUsed=%08d\n", settings.LastUsedDate)
end
buf:append("\n")
end
end
end
function ImGui.TableSettingsAddSettingsHandler()
local ini_handler = ImGuiSettingsHandler()
ini_handler.TypeName = "Table"
ini_handler.TypeHash = ImHashStr("Table")
ini_handler.ClearAllFn = TableSettingsHandler_ClearAll
ini_handler.ReadOpenFn = TableSettingsHandler_ReadOpen
ini_handler.ReadLineFn = TableSettingsHandler_ReadLine
ini_handler.ApplyAllFn = TableSettingsHandler_ApplyAll
ini_handler.WriteAllFn = TableSettingsHandler_WriteAll
ini_handler.CleanupFn = TableSettingsHandler_Cleanup
ImGui.AddSettingsHandler(ini_handler)
end
-------------------------------------------------------------------------------
-- [SECTION] Tables: Garbage Collection
-------------------------------------------------------------------------------
-- - TableRemove() [Internal]
-- - TableGcCompactTransientBuffers() [Internal]
-- - TableGcCompactSettings() [Internal]
-------------------------------------------------------------------------------
-- Remove Table data (currently only used by TestEngine)
function ImGui.TableRemove(table_)
--IMGUI_DEBUG_PRINT("TableRemove() id=0x%08X\n", table_.ID)
local g = GImGui
local table_idx = g.Tables:GetIndex(table_)
g.Tables:Remove(table_.ID, table_)
g.TablesLastTimeActive.Data[table_idx + 1] = -1.0
end
-- Free up/compact internal Table buffers for when it gets unused
-- Overloads: (ImGuiTable) / (ImGuiTableTempData)
function ImGui.TableGcCompactTransientBuffers(t)
if not t then return end
if t.TableIndex ~= nil then -- ImGuiTableTempData
t.AngledHeadersRequests:clear()
t.DrawSplitter:ClearFreeMemory()
t.LastTimeActive = -1.0
return
end
--IMGUI_DEBUG_PRINT("TableGcCompactTransientBuffers() id=0x%08X\n", t.ID)
local g = GImGui
local table_ = t
assert(table_.MemoryCompacted == false)
table_.SortSpecs.Specs = nil
table.clear(table_.SortSpecsMulti)
table_.IsSortSpecsDirty = true -- FIXME: In theory shouldn't have to leak into user performing a sort on resume.
table.clear(table_.ColumnsNames)
table_.MemoryCompacted = true
for n = 0, table_.ColumnsCount - 1 do
table_.Columns[n].NameOffset = -1
end
g.TablesLastTimeActive.Data[g.Tables:GetIndex(table_) + 1] = -1.0
end
-- Compact and remove unused or resize settings data (only TestEngine mark unused, but resizing table down can lead to compaction)
function ImGui.TableGcCompactSettings()
local g = GImGui
local has_dead = false
for _, settings in g.SettingsTables:iter() do
if settings.ID == 0 then has_dead = true end
end
if not has_dead then
return
end
local new_list = ImVector()
for _, settings in g.SettingsTables:iter() do
if settings.ID ~= 0 then new_list:push_back(settings) end
end
g.SettingsTables = new_list
ForEachTable(g, function(t) t.SettingsOffset = -1 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, 57344 --[[ImGuiTableFlags.SizingMask_]])
if sizing_policy == 8192 --[[ImGuiTableFlags.SizingFixedFit]] then return "FixedFit" end
if sizing_policy == 16384 --[[ImGuiTableFlags.SizingFixedSame]] then return "FixedSame" end
if sizing_policy == 24576 --[[ImGuiTableFlags.SizingStretchProp]] then return "StretchProp" end
if sizing_policy == 32768 --[[ImGuiTableFlags.SizingStretchSame]] then return "StretchSame" end
return "N/A"
end
-------------------------------------------------------------------------------
-- [SECTION] Columns, BeginColumns, EndColumns, etc.
-- (This is a legacy API, prefer using BeginTable/EndTable!)
-------------------------------------------------------------------------------
-- FIXME: sizing is lossy when columns width is very small (default width may turn negative etc.)
-------------------------------------------------------------------------------
-- - GetColumnIndex()
-- - GetColumnsCount()
-- - GetColumnOffset()
-- - GetColumnWidth()
-- - SetColumnOffset()
-- - SetColumnWidth()
-- - PushColumnClipRect() [Internal]
-- - PushColumnsBackground() [Internal]
-- - PopColumnsBackground() [Internal]
-- - FindOrCreateColumns() [Internal]
-- - GetColumnsID() [Internal]
-- - BeginColumns()
-- - NextColumn()
-- - EndColumns()
-- - Columns()
-------------------------------------------------------------------------------
-- (Lua: columns.Columns is an ImVector, column n lives in .Data[n + 1])
function ImGui.GetColumnIndex()
local window = ImGui.GetCurrentWindowRead()
return window.DC.CurrentColumns and window.DC.CurrentColumns.Current or 0
end
function ImGui.GetColumnsCount()
local window = ImGui.GetCurrentWindowRead()
return window.DC.CurrentColumns and window.DC.CurrentColumns.Count or 1
end
function ImGui.GetColumnOffsetFromNorm(columns, offset_norm)
return offset_norm * (columns.OffMaxX - columns.OffMinX)
end
function ImGui.GetColumnNormFromOffset(columns, offset)
return offset / (columns.OffMaxX - columns.OffMinX)
end
local COLUMNS_HIT_RECT_HALF_THICKNESS = 4.0
local function GetDraggedColumnOffset(columns, column_index)
-- Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
-- window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
local g = GImGui
local window = g.CurrentWindow
assert(column_index > 0) -- We are not supposed to drag column 0.
assert(g.ActiveId == band(columns.ID + column_index, 0xFFFFFFFF))
local x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + ImTrunc(COLUMNS_HIT_RECT_HALF_THICKNESS * g.CurrentDpiScale) - window.Pos.x
x = ImMax(x, ImGui.GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing)
if band(columns.Flags, 4 --[[ImGuiOldColumnFlags.NoPreserveWidths]]) ~= 0 then
x = ImMin(x, ImGui.GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing)
end
return x
end
function ImGui.GetColumnOffset(column_index)
if column_index == nil then column_index = -1 end
local window = ImGui.GetCurrentWindowRead()
local columns = window.DC.CurrentColumns
if columns == nil then
return 0.0
end
if column_index < 0 then
column_index = columns.Current
end
assert(column_index < columns.Columns.Size)
local t = columns.Columns.Data[column_index + 1].OffsetNorm
local x_offset = ImLerp(columns.OffMinX, columns.OffMaxX, t)
return x_offset
end
local function GetColumnWidthEx(columns, column_index, before_resize)
if column_index < 0 then
column_index = columns.Current
end
local d = columns.Columns.Data
local offset_norm
if before_resize then
offset_norm = d[column_index + 2].OffsetNormBeforeResize - d[column_index + 1].OffsetNormBeforeResize
else
offset_norm = d[column_index + 2].OffsetNorm - d[column_index + 1].OffsetNorm
end
return ImGui.GetColumnOffsetFromNorm(columns, offset_norm)
end
function ImGui.GetColumnWidth(column_index)
if column_index == nil then column_index = -1 end
local g = GImGui
local window = g.CurrentWindow
local columns = window.DC.CurrentColumns
if columns == nil then
return ImGui.GetContentRegionAvail().x
end
if column_index < 0 then
column_index = columns.Current
end
local d = columns.Columns.Data
return ImGui.GetColumnOffsetFromNorm(columns, d[column_index + 2].OffsetNorm - d[column_index + 1].OffsetNorm)
end
function ImGui.SetColumnOffset(column_index, offset)
local g = GImGui
local window = g.CurrentWindow
local columns = window.DC.CurrentColumns
assert(columns ~= nil)
if column_index < 0 then
column_index = columns.Current
end
assert(column_index < columns.Columns.Size)
local preserve_width = band(columns.Flags, 4 --[[ImGuiOldColumnFlags.NoPreserveWidths]]) == 0 and (column_index < columns.Count - 1)
local width = preserve_width and GetColumnWidthEx(columns, column_index, columns.IsBeingResized) or 0.0
if band(columns.Flags, 8 --[[ImGuiOldColumnFlags.NoForceWithinWindow]]) == 0 then
offset = ImMin(offset, columns.OffMaxX - g.Style.ColumnsMinSpacing * (columns.Count - column_index))
end
columns.Columns.Data[column_index + 1].OffsetNorm = ImGui.GetColumnNormFromOffset(columns, offset - columns.OffMinX)
if preserve_width then
ImGui.SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width))
end
end
function ImGui.SetColumnWidth(column_index, width)
local window = ImGui.GetCurrentWindowRead()
local columns = window.DC.CurrentColumns
assert(columns ~= nil)
if column_index < 0 then
column_index = columns.Current
end
ImGui.SetColumnOffset(column_index + 1, ImGui.GetColumnOffset(column_index) + width)
end
function ImGui.PushColumnClipRect(column_index)
local window = ImGui.GetCurrentWindowRead()
local columns = window.DC.CurrentColumns
if column_index < 0 then
column_index = columns.Current
end
local column = columns.Columns.Data[column_index + 1]
ImGui.PushClipRect(column.ClipRect.Min, column.ClipRect.Max, false)
end
-- Get into the columns background draw command (which is generally the same draw command as before we called BeginColumns)
function ImGui.PushColumnsBackground()
local window = ImGui.GetCurrentWindowRead()
local columns = window.DC.CurrentColumns
if columns.Count == 1 then
return
end
-- Optimization: avoid SetCurrentChannel() + PushClipRect()
ImRect_Copy(columns.HostBackupClipRect, window.ClipRect)
ImGui.SetWindowClipRectBeforeSetChannel(window, columns.HostInitialClipRect)
columns.Splitter:SetCurrentChannel(window.DrawList, 0)
end
function ImGui.PopColumnsBackground()
local window = ImGui.GetCurrentWindowRead()
local columns = window.DC.CurrentColumns
if columns.Count == 1 then
return
end
-- Optimization: avoid PopClipRect() + SetCurrentChannel()
ImGui.SetWindowClipRectBeforeSetChannel(window, columns.HostBackupClipRect)
columns.Splitter:SetCurrentChannel(window.DrawList, columns.Current + 1)
end
function ImGui.FindOrCreateColumns(window, id)
-- We have few columns per window so for now we don't need bother much with turning this into a faster lookup.
for _, c in window.ColumnsStorage:iter() do
if c.ID == id then
return c
end
end
local columns = ImGuiOldColumns()
window.ColumnsStorage:push_back(columns)
columns.ID = id
return columns
end
function ImGui.GetColumnsID(str_id, columns_count)
local window = ImGui.GetCurrentWindow()
-- Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
-- In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
ImGui.PushID(0x11223347 + (str_id and 0 or columns_count))
local id = window:GetID(str_id or "columns")
ImGui.PopID()
return id
end
function ImGui.BeginColumns(str_id, columns_count, flags)
if flags == nil then flags = 0 end
local g = GImGui
local window = ImGui.GetCurrentWindow()
assert(columns_count >= 1)
assert(window.DC.CurrentColumns == nil) -- Nested columns are currently not supported
-- Acquire storage for the columns set
local id = ImGui.GetColumnsID(str_id, columns_count)
local columns = ImGui.FindOrCreateColumns(window, id)
assert(columns.ID == id)
columns.Current = 0
columns.Count = columns_count
columns.Flags = flags
window.DC.CurrentColumns = columns
window.DC.NavIsScrollPushableX = false -- Shortcut for NavUpdateCurrentWindowIsScrollPushableX();
columns.HostCursorPosY = window.DC.CursorPos.y
columns.HostCursorMaxPosX = window.DC.CursorMaxPos.x
ImRect_Copy(columns.HostInitialClipRect, window.ClipRect)
ImRect_Copy(columns.HostBackupParentWorkRect, window.ParentWorkRect)
ImRect_Copy(window.ParentWorkRect, window.WorkRect)
-- Set state for first column
-- We aim so that the right-most column will have the same clipping width as other after being clipped by parent ClipRect
local column_padding = g.Style.ItemSpacing.x
local half_clip_extend_x = ImTrunc(ImMax(window.WindowPadding.x * 0.5, window.WindowBorderSize))
local max_1 = window.WorkRect.Max.x + column_padding - ImMax(column_padding - window.WindowPadding.x, 0.0)
local max_2 = window.WorkRect.Max.x + half_clip_extend_x
columns.OffMinX = window.DC.Indent.x - column_padding + ImMax(column_padding - window.WindowPadding.x, 0.0)
columns.OffMaxX = ImMax(ImMin(max_1, max_2) - window.Pos.x, columns.OffMinX + 1.0)
columns.LineMinY = window.DC.CursorPos.y; columns.LineMaxY = window.DC.CursorPos.y
-- Clear data if columns count changed
if columns.Columns.Size ~= 0 and columns.Columns.Size ~= columns_count + 1 then
columns.Columns:resize(0)
end
-- Initialize default widths
columns.IsFirstFrame = (columns.Columns.Size == 0)
if columns.Columns.Size == 0 then
for n = 0, columns_count do
local column = ImGuiOldColumnData()
column.OffsetNorm = n / columns_count
columns.Columns:push_back(column)
end
end
for n = 0, columns_count - 1 do
-- Compute clipping rectangle
local column = columns.Columns.Data[n + 1]
local clip_x1 = IM_ROUND(window.Pos.x + ImGui.GetColumnOffset(n))
local clip_x2 = IM_ROUND(window.Pos.x + ImGui.GetColumnOffset(n + 1) - 1.0)
column.ClipRect = ImRect(clip_x1, -3.4028234663853e+38 --[[FLT_MAX]], clip_x2, 3.4028234663853e+38 --[[FLT_MAX]])
column.ClipRect:ClipWithFull(window.ClipRect)
end
if columns.Count > 1 then
columns.Splitter:Split(window.DrawList, 1 + columns.Count)
columns.Splitter:SetCurrentChannel(window.DrawList, 1)
ImGui.PushColumnClipRect(0)
end
-- We don't generally store Indent.x inside ColumnsOffset because it may be manipulated by the user.
local offset_0 = ImGui.GetColumnOffset(columns.Current)
local offset_1 = ImGui.GetColumnOffset(columns.Current + 1)
local width = offset_1 - offset_0
ImGui.PushItemWidth(width * 0.65)
window.DC.ColumnsOffset.x = ImMax(column_padding - window.WindowPadding.x, 0.0)
window.DC.CursorPos.x = IM_TRUNC(window.Pos.x + window.DC.Indent.x + window.DC.ColumnsOffset.x)
window.WorkRect.Max.x = window.Pos.x + offset_1 - column_padding
window.WorkRect.Max.y = window.ContentRegionRect.Max.y
end
function ImGui.NextColumn()
local window = ImGui.GetCurrentWindow()
if window.SkipItems or window.DC.CurrentColumns == nil then
return
end
local g = GImGui
local columns = window.DC.CurrentColumns
if columns.Count == 1 then
window.DC.CursorPos.x = IM_TRUNC(window.Pos.x + window.DC.Indent.x + window.DC.ColumnsOffset.x)
assert(columns.Current == 0)
return
end
-- Next column
columns.Current = columns.Current + 1
if columns.Current == columns.Count then
columns.Current = 0
end
ImGui.PopItemWidth()
-- Optimization: avoid PopClipRect() + SetCurrentChannel() + PushClipRect()
-- (which would needlessly attempt to update commands in the wrong channel, then pop or overwrite them),
local column = columns.Columns.Data[columns.Current + 1]
ImGui.SetWindowClipRectBeforeSetChannel(window, column.ClipRect)
columns.Splitter:SetCurrentChannel(window.DrawList, columns.Current + 1)
local column_padding = g.Style.ItemSpacing.x
columns.LineMaxY = ImMax(columns.LineMaxY, window.DC.CursorPos.y)
if columns.Current > 0 then
-- Columns 1+ ignore IndentX (by canceling it out)
-- FIXME-COLUMNS: Unnecessary, could be locked?
window.DC.ColumnsOffset.x = ImGui.GetColumnOffset(columns.Current) - window.DC.Indent.x + column_padding
else
-- New row/line: column 0 honor IndentX.
window.DC.ColumnsOffset.x = ImMax(column_padding - window.WindowPadding.x, 0.0)
window.DC.IsSameLine = false
columns.LineMinY = columns.LineMaxY
end
window.DC.CursorPos.x = IM_TRUNC(window.Pos.x + window.DC.Indent.x + window.DC.ColumnsOffset.x)
window.DC.CursorPos.y = columns.LineMinY
window.DC.CurrLineSize.x = 0.0; window.DC.CurrLineSize.y = 0.0
window.DC.CurrLineTextBaseOffset = 0.0
-- FIXME-COLUMNS: Share code with BeginColumns() - move code on columns setup.
local offset_0 = ImGui.GetColumnOffset(columns.Current)
local offset_1 = ImGui.GetColumnOffset(columns.Current + 1)
local width = offset_1 - offset_0
ImGui.PushItemWidth(width * 0.65)
window.WorkRect.Max.x = window.Pos.x + offset_1 - column_padding
end
function ImGui.EndColumns()
local g = GImGui
local window = ImGui.GetCurrentWindow()
local columns = window.DC.CurrentColumns
assert(columns ~= nil)
ImGui.PopItemWidth()
if columns.Count > 1 then
ImGui.PopClipRect()
columns.Splitter:Merge(window.DrawList)
end
local flags = columns.Flags
columns.LineMaxY = ImMax(columns.LineMaxY, window.DC.CursorPos.y)
window.DC.CursorPos.y = columns.LineMaxY
if band(flags, 16 --[[ImGuiOldColumnFlags.GrowParentContentsSize]]) == 0 then
window.DC.CursorMaxPos.x = columns.HostCursorMaxPosX -- Restore cursor max pos, as columns don't grow parent
end
-- Draw columns borders and handle resize
-- The IsBeingResized flag ensure we preserve pre-resize columns width so back-and-forth are not lossy
local is_being_resized = false
if band(flags, 1 --[[ImGuiOldColumnFlags.NoBorder]]) == 0 and not window.SkipItems then
-- We clip Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.
local y1 = ImMax(columns.HostCursorPosY, window.ClipRect.Min.y)
local y2 = ImMin(window.DC.CursorPos.y, window.ClipRect.Max.y)
local dragging_column = -1
for n = 1, columns.Count - 1 do
local column = columns.Columns.Data[n + 1]
local x = window.Pos.x + ImGui.GetColumnOffset(n)
local column_id = band(columns.ID + n, 0xFFFFFFFF)
local column_hit_hw = ImTrunc(COLUMNS_HIT_RECT_HALF_THICKNESS * g.CurrentDpiScale)
local column_hit_rect = ImRect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2))
if ImGui.ItemAdd(column_hit_rect, column_id, nil, 2 --[[ImGuiItemFlags.NoNav]]) then
local hovered, held = false, false
if band(flags, 2 --[[ImGuiOldColumnFlags.NoResize]]) == 0 then
local _
_, hovered, held = ImGui.ButtonBehavior(column_hit_rect, column_id)
if hovered or held then
ImGui.SetMouseCursor(4 --[[ImGuiMouseCursor.ResizeEW]])
end
if held and band(column.Flags, 2 --[[ImGuiOldColumnFlags.NoResize]]) == 0 then
dragging_column = n
end
end
-- Draw column
local col = ImGui.GetColorU32(held and 30 --[[ImGuiCol.SeparatorActive]] or (hovered and 29 --[[ImGuiCol.SeparatorHovered]] or 28 --[[ImGuiCol.Separator]]))
local xi = IM_TRUNC(x)
window.DrawList:AddLineV(xi, y1 + 1.0, y2, col)
end
end
-- Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame.
if dragging_column ~= -1 then
if not columns.IsBeingResized then
for n = 0, columns.Count do
local c = columns.Columns.Data[n + 1]
c.OffsetNormBeforeResize = c.OffsetNorm
end
end
columns.IsBeingResized = true; is_being_resized = true
local x = GetDraggedColumnOffset(columns, dragging_column)
ImGui.SetColumnOffset(dragging_column, x)
end
end
columns.IsBeingResized = is_being_resized
ImRect_Copy(window.WorkRect, window.ParentWorkRect)
ImRect_Copy(window.ParentWorkRect, columns.HostBackupParentWorkRect)
window.DC.CurrentColumns = nil
window.DC.ColumnsOffset.x = 0.0
window.DC.CursorPos.x = IM_TRUNC(window.Pos.x + window.DC.Indent.x + window.DC.ColumnsOffset.x)
ImGui.NavUpdateCurrentWindowIsScrollPushableX()
end
--- @param columns_count? int
--- @param id? string
--- @param borders? bool
function ImGui.Columns(columns_count, id, borders)
if columns_count == nil then columns_count = 1 end
if borders == nil then borders = true end
local window = ImGui.GetCurrentWindow()
assert(columns_count >= 1)
local flags = borders and 0 or 1 --[[ImGuiOldColumnFlags.NoBorder]]
--flags = bor(flags, ImGuiOldColumnFlags.NoPreserveWidths) -- NB: Legacy behavior
local columns = window.DC.CurrentColumns
if columns ~= nil and columns.Count == columns_count and columns.Flags == flags then
return
end
if columns ~= nil then
ImGui.EndColumns()
end
if columns_count ~= 1 then
ImGui.BeginColumns(id, columns_count, flags)
end
end
return true -- [Roblox] ModuleScripts must return exactly one value
end
__FILES['imgui_widgets.lua'] = function()
--- Dear ImGui WIP
-- (widgets code)
--
-- Index of this file:
--
-- [SECTION] Widgets: Text, etc.
-- [SECTION] Widgets: Value helpers
-- [SECTION] Widgets: Main (Button, Image, Checkbox, RadioButton, ProgressBar, Bullet, etc.)
-- [SECTION] Widgets: Low-level Layout helpers (Spacing, Dummy, NewLine, Separator, etc.)
-- [SECTION] Widgets: ComboBox
-- [SECTION] Data Type and Data Formatting Helpers
-- [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc.
-- [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc.
-- [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc.
-- [SECTION] Widgets: InputText, InputTextMultiline
-- [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc.
-- [SECTION] Widgets: TreeNode, CollapsingHeader, etc.
-- [SECTION] Widgets: Selectable
-- [SECTION] Widgets: ListBox
-- [SECTION] Widgets: PlotLines, PlotHistogram
-- [SECTION] Widgets: MenuItem, BeginMenu, EndMenu, etc.
-- [SECTION] Widgets: BeginTabBar, EndTabBar, etc.
-- [SECTION] Widgets: BeginTabItem, EndTabItem, etc.
--
--- @type ImGuiContext?
local GImGui
-- Sets local `GImGui` in this file(imgui_widgets.lua).
-- This is currently only used in main code `ImGui.SetCurrentContext()`
--- @param ctx ImGuiContext?
function ImGui._SetCurrentContext_Widgets(ctx)
GImGui = ctx
end
-------------------------------------------------------------------------------
-- Data
-------------------------------------------------------------------------------
-- Widgets
local DRAGDROP_HOLD_TO_OPEN_TIMER = 0.70 -- Time for drag-hold to activate items accepting the ImGuiButtonFlags.PressedOnDragDropHold button behavior.
local DRAG_MOUSE_THRESHOLD_FACTOR = 0.50 -- Multiplier for the default value of io.MouseDragThreshold to make DragFloat/DragInt react faster to mouse drags.
local IM_S8_MIN = -128
local IM_S8_MAX = 127
local IM_U8_MIN = 0
local IM_U8_MAX = 0xFF
local IM_S16_MIN = -32768
local IM_S16_MAX = 32767
local IM_U16_MIN = 0
local IM_U16_MAX = 0xFFFF
local IM_S32_MIN = INT_MIN -- (-2147483647 - 1), (0x80000000);
local IM_S32_MAX = INT_MAX -- (2147483647), (0x7FFFFFFF)
local IM_U32_MIN = 0
local IM_U32_MAX = UINT_MAX -- (0xFFFFFFFF)
-------------------------------------------------------------------------------
-- [SECTION] Widgets: Text, etc.
-------------------------------------------------------------------------------
-- - TextEx() [Internal]
-- - TextUnformatted()
-- - Text()
-- - TextV()
-- - TextColored()
-- - TextDisabled()
-- - TextWrapped()
-- - TextAligned()
-- - LabelText()
-- - BulletText()
-------------------------------------------------------------------------------
--- @param text string
--- @param text_end? int
--- @param flags? ImGuiTextFlags
local TextEx_bb = ImRect() -- [Luau] scratch, never escapes TextEx()
local LAYOUT_ZERO_SIZE = ImVec2(0, 0) -- [Luau] shared read-only: ItemSize()/CalcItemSize() only read it
@native
function ImGui.TextEx(text, text_end, flags)
if not flags then flags = 0 end
local g = GImGui
local window = g.CurrentWindow
if window.SkipItems then
return
end
-- Accept null ranges
if not text or text == "" then
text = ""
text_end = 1
end
-- Calculate length
if text_end == nil then
text_end = #text + 1
end
local dc = window.DC
local wrap_pos_x = dc.TextWrapPos
local wrap_enabled = (wrap_pos_x >= 0.0)
if (text_end - 1 <= 2000) or wrap_enabled then
-- Common case
-- [Luau] scalar text_pos, scratch bb (ItemAdd() copies it, RenderTextWrapped() only reads it)
local text_pos_x, text_pos_y = dc.CursorPos.x, dc.CursorPos.y + dc.CurrLineTextBaseOffset
local wrap_width = wrap_enabled and ImGui.CalcWrapWidthForPos(dc.CursorPos, wrap_pos_x) or 0.0
local text_size = ImGui.CalcTextSizeEx(text, 1, text_end, false, wrap_width)
local bb = TextEx_bb
local bb_min, bb_max = bb.Min, bb.Max
bb_min.x = text_pos_x; bb_min.y = text_pos_y
bb_max.x = text_pos_x + text_size.x; bb_max.y = text_pos_y + text_size.y
ImGui.ItemSize(text_size, 0.0)
if not ImGui.ItemAdd(bb, 0) then
return
end
-- Render (we don't hide text after ## in this end-user function)
ImGui.RenderTextWrapped(bb_min, text, text_end, wrap_width)
else
local text_pos = ImVec2(dc.CursorPos.x, dc.CursorPos.y + dc.CurrLineTextBaseOffset)
-- Long text!
-- Perform manual coarse clipping to optimize for long multi-line text
-- - From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled.
-- - We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line.
local line = 1
local line_height = ImGui.GetTextLineHeight()
local text_size = ImVec2(0, 0)
-- Lines to skip (can't skip when logging text)
local pos = ImVec2(text_pos.x, text_pos.y)
if not g.LogEnabled then
local lines_skippable = ImFloor((window.ClipRect.Min.y - text_pos.y) / line_height)
if lines_skippable > 0 then
local lines_skipped = 0
while line < text_end and lines_skipped < lines_skippable do
local line_end = ImMemchr(text, "\n", line)
if not line_end then
line_end = text_end
end
if bit32.band(flags, 1 --[[ImGuiTextFlags.NoWidthForLargeClippedText]]) == 0 then
local line_size = ImGui.CalcTextSizeEx(text, line, line_end)
text_size.x = ImMax(text_size.x, line_size.x)
end
line = line_end + 1
lines_skipped = lines_skipped + 1
end
pos.y = pos.y + lines_skipped * line_height
end
end
-- Lines to render
if line < text_end then
local line_rect = ImRect(pos, pos + ImVec2(3.4028234663853e+38 --[[FLT_MAX]], line_height))
while line < text_end do
if ImGui.IsClippedEx(line_rect, 0) then
break
end
local line_end = ImMemchr(text, "\n", line)
if not line_end then
line_end = text_end
end
local line_size = ImGui.CalcTextSizeEx(text, line, line_end)
text_size.x = ImMax(text_size.x, line_size.x)
ImGui.RenderText(pos, text, line, nil, false)
line = line_end + 1
line_rect.Min.y = line_rect.Min.y + line_height
line_rect.Max.y = line_rect.Max.y + line_height
pos.y = pos.y + line_height
end
-- Count remaining lines
local lines_skipped = 0
while line < text_end do
local line_end = ImMemchr(text, "\n", line)
if not line_end then
line_end = text_end
end
if bit32.band(flags, 1 --[[ImGuiTextFlags.NoWidthForLargeClippedText]]) == 0 then
local line_size = ImGui.CalcTextSizeEx(text, line, line_end)
text_size.x = ImMax(text_size.x, line_size.x)
end
line = line_end + 1
lines_skipped = lines_skipped + 1
end
pos.y = pos.y + lines_skipped * line_height
end
text_size.y = pos.y - text_pos.y
local bb = ImRect(text_pos, text_pos + text_size)
ImGui.ItemSize(text_size, 0.0)
ImGui.ItemAdd(bb, 0)
end
end
--- @param text string
--- @param text_end? int
function ImGui.TextUnformatted(text, text_end)
ImGui.TextEx(text, text_end, 1 --[[ImGuiTextFlags.NoWidthForLargeClippedText]])
end
--- @param fmt string
--- @param ... any
@native
function ImGui.TextV(fmt, ...)
local window = GImGui.CurrentWindow -- [Luau] inline GetCurrentWindow()
window.WriteAccessed = true
if window.SkipItems then
return
end
local text = ImFormatString(fmt, ...)
ImGui.TextEx(text, nil, 1 --[[ImGuiTextFlags.NoWidthForLargeClippedText]])
end
--- @param fmt string
--- @param ... any
@native
function ImGui.Text(fmt, ...)
if select('#', ...) > 0 then
ImGui.TextV(fmt, ...)
else
ImGui.TextEx(fmt)
end
end
--- @param col ImVec4
--- @param fmt string
--- @param ... any
function ImGui.TextColored(col, fmt, ...)
ImGui.PushStyleColor(0 --[[ImGuiCol.Text]], col)
ImGui.TextV(fmt, ...)
ImGui.PopStyleColor()
end
--- @param fmt string
--- @param ... any
function ImGui.TextDisabled(fmt, ...)
local g = GImGui
ImGui.PushStyleColor(0 --[[ImGuiCol.Text]], g.Style.Colors[1 --[[ImGuiCol.TextDisabled]]])
ImGui.TextV(fmt, ...)
ImGui.PopStyleColor()
end
--- @param fmt string
--- @param ... any
function ImGui.TextWrapped(fmt, ...)
local g = GImGui
local need_backup = (g.CurrentWindow.DC.TextWrapPos < 0.0) -- Keep existing wrap position if one is already set
if need_backup then
ImGui.PushTextWrapPos(0.0)
end
ImGui.TextV(fmt, ...)
if need_backup then
ImGui.PopTextWrapPos()
end
end
-- align_x: 0.0f = left, 0.5f = center, 1.0f = right.
-- size_x < 0.0f (e.g. -FLT_MIN): shortcut for GetContentRegionAvail().x
-- FIXME-WIP: Works but API is likely to be reworked. This is designed for 1 item on the line. (#7024, #9455)
--- @param align_x float
--- @param size_x float
--- @param fmt string
--- @param ... any
function ImGui.TextAligned(align_x, size_x, fmt, ...)
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return
end
local text = ImFormatString(fmt, ...)
local text_end = #text + 1
local text_size = ImGui.CalcTextSize(text, text_end)
size_x = ImGui._CalcItemSizeXY(size_x, 0.0, 0.0, text_size.y)
local pos = ImVec2(window.DC.CursorPos.x, window.DC.CursorPos.y + window.DC.CurrLineTextBaseOffset)
local pos_max = ImVec2(pos.x + size_x, window.ClipRect.Max.y)
local size = ImVec2(ImMin(size_x, text_size.x), text_size.y)
window.DC.CursorMaxPos.x = ImMax(window.DC.CursorMaxPos.x, pos.x + text_size.x)
window.DC.IdealMaxPos.x = ImMax(window.DC.IdealMaxPos.x, pos.x + text_size.x)
if align_x > 0.0 and text_size.x < size_x then
pos.x = pos.x + ImTrunc((size_x - text_size.x) * align_x)
end
ImGui.RenderTextEllipsis(window.DrawList, pos, pos_max, pos_max.x, text, text_end, text_size)
local backup_max_pos = ImVec2()
ImVec2_Copy(backup_max_pos, window.DC.CursorMaxPos)
ImGui.ItemSize(size)
ImGui.ItemAdd(ImRect(pos, pos + size), 0)
window.DC.CursorMaxPos.x = backup_max_pos.x -- Cancel out extending content to 'size' to use 'text_size' allowing auto-resize to function with align_x > 0.0f
if size_x < text_size.x and ImGui.IsItemHovered(bit32.bor(2048 --[[ImGuiHoveredFlags.NoNavOverride]], 1024 --[[ImGuiHoveredFlags.AllowWhenDisabled]], 4096 --[[ImGuiHoveredFlags.ForTooltip]])) then
ImGui.SetTooltip("%.*s", text_end - 1, text)
end
end
-- Add a label+text combo aligned to other label+value widgets
--- @param label string
--- @param fmt string
--- @param ... any
function ImGui.LabelText(label, fmt, ...)
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return
end
local g = GImGui
local style = g.Style
local w = ImGui.CalcItemWidth()
local value_text = ImFormatString(fmt, ...)
local value_text_end = #value_text + 1
local value_size = ImGui.CalcTextSize(value_text, value_text_end, false)
local label_end = ImGui.FindRenderedTextEnd(label)
local label_size = ImGui.CalcTextSize(label, label_end, false)
local pos = ImVec2()
ImVec2_Copy(pos, window.DC.CursorPos)
local value_bb = ImRect(pos, pos + ImVec2(w, value_size.y + style.FramePadding.y * 2))
local total_bb = ImRect(pos, pos + ImVec2(w + ((label_size.x > 0.0) and (style.ItemInnerSpacing.x + label_size.x) or 0.0), ImMax(value_size.y, label_size.y) + style.FramePadding.y * 2))
ImGui.ItemSize(total_bb, style.FramePadding.y)
if not ImGui.ItemAdd(total_bb, 0) then
return
end
-- Render
ImGui.RenderTextClipped(value_bb.Min + style.FramePadding, value_bb.Max, value_text, value_text_end, value_size, ImVec2(0.0, 0.0))
if label_size.x > 0.0 then
ImGui.RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label, 1, label_end, false)
end
end
-- Text with a little bullet aligned to the typical tree node.
--- @param fmt string
--- @param ... any
function ImGui.BulletText(fmt, ...)
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return
end
local g = GImGui
local style = g.Style
local text = ImFormatString(fmt, ...)
local text_end = #text + 1
local label_size = ImGui.CalcTextSize(text, text_end, false)
local total_size = ImVec2(g.FontSize + ((label_size.x > 0.0) and (label_size.x + style.FramePadding.x * 2) or 0.0), label_size.y) -- Empty text doesn't add padding
-- [Luau] scalar pos, no temporary ImVec2
local pos_x, pos_y = window.DC.CursorPos.x, window.DC.CursorPos.y + window.DC.CurrLineTextBaseOffset
ImGui.ItemSize(total_size, 0.0)
local bb = ImRect(pos_x, pos_y, pos_x + total_size.x, pos_y + total_size.y)
if not ImGui.ItemAdd(bb, 0) then
return
end
-- Render
local text_col = ImGui.GetColorU32(0 --[[ImGuiCol.Text]])
ImGui.RenderBullet(window.DrawList, ImVec2(bb.Min.x + (style.FramePadding.x + g.FontSize * 0.5), bb.Min.y + g.FontSize * 0.5), text_col)
ImGui.RenderText(ImVec2(bb.Min.x + (g.FontSize + style.FramePadding.x * 2), bb.Min.y + 0.0), text, 1, text_end, false)
end
-- va_list variants: identical to the varargs versions in Lua
ImGui.TextColoredV = ImGui.TextColored
ImGui.TextDisabledV = ImGui.TextDisabled
ImGui.TextWrappedV = ImGui.TextWrapped
ImGui.TextAlignedV = ImGui.TextAligned
ImGui.LabelTextV = ImGui.LabelText
ImGui.BulletTextV = ImGui.BulletText
-------------------------------------------------------------------------------
-- [SECTION] Widgets: Value helpers
-- Those is not very useful, legacy API.
-------------------------------------------------------------------------------
-- - Value()
-------------------------------------------------------------------------------
-- Value(prefix, b) / Value(prefix, int) / Value(prefix, float, float_format)
-- Lua can't tell int from float: integers without float_format print as %d, otherwise %.3f
function ImGui.Value(prefix, v, float_format)
if type(v) == "boolean" then
ImGui.Text("%s: %s", prefix, v and "true" or "false")
elseif float_format then
ImGui.Text("%s: " .. float_format, prefix, v)
elseif math.floor(v) == v then
ImGui.Text("%s: %d", prefix, v)
else
ImGui.Text("%s: %.3f", prefix, v)
end
end
-------------------------------------------------------------------------------
-- [SECTION] Widgets: Main
-------------------------------------------------------------------------------
-- - ButtonBehavior() [Internal]
-- - Button()
-- - SmallButton()
-- - InvisibleButton()
-- - ArrowButton()
-- - CloseButton() [Internal]
-- - CollapseButton() [Internal]
-- - GetWindowScrollbarID() [Internal]
-- - GetWindowScrollbarRect() [Internal]
-- - Scrollbar() [Internal]
-- - ScrollbarEx() [Internal]
-- - Image()
-- - ImageButton()
-- - Checkbox()
-- - CheckboxFlagsT() [Internal]
-- - CheckboxFlags()
-- - RadioButton()
-- - ProgressBar()
-- - Bullet()
-- - Hyperlink()
-------------------------------------------------------------------------------
-- The ButtonBehavior() function is key to many interactions and used by many/most widgets.
-- Because we handle so many cases (keyboard/gamepad navigation, drag and drop) and many specific behavior (via ImGuiButtonFlags),
-- this code is a little complex.
-- By far the most common path is interacting with the Mouse using the default ImGuiButtonFlags.PressedOnClickRelease button behavior.
-- See the series of events below and the corresponding state reported by dear imgui:
-------------------------------------------------------------------------------
-- with PressedOnClickRelease: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked()
-- Frame N+0 (mouse is outside bb) - - - - - -
-- Frame N+1 (mouse moves inside bb) - true - - - -
-- Frame N+2 (mouse button is down) - true true true - true
-- Frame N+3 (mouse button is down) - true true - - -
-- Frame N+4 (mouse moves outside bb) - - true - - -
-- Frame N+5 (mouse moves inside bb) - true true - - -
-- Frame N+6 (mouse button is released) true true - - true -
-- Frame N+7 (mouse button is released) - true - - - -
-- Frame N+8 (mouse moves outside bb) - - - - - -
-------------------------------------------------------------------------------
-- with PressedOnClick: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked()
-- Frame N+2 (mouse button is down) true true true true - true
-- Frame N+3 (mouse button is down) - true true - - -
-- Frame N+6 (mouse button is released) - true - - true -
-- Frame N+7 (mouse button is released) - true - - - -
-------------------------------------------------------------------------------
-- with PressedOnRelease: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked()
-- Frame N+2 (mouse button is down) - true - - - true
-- Frame N+3 (mouse button is down) - true - - - -
-- Frame N+6 (mouse button is released) true true - - - -
-- Frame N+7 (mouse button is released) - true - - - -
-------------------------------------------------------------------------------
-- with PressedOnDoubleClick: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked()
-- Frame N+0 (mouse button is down) - true - - - true
-- Frame N+1 (mouse button is down) - true - - - -
-- Frame N+2 (mouse button is released) - true - - - -
-- Frame N+3 (mouse button is released) - true - - - -
-- Frame N+4 (mouse button is down) true true true true - true
-- Frame N+5 (mouse button is down) - true true - - -
-- Frame N+6 (mouse button is released) - true - - true -
-- Frame N+7 (mouse button is released) - true - - - -
-------------------------------------------------------------------------------
-- Note that some combinations are supported,
-- - PressedOnDragDropHold can generally be associated with any flag.
-- - PressedOnDoubleClick can be associated by PressedOnClickRelease/PressedOnRelease, in which case the second release event won't be reported.
-------------------------------------------------------------------------------
-- The behavior of the return-value changes when ImGuiItemFlags.ButtonRepeat is set:
-- Repeat+ Repeat+ Repeat+ Repeat+
-- PressedOnClickRelease PressedOnClick PressedOnRelease PressedOnDoubleClick
-------------------------------------------------------------------------------
-- Frame N+0 (mouse button is down) - true - true
-- ... - - - -
-- Frame N + RepeatDelay true true - true
-- ... - - - -
-- Frame N + RepeatDelay + RepeatRate*N true true - true
-------------------------------------------------------------------------------
-- - FIXME: For refactor we could output flags, incl mouse hovered vs nav keyboard vs nav triggered etc.
-- And better standardize how widgets use 'GetColor32((held && hovered) ? ... : hovered ? ...)' vs 'GetColor32(held ? ... : hovered ? ...);'
-- For mouse feedback we typically prefer the 'held && hovered' test, but for nav feedback not always. Outputting hovered=true on Activation may be misleading.
-- - Since v1.91.2 (Sept 2024) we included io.ConfigDebugHighlightIdConflicts feature.
-- One idiom which was previously valid which will now emit a warning is when using multiple overlaid ButtonBehavior()
-- with same ID and different MouseButton (see #8030). You can fix it by:
-- (1) switching to use a single ButtonBehavior() with multiple _MouseButton flags.
-- or (2) surrounding those calls with PushItemFlag(ImGuiItemFlags.AllowDuplicateId, true); ... PopItemFlag()
-- Returns (pressed, hovered, held).
--- @param bb ImRect
--- @param id ImGuiID
--- @param flags? ImGuiButtonFlags
@native
function ImGui.ButtonBehavior(bb, id, flags)
if flags == nil then flags = 0 end
local g = GImGui
local window = g.CurrentWindow
-- Default behavior inherited from item flags
-- Note that _both_ ButtonFlags and ItemFlags are valid sources, so copy one into the item_flags and only check that.
local item_flags = (g.LastItemData.ID == id) and g.LastItemData.ItemFlags or g.CurrentItemFlags
if bit32.band(flags, 4096 --[[ImGuiButtonFlags.AllowOverlap]]) ~= 0 then
item_flags = bit32.bor(item_flags, 16384 --[[ImGuiItemFlags.AllowOverlap]])
end
if bit32.band(item_flags, 131072 --[[ImGuiItemFlags.NoFocus]]) ~= 0 then
flags = bit32.bor(flags, 4194304 --[[ImGuiButtonFlags.NoFocus]], 262144 --[[ImGuiButtonFlags.NoNavFocus]])
end
-- Default only reacts to left mouse button
if bit32.band(flags, 7 --[[ImGuiButtonFlags.MouseButtonMask_]]) == 0 then
flags = bit32.bor(flags, 1 --[[ImGuiButtonFlags.MouseButtonLeft]])
end
-- Default behavior requires click + release inside bounding box
if bit32.band(flags, 1008 --[[ImGuiButtonFlags.PressedOnMask_]]) == 0 then
flags = bit32.bor(flags, (bit32.band(item_flags, 8 --[[ImGuiItemFlags.ButtonRepeat]]) ~= 0) and 16 --[[ImGuiButtonFlags.PressedOnClick]] or 32 --[[ImGuiButtonFlags.PressedOnDefault_]])
end
local backup_hovered_window = g.HoveredWindow
local flatten_hovered_children = (bit32.band(flags, 2048 --[[ImGuiButtonFlags.FlattenChildren]]) ~= 0) and g.HoveredWindow and g.HoveredWindow.RootWindowDockTree == window.RootWindowDockTree
if flatten_hovered_children then
g.HoveredWindow = window
end
local pressed = false
local hovered = ImGui.ItemHoverable(bb, id, item_flags)
-- Special mode for Drag and Drop used by openables (tree nodes, tabs etc.)
-- where holding the button pressed for a long time while drag a payload item triggers the button.
if g.DragDropActive then
if (bit32.band(flags, 512 --[[ImGuiButtonFlags.PressedOnDragDropHold]]) ~= 0) and (bit32.band(g.DragDropSourceFlags, 4 --[[ImGuiDragDropFlags.SourceNoHoldToOpenOthers]]) == 0) and ImGui.IsItemHovered(128 --[[ImGuiHoveredFlags.AllowWhenBlockedByActiveItem]]) then
hovered = true
ImGui.SetHoveredID(id)
if (g.HoveredIdTimer - g.IO.DeltaTime <= DRAGDROP_HOLD_TO_OPEN_TIMER) and (g.HoveredIdTimer >= DRAGDROP_HOLD_TO_OPEN_TIMER) then
pressed = true
g.DragDropHoldJustPressedId = id
ImGui.FocusWindow(window)
end
end
if (g.DragDropAcceptIdPrev == id) and (bit32.band(g.DragDropAcceptFlagsPrev, 8192 --[[ImGuiDragDropFlags.AcceptDrawAsHovered]]) ~= 0) then
hovered = true
end
end
if (flatten_hovered_children) then
g.HoveredWindow = backup_hovered_window
end
-- Mouse handling
local test_owner_id = (bit32.band(flags, 2097152 --[[ImGuiButtonFlags.NoTestKeyOwner]]) ~= 0) and 0 --[[ImGuiKeyOwner_Any]] or id
if hovered then
assert(id ~= 0) -- Lazily check inside rare path.
-- Poll mouse buttons
-- - 'mouse_button_clicked' is generally carried into ActiveIdMouseButton when setting ActiveId.
-- - Technically we only need some values in one code path, but since this is gated by hovered test this is fine.
local mouse_button_clicked = -1
local mouse_button_released = -1
for button = 0, 2 do
if bit32.band(flags, bit32.lshift(1 --[[ImGuiButtonFlags.MouseButtonLeft]], button)) ~= 0 then -- Handle ImGuiButtonFlags.MouseButtonRight and ImGuiButtonFlags.MouseButtonMiddle here.
if (ImGui.IsMouseClickedEx(button, 0 --[[ImGuiInputFlags.None]], test_owner_id) and mouse_button_clicked == -1) then mouse_button_clicked = button end
if (ImGui.IsMouseReleased(button, test_owner_id) and mouse_button_released == -1) then mouse_button_released = button end
end
end
-- Process initial action
local mods_ok = (bit32.band(flags, 65536 --[[ImGuiButtonFlags.NoKeyModsAllowed]]) == 0) or (not g.IO.KeyCtrl and not g.IO.KeyShift and not g.IO.KeyAlt)
if mods_ok then
if mouse_button_clicked ~= -1 and g.ActiveId ~= id then
--- @cast mouse_button_clicked ImGuiMouseButton
if bit32.band(flags, 1048576 --[[ImGuiButtonFlags.NoSetKeyOwner]]) == 0 then
ImGui.SetKeyOwner(ImGui.MouseButtonToKey(mouse_button_clicked), id)
end
if bit32.band(flags, bit32.bor(32 --[[ImGuiButtonFlags.PressedOnClickRelease]], 64 --[[ImGuiButtonFlags.PressedOnClickReleaseAnywhere]])) ~= 0 then
ImGui.SetActiveID(id, window)
g.ActiveIdMouseButton = mouse_button_clicked
if bit32.band(flags, 262144 --[[ImGuiButtonFlags.NoNavFocus]]) == 0 then
ImGui.SetFocusID(id, window)
ImGui.FocusWindow(window)
elseif bit32.band(flags, 4194304 --[[ImGuiButtonFlags.NoFocus]]) == 0 then
ImGui.FocusWindow(window, 1 --[[ImGuiFocusRequestFlags.RestoreFocusedChild]]) -- Still need to focus and bring to front, but try to avoid losing NavId when navigating a child
end
end
if (bit32.band(flags, 16 --[[ImGuiButtonFlags.PressedOnClick]]) ~= 0) or ((bit32.band(flags, 256 --[[ImGuiButtonFlags.PressedOnDoubleClick]]) ~= 0) and g.IO.MouseClickedCount[mouse_button_clicked] == 2) then
pressed = true
if bit32.band(flags, 131072 --[[ImGuiButtonFlags.NoHoldingActiveId]]) ~= 0 then
ImGui.ClearActiveID()
else
ImGui.SetActiveID(id, window) -- Hold on ID
end
g.ActiveIdMouseButton = mouse_button_clicked
if bit32.band(flags, 262144 --[[ImGuiButtonFlags.NoNavFocus]]) == 0 then
ImGui.SetFocusID(id, window)
ImGui.FocusWindow(window)
elseif bit32.band(flags, 4194304 --[[ImGuiButtonFlags.NoFocus]]) == 0 then
ImGui.FocusWindow(window, 1 --[[ImGuiFocusRequestFlags.RestoreFocusedChild]]) -- Still need to focus and bring to front, but try to avoid losing NavId when navigating a child
end
end
if bit32.band(flags, 128 --[[ImGuiButtonFlags.PressedOnRelease]]) ~= 0 then
-- FIXME: Traditionally ImGuiButtonFlags.PressedOnRelease never took ActiveId. Adding it in 2026-03-20 since ImGuiButtonFlags.NoHoldingActiveId can always be added.
-- We don't yet perform an explicit ClearActiveID() to reduce scope of change, but this possibility could be investigated.
if bit32.band(flags, 131072 --[[ImGuiButtonFlags.NoHoldingActiveId]]) == 0 then
ImGui.SetActiveID(id, window) -- Hold on ID
end
g.ActiveIdMouseButton = mouse_button_clicked
end
end
if bit32.band(flags, 128 --[[ImGuiButtonFlags.PressedOnRelease]]) ~= 0 then
if mouse_button_released ~= -1 then
local has_repeated_at_least_once = (bit32.band(item_flags, 8 --[[ImGuiItemFlags.ButtonRepeat]]) ~= 0) and g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay -- Repeat mode trumps on release behavior
if not has_repeated_at_least_once then
pressed = true
end
if bit32.band(flags, 262144 --[[ImGuiButtonFlags.NoNavFocus]]) == 0 then
ImGui.SetFocusID(id, window) -- FIXME: Lack of FocusWindow() call here is inconsistent with other paths. Research why.
end
ImGui.ClearActiveID()
end
end
-- 'Repeat' mode acts when held regardless of _PressedOn flags (see table above).
-- Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings.
if g.ActiveId == id and (bit32.band(item_flags, 8 --[[ImGuiItemFlags.ButtonRepeat]]) ~= 0) then
if g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0 and ImGui.IsMouseClickedEx(g.ActiveIdMouseButton, 1 --[[ImGuiInputFlags.Repeat]], test_owner_id) then
pressed = true
end
end
end
if pressed and g.IO.ConfigNavCursorVisibleAuto then
g.NavCursorVisible = false
end
end
-- Keyboard/Gamepad navigation handling
-- We report navigated and navigation-activated items as hovered but we don't set g.HoveredId to not interfere with mouse.
if bit32.band(item_flags, 64 --[[ImGuiItemFlags.Disabled]]) == 0 then
if g.NavId == id and g.NavCursorVisible and g.NavHighlightItemUnderNav then
if bit32.band(flags, 524288 --[[ImGuiButtonFlags.NoHoveredOnFocus]]) == 0 then
hovered = true
end
end
if g.NavActivateDownId == id then
local nav_activated_by_code = (g.NavActivateId == id)
local nav_activated_by_inputs = (g.NavActivatePressedId == id)
if not nav_activated_by_inputs and bit32.band(item_flags, 8 --[[ImGuiItemFlags.ButtonRepeat]]) ~= 0 then
-- Avoid pressing multiple keys from triggering excessive amount of repeat events
local key1 = ImGui.GetKeyData(g, 524 --[[ImGuiKey.Space]])
local key2 = ImGui.GetKeyData(g, 525 --[[ImGuiKey.Enter]])
local key3 = ImGui.GetKeyData(g, ImGuiKey.NavGamepadActivate)
local t1 = ImMax(ImMax(key1.DownDuration, key2.DownDuration), key3.DownDuration)
nav_activated_by_inputs = ImGui.CalcTypematicRepeatAmount(t1 - g.IO.DeltaTime, t1, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0
end
if nav_activated_by_code or nav_activated_by_inputs then
-- Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button.
pressed = true
ImGui.SetActiveID(id, window)
g.ActiveIdSource = g.NavInputSource
if bit32.band(flags, 262144 --[[ImGuiButtonFlags.NoNavFocus]]) == 0 and bit32.band(g.NavActivateFlags, 16 --[[ImGuiActivateFlags.FromShortcut]]) == 0 then
ImGui.SetFocusID(id, window)
end
if bit32.band(g.NavActivateFlags, 16 --[[ImGuiActivateFlags.FromShortcut]]) ~= 0 then
g.ActiveIdFromShortcut = true
end
end
end
end
-- Process while held
local held = false
if g.ActiveId == id then
if g.ActiveIdSource == 1 --[[ImGuiInputSource.Mouse]] then
if g.ActiveIdIsJustActivated then
g.ActiveIdClickOffset = g.IO.MousePos - bb.Min
end
local mouse_button = g.ActiveIdMouseButton
if mouse_button == -1 then
-- Fallback for the rare situation were g.ActiveId was set programmatically or from another widget (e.g. #6304).
ImGui.ClearActiveID()
elseif ImGui.IsMouseDown(mouse_button, test_owner_id) then
held = true
else
local release_in = hovered and (bit32.band(flags, 32 --[[ImGuiButtonFlags.PressedOnClickRelease]]) ~= 0)
local release_anywhere = (bit32.band(flags, 64 --[[ImGuiButtonFlags.PressedOnClickReleaseAnywhere]]) ~= 0)
if (release_in or release_anywhere) and not g.DragDropActive then
-- Report as pressed when releasing the mouse (this is the most common path)
local is_double_click_release = (bit32.band(flags, 256 --[[ImGuiButtonFlags.PressedOnDoubleClick]]) ~= 0) and g.IO.MouseReleased[mouse_button] and g.IO.MouseClickedLastCount[mouse_button] == 2
local is_repeating_already = (bit32.band(item_flags, 8 --[[ImGuiItemFlags.ButtonRepeat]]) ~= 0) and g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay -- Repeat mode trumps <on release>
local is_button_avail_or_owned = ImGui.TestKeyOwner(ImGui.MouseButtonToKey(mouse_button), test_owner_id)
if not is_double_click_release and not is_repeating_already and is_button_avail_or_owned then
pressed = true
end
end
ImGui.ClearActiveID()
end
if bit32.band(flags, 262144 --[[ImGuiButtonFlags.NoNavFocus]]) == 0 and g.IO.ConfigNavCursorVisibleAuto then
g.NavCursorVisible = false
end
elseif g.ActiveIdSource == 2 --[[ImGuiInputSource.Keyboard]] or g.ActiveIdSource == 3 --[[ImGuiInputSource.Gamepad]] then
-- When activated using Nav, we hold on the ActiveID until activation button is released
if g.NavActivateDownId == id then
held = true -- hovered == true not true as we are already likely hovered on direct activation.
else
ImGui.ClearActiveID()
end
end
if pressed then
g.ActiveIdHasBeenPressedBefore = true
end
end
-- Activation highlight (this may be a remote activation)
if g.NavHighlightActivatedId == id and (bit32.band(item_flags, 64 --[[ImGuiItemFlags.Disabled]]) == 0) then
hovered = true
end
return pressed, hovered, held
end
--- @param label string
--- @param size_arg? ImVec2
--- @param flags? ImGuiButtonFlags
--- @return bool
local ButtonEx_text_min, ButtonEx_text_max = ImVec2(), ImVec2()
@native
function ImGui.ButtonEx(label, size_arg, flags)
if size_arg == nil then size_arg = LAYOUT_ZERO_SIZE end -- [Luau] shared read-only: CalcItemSize() only reads it
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
local g = GImGui
local style = g.Style
local id = window:GetID(label)
local label_end = ImGui.FindRenderedTextEnd(label)
local label_size = ImGui.CalcTextSize(label, label_end, false)
-- [Luau] scalar pos, no temporary ImVec2
local pos_x, pos_y = window.DC.CursorPos.x, window.DC.CursorPos.y -- Don't modify the cursor!
local frame_padding = style.FramePadding
if bit32.band(flags, 32768 --[[ImGuiButtonFlags.AlignTextBaseLine]]) ~= 0 and frame_padding.y < window.DC.CurrLineTextBaseOffset then -- Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)
pos_y = pos_y + window.DC.CurrLineTextBaseOffset - frame_padding.y
end
local size = ImGui.CalcItemSize(size_arg, label_size.x + frame_padding.x * 2.0, label_size.y + frame_padding.y * 2.0)
local bb = ImRect(pos_x, pos_y, pos_x + size.x, pos_y + size.y)
ImGui.ItemSize(size, frame_padding.y)
if not ImGui.ItemAdd(bb, id) then
return false
end
local pressed, hovered, held = ImGui.ButtonBehavior(bb, id, flags)
-- Render
local col
if held and hovered then
col = ImGui.GetColorU32(24 --[[ImGuiCol.ButtonActive]])
elseif hovered then
col = ImGui.GetColorU32(23 --[[ImGuiCol.ButtonHovered]])
else
col = ImGui.GetColorU32(22 --[[ImGuiCol.Button]])
end
ImGui.RenderNavCursor(bb, id)
ImGui.RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding)
-- if (g.LogEnabled)
-- LogSetNextTextDecoration("[", "]");
local text_min, text_max = ButtonEx_text_min, ButtonEx_text_max -- [Luau] scratch: RenderTextClipped() only reads them
text_min.x = bb.Min.x + frame_padding.x; text_min.y = bb.Min.y + frame_padding.y
text_max.x = bb.Max.x - frame_padding.x; text_max.y = bb.Max.y - frame_padding.y
ImGui.RenderTextClipped(text_min, text_max, label, label_end, label_size, style.ButtonTextAlign, bb)
-- Automatically close popups
--if (pressed && !(flags & ImGuiButtonFlags.DontClosePopups) && (window->Flags & ImGuiWindowFlags.Popup))
-- CloseCurrentPopup();
-- IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
return pressed
end
--- @param label string
--- @param size_arg? ImVec2
--- @return bool
function ImGui.Button(label, size_arg)
return ImGui.ButtonEx(label, size_arg, 0 --[[ImGuiButtonFlags.None]])
end
-- Small buttons fits within text without additional vertical spacing.
--- @param label string
--- @return bool
function ImGui.SmallButton(label)
local g = GImGui
local backup_padding_y = g.Style.FramePadding.y
g.Style.FramePadding.y = 0.0
local pressed = ImGui.ButtonEx(label, ImVec2(0, 0), 32768 --[[ImGuiButtonFlags.AlignTextBaseLine]])
g.Style.FramePadding.y = backup_padding_y
return pressed
end
-- Tip: use ImGui.PushID()/PopID() to push indices or pointers in the ID stack.
-- Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id)
--- @param str_id string
--- @param size_arg ImVec2
--- @param flags? ImGuiButtonFlags
function ImGui.InvisibleButton(str_id, size_arg, flags)
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
-- Ensure zero-size fits to contents
local size = ImVec2(ImGui._CalcItemSizeXY(size_arg.x ~= 0.0 and size_arg.x or -1.1754943508223e-38 --[[FLT_MIN]], size_arg.y ~= 0.0 and size_arg.y or -1.1754943508223e-38 --[[FLT_MIN]], 0.0, 0.0))
local id = window:GetID(str_id)
local bb = ImRect(window.DC.CursorPos, window.DC.CursorPos + size)
ImGui.ItemSize(size)
local item_flags = (bit32.band(flags, 8 --[[ImGuiButtonFlags.EnableNav]]) ~= 0) and 0 --[[ImGuiItemFlags.None]] or 2 --[[ImGuiItemFlags.NoNav]]
if not ImGui.ItemAdd(bb, id, nil, item_flags) then
return false
end
local pressed, hovered, held = ImGui.ButtonBehavior(bb, id, flags)
ImGui.RenderNavCursor(bb, id)
-- IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags)
return pressed
end
--- @param str_id string
--- @param dir ImGuiDir
--- @param size ImVec2
--- @param flags? ImGuiButtonFlags
function ImGui.ArrowButtonEx(str_id, dir, size, flags)
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
local g = GImGui
local id = window:GetID(str_id)
local bb = ImRect(window.DC.CursorPos, window.DC.CursorPos + size)
local default_size = ImGui.GetFrameHeight()
ImGui.ItemSize(size, (size.y >= default_size) and g.Style.FramePadding.y or -1.0)
if not ImGui.ItemAdd(bb, id) then
return false
end
local pressed, hovered, held = ImGui.ButtonBehavior(bb, id, flags)
-- Render
local bg_col = ImGui.GetColorU32((held and hovered) and 24 --[[ImGuiCol.ButtonActive]] or hovered and 23 --[[ImGuiCol.ButtonHovered]] or 22 --[[ImGuiCol.Button]])
local text_col = ImGui.GetColorU32(0 --[[ImGuiCol.Text]])
ImGui.RenderNavCursor(bb, id)
ImGui.RenderFrame(bb.Min, bb.Max, bg_col, true, g.Style.FrameRounding)
ImGui.RenderArrow(window.DrawList, bb.Min + ImVec2(ImMax(0.0, (size.x - g.FontSize) * 0.5), ImMax(0.0, (size.y - g.FontSize) * 0.5)), text_col, dir)
-- IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags)
return pressed
end
--- @param str_id string
--- @param dir ImGuiDir
function ImGui.ArrowButton(str_id, dir)
local sz = ImGui.GetFrameHeight()
return ImGui.ArrowButtonEx(str_id, dir, ImVec2(sz, sz), 0 --[[ImGuiButtonFlags.None]])
end
-- Button to close a window
--- @param id ImGuiID
--- @param pos ImVec2
--- @return bool
function ImGui.CloseButton(id, pos)
local g = GImGui
local window = g.CurrentWindow
-- Tweak 1: Shrink hit-testing area if button covers an abnormally large proportion of the visible region. That's in order to facilitate moving the window away. (#3825)
-- This may better be applied as a general hit-rect reduction mechanism for all widgets to ensure the area to move window is always accessible?
local bb = ImRect(pos, pos + ImVec2(g.FontSize, g.FontSize))
local bb_interact = ImRect()
ImRect_Copy(bb_interact, bb)
local area_to_visible_ratio = window.OuterRectClipped:GetArea() / bb:GetArea()
if area_to_visible_ratio < 1.5 then
bb_interact:Expand(ImTrunc(bb_interact:GetSize() * -0.25))
end
-- Tweak 2: We intentionally allow interaction when clipped so that a mechanical Alt,Right,Activate sequence can always close a window.
-- (this isn't the common behavior of buttons, but it doesn't affect the user because navigation tends to keep items visible in scrolling layer).
local is_clipped = not ImGui.ItemAdd(bb_interact, id)
local pressed, hovered, held = ImGui.ButtonBehavior(bb, id)
if is_clipped then
return pressed
end
-- Render
local bg_col
if held then
bg_col = ImGui.GetColorU32(24 --[[ImGuiCol.ButtonActive]])
else
bg_col = ImGui.GetColorU32(23 --[[ImGuiCol.ButtonHovered]])
end
if hovered then
window.DrawList:AddRectFilled(bb.Min, bb.Max, bg_col)
end
local cross_center = bb:GetCenter()
local cross_extent = g.FontSize * 0.5 * 0.7071 - 1
local cross_col = ImGui.GetColorU32(0 --[[ImGuiCol.Text]])
local cross_thickness = 1.0 -- FIXME-DPI
window.DrawList:AddLine(cross_center + ImVec2(cross_extent, cross_extent), cross_center + ImVec2(-cross_extent, -cross_extent), cross_col, cross_thickness)
window.DrawList:AddLine(cross_center + ImVec2(cross_extent, -cross_extent), cross_center + ImVec2(-cross_extent, cross_extent), cross_col, cross_thickness)
return pressed
end
--- @param window ImGuiWindow
--- @param axis ImGuiAxis
--- @return ImGuiID
function ImGui.GetWindowScrollbarID(window, axis)
if axis == 1 --[[ImGuiAxis.X]] then
return window:GetID("#SCROLLX")
else
return window:GetID("#SCROLLY")
end
end
-- Return scrollbar rectangle, must only be called for corresponding axis if window.ScrollbarX/Y is set.
--- @param window ImGuiWindow
--- @param axis ImGuiAxis
--- @return ImRect
--- @nodiscard
function ImGui.GetWindowScrollbarRect(window, axis)
local g = GImGui
local outer_rect = window:Rect()
local inner_rect = window.InnerRect
-- (ScrollbarSizes.x = width of Y scrollbar; ScrollbarSizes.y = height of X scrollbar)
local scrollbar_size = window.ScrollbarSizes[axis == 1 --[[ImGuiAxis.X]] and 2 --[[ImGuiAxis.Y]] or 1 --[[ImGuiAxis.X]]]
assert(scrollbar_size >= 0.0)
local border_size = IM_ROUND(window.WindowBorderSize * 0.5)
local border_top = (bit32.band(window.Flags, 1024 --[[ImGuiWindowFlags.MenuBar]]) ~= 0) and IM_ROUND(g.Style.FrameBorderSize * 0.5) or (bit32.band(window.Flags, 1 --[[ImGuiWindowFlags.NoTitleBar]]) ~= 0 and border_size or 0)
if axis == 1 --[[ImGuiAxis.X]] then
return ImRect(inner_rect.Min.x + border_size, ImMax(outer_rect.Min.y + border_size, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x - border_size, outer_rect.Max.y - border_size)
else
return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y + border_top, outer_rect.Max.x - border_size, inner_rect.Max.y - border_size)
end
end
--- @param window ImGuiWindow
--- @param bb ImRect
--- @param threshold float
--- @param axis ImGuiAxis
function ImGui.ExtendHitBoxWhenNearViewportEdge(window, bb, threshold, axis)
local window_rect = window.RootWindow:Rect()
local viewport_rect = window.Viewport:GetMainRect()
if window_rect.Min[axis] == viewport_rect.Min[axis] and bb.Min[axis] > window_rect.Min[axis] and bb.Min[axis] - threshold <= window_rect.Min[axis] then
bb.Min[axis] = window_rect.Min[axis]
end
if window_rect.Max[axis] == viewport_rect.Max[axis] and bb.Max[axis] < window_rect.Max[axis] and bb.Max[axis] + threshold >= window_rect.Max[axis] then
bb.Max[axis] = window_rect.Max[axis]
end
end
-- Vertical/Horizontal scrollbar
-- The entire piece of code below is rather confusing because:
-- - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab)
-- - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar
-- - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal.
-- Still, the code should probably be made simpler..
--- @param bb_frame ImRect
--- @param id ImGuiID
--- @param axis ImGuiAxis
--- @param p_scroll_v number
--- @param size_visible_v number
--- @param size_contents_v number
--- @param draw_rounding_flags ImDrawFlags
--- @return bool is_held
--- @return number scroll_v # Updated p_scroll_v
function ImGui.ScrollbarEx(bb_frame, id, axis, p_scroll_v, size_visible_v, size_contents_v, draw_rounding_flags)
local g = GImGui
local window = g.CurrentWindow
if window.SkipItems then
return false, p_scroll_v
end
local bb_frame_width = bb_frame:GetWidth()
local bb_frame_height = bb_frame:GetHeight()
if bb_frame_width <= 0.0 or bb_frame_height <= 0.0 then
return false, p_scroll_v
end
-- When we are too small, start hiding and disabling the grab (this reduce visual noise on very small window and facilitate using the window resize grab)
local alpha = 1.0
if axis == 2 --[[ImGuiAxis.Y]] and bb_frame_height < bb_frame_width then
alpha = ImSaturate(bb_frame_height / ImMax(bb_frame_width * 2.0, 1.0))
end
if alpha <= 0.0 then
return false, p_scroll_v
end
local style = g.Style
local allow_interaction = (alpha >= 1.0)
local bb = ImRect()
ImRect_Copy(bb, bb_frame)
local padding = IM_TRUNC(ImMin(style.ScrollbarPadding, ImMin(bb_frame_width, bb_frame_height) * 0.5))
bb:Expand(-padding)
-- V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar)
local scrollbar_size_v
if axis == 1 --[[ImGuiAxis.X]] then
scrollbar_size_v = bb:GetWidth()
else
scrollbar_size_v = bb:GetHeight()
end
if scrollbar_size_v < 1.0 then
return false, p_scroll_v
end
-- Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount)
-- But we maintain a minimum size in pixel to allow for the user to still aim inside.
assert(ImMax(size_contents_v, size_visible_v) > 0.0) -- Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers.
local win_size_v = ImMax(ImMax(size_contents_v, size_visible_v), 1)
local grab_h_minsize = ImMin(bb:GetSize()[axis], style.GrabMinSize)
local grab_h_pixels = ImTrunc(ImClamp(scrollbar_size_v * (size_visible_v / win_size_v), grab_h_minsize, scrollbar_size_v))
local grab_h_norm = grab_h_pixels / scrollbar_size_v
-- As a special thing, we allow scrollbar near the edge of a screen/viewport to be reachable with mouse at the extreme edge (#9276)
local bb_hit = ImRect()
ImRect_Copy(bb_hit, bb_frame)
ImGui.ExtendHitBoxWhenNearViewportEdge(window, bb_hit, g.Style.WindowBorderSize, 1 --[[ImGuiAxis.X]] + 2 --[[ImGuiAxis.Y]] - axis) -- swap axis here
-- Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar().
ImGui.ItemAdd(bb_frame, id, nil, 2 --[[ImGuiItemFlags.NoNav]])
local pressed, hovered, held = ImGui.ButtonBehavior(bb_hit, id, 262144 --[[ImGuiButtonFlags.NoNavFocus]])
local scroll_max = ImMax(1, size_contents_v - size_visible_v)
local scroll_ratio = ImSaturate(p_scroll_v / scroll_max)
local grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v -- Grab position in normalized space
if held and allow_interaction and grab_h_norm < 1.0 then
local scrollbar_pos_v = bb.Min[axis]
local mouse_pos_v = g.IO.MousePos[axis]
-- Click position in scrollbar normalized space (0.0f->1.0f)
local clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v)
local held_dir
if clicked_v_norm < grab_v_norm then
held_dir = -1
elseif clicked_v_norm > grab_v_norm + grab_h_norm then
held_dir = 1
else
held_dir = 0
end
if g.ActiveIdIsJustActivated then
-- On initial click when held_dir == 0 (clicked over grab): calculate the distance between mouse and the center of the grab
local scroll_to_clicked_location = (g.IO.ConfigScrollbarScrollByPage == false) or g.IO.KeyShift or held_dir == 0
if scroll_to_clicked_location then
g.ScrollbarSeekMode = 0
else
g.ScrollbarSeekMode = held_dir
end
if held_dir == 0 and not g.IO.KeyShift then
g.ScrollbarClickDeltaToGrabCenter = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5
else
g.ScrollbarClickDeltaToGrabCenter = 0.0
end
end
-- Apply scroll (p_scroll_v will generally point on one member of window.Scroll)
-- It is ok to modify Scroll here because we are being called in Begin() after the calculation of ContentSize and before setting up our starting position
if g.ScrollbarSeekMode == 0 then
-- Absolute seeking
scroll_v_norm = ImSaturate((clicked_v_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm * 0.5) / (1.0 - grab_h_norm))
p_scroll_v = scroll_v_norm * scroll_max
else
-- Page by page
if ImGui.IsMouseClickedEx(0 --[[ImGuiMouseButton.Left]], 1 --[[ImGuiInputFlags.Repeat]]) and held_dir == g.ScrollbarSeekMode then
local page_dir
if g.ScrollbarSeekMode > 0.0 then
page_dir = 1.0
else
page_dir = -1.0
end
p_scroll_v = ImClamp(p_scroll_v + page_dir * size_visible_v, 0, scroll_max)
end
end
-- Update values for rendering
scroll_ratio = ImSaturate(p_scroll_v / scroll_max)
grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v
-- Update distance to grab now that we have seek'ed and saturated
--if (seek_absolute)
-- g.ScrollbarClickDeltaToGrabCenter = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f;
end
-- Render
local bg_col = ImGui.GetColorU32(14 --[[ImGuiCol.ScrollbarBg]])
local grab_col
if held then
grab_col = ImGui.GetColorU32(17 --[[ImGuiCol.ScrollbarGrabActive]], alpha)
elseif hovered then
grab_col = ImGui.GetColorU32(16 --[[ImGuiCol.ScrollbarGrabHovered]], alpha)
else
grab_col = ImGui.GetColorU32(15 --[[ImGuiCol.ScrollbarGrab]], alpha)
end
window.DrawList:AddRectFilled(bb_frame.Min, bb_frame.Max, bg_col, window.WindowRounding, draw_rounding_flags)
local grab_rect
if axis == 1 --[[ImGuiAxis.X]] then
local x1 = ImLerp(bb.Min.x, bb.Max.x, grab_v_norm)
grab_rect = ImRect(x1, bb.Min.y, x1 + grab_h_pixels, bb.Max.y)
else
local y1 = ImLerp(bb.Min.y, bb.Max.y, grab_v_norm)
grab_rect = ImRect(bb.Min.x, y1, bb.Max.x, y1 + grab_h_pixels)
end
window.DrawList:AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding)
return held, p_scroll_v
end
--- @param axis ImGuiAxis
function ImGui.Scrollbar(axis)
local g = GImGui
local window = g.CurrentWindow
local id = ImGui.GetWindowScrollbarID(window, axis)
-- Calculate scrollbar bounding box
local bb = ImGui.GetWindowScrollbarRect(window, axis)
local rounding_corners = ImGui.CalcRoundingFlagsForRectInRect(bb, window:Rect(), g.Style.WindowBorderSize)
local k = (axis == 1 --[[ImGuiAxis.X]]) and "x" or "y" -- [Luau] named key: ImVec2[axis] goes through IM_VEC2.__index
local size_visible = window.InnerRect.Max[k] - window.InnerRect.Min[k]
local size_contents = window.ContentSize[k] + window.WindowPadding[k] * 2.0
local scroll = window.Scroll[k]
local held
held, scroll = ImGui.ScrollbarEx(bb, id, axis, scroll, size_visible, size_contents, rounding_corners)
window.Scroll[k] = scroll
end
-- - Read about ImTextureID/ImTextureRef here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
-- - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above.
--- @param tex_ref ImTextureRef
--- @param image_size ImVec2
--- @param uv0? ImVec2
--- @param uv1? ImVec2
--- @param bg_col? ImVec4
--- @param tint_col? ImVec4
function ImGui.ImageWithBg(tex_ref, image_size, uv0, uv1, bg_col, tint_col)
if uv0 == nil then uv0 = ImVec2(0, 0) end
if uv1 == nil then uv1 = ImVec2(1, 1) end
if bg_col == nil then bg_col = ImVec4(0, 0, 0, 0) end
if tint_col == nil then tint_col = ImVec4(1, 1, 1, 1) end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return
end
local g = GImGui
local padding = ImVec2(g.Style.ImageBorderSize, g.Style.ImageBorderSize)
local bb = ImRect(window.DC.CursorPos, window.DC.CursorPos + image_size + padding * 2.0)
ImGui.ItemSize(bb)
if not ImGui.ItemAdd(bb, 0) then
return
end
-- Render
local rounding = g.Style.ImageRounding
if bg_col.w > 0.0 then
window.DrawList:AddRectFilled(bb.Min + padding, bb.Max - padding, ImGui.GetColorU32(bg_col), rounding)
end
if rounding > 0.0 then
window.DrawList:AddImageRounded(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, ImGui.GetColorU32(tint_col), rounding)
else
window.DrawList:AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, ImGui.GetColorU32(tint_col))
end
if g.Style.ImageBorderSize > 0.0 then
window.DrawList:AddRect(bb.Min, bb.Max, ImGui.GetColorU32(5 --[[ImGuiCol.Border]]), rounding, g.Style.ImageBorderSize, 0 --[[ImDrawFlags.None]])
end
end
--- @param tex_ref ImTextureRef
--- @param image_size ImVec2
--- @param uv0? ImVec2
--- @param uv1? ImVec2
function ImGui.Image(tex_ref, image_size, uv0, uv1)
ImGui.ImageWithBg(tex_ref, image_size, uv0, uv1)
end
--- @param id ImGuiID
--- @param tex_ref ImTextureRef
--- @param image_size ImVec2
--- @param uv0 ImVec2
--- @param uv1 ImVec2
--- @param bg_col ImVec4
--- @param tint_col ImVec4
--- @param flags? ImGuiButtonFlags
function ImGui.ImageButtonEx(id, tex_ref, image_size, uv0, uv1, bg_col, tint_col, flags)
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
local g = GImGui
local padding = g.Style.FramePadding
local bb = ImRect(window.DC.CursorPos, window.DC.CursorPos + image_size + padding * 2.0)
ImGui.ItemSize(bb)
if not ImGui.ItemAdd(bb, id) then
return false
end
local pressed, hovered, held = ImGui.ButtonBehavior(bb, id, flags)
-- Render
local col = ImGui.GetColorU32((held and hovered) and 24 --[[ImGuiCol.ButtonActive]] or hovered and 23 --[[ImGuiCol.ButtonHovered]] or 22 --[[ImGuiCol.Button]])
ImGui.RenderNavCursor(bb, id)
ImGui.RenderFrame(bb.Min, bb.Max, col, true, g.Style.FrameRounding)
if bg_col.w > 0.0 then
window.DrawList:AddRectFilled(bb.Min + padding, bb.Max - padding, ImGui.GetColorU32(bg_col))
end
local image_rounding = ImMax(g.Style.FrameRounding - ImMax(padding.x, padding.y), g.Style.ImageRounding)
if image_rounding > 0.0 then
window.DrawList:AddImageRounded(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, ImGui.GetColorU32(tint_col), image_rounding)
else
window.DrawList:AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, ImGui.GetColorU32(tint_col))
end
return pressed
end
-- - ImageButton() adds style.FramePadding*2.0f to provided size. This is in order to facilitate fitting an image in a button.
-- - ImageButton() draws a background based on regular Button() color + optionally an inner background if specified. (#8165) -- FIXME: Maybe that's not the best design?
--- @param str_id string
--- @param tex_ref ImTextureRef
--- @param image_size ImVec2
--- @param uv0 ImVec2
--- @param uv1 ImVec2
--- @param bg_col ImVec4
--- @param tint_col ImVec4
function ImGui.ImageButton(str_id, tex_ref, image_size, uv0, uv1, bg_col, tint_col)
local g = GImGui
local window = g.CurrentWindow
if window.SkipItems then
return false
end
return ImGui.ImageButtonEx(window:GetID(str_id), tex_ref, image_size, uv0, uv1, bg_col, tint_col)
end
--- @param label string
--- @param v bool
--- @return bool is_pressed
--- @return bool is_checked # The updated `v` passed in
@native
function ImGui.Checkbox(label, v)
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false, v
end
local g = GImGui
local style = g.Style
local id = window:GetID(label)
local label_end = ImGui.FindRenderedTextEnd(label)
local label_size = ImGui.CalcTextSize(label, label_end, false)
local square_sz = g.FontSize + style.FramePadding.y * 2.0 -- [Luau] inline GetFrameHeight()
local pos_x, pos_y = window.DC.CursorPos.x, window.DC.CursorPos.y -- [Luau] scalar pos, no temporary ImVec2
local total_width
if label_size.x > 0.0 then
total_width = square_sz + style.ItemInnerSpacing.x + label_size.x
else
total_width = square_sz
end
local total_bb = ImRect(pos_x, pos_y, pos_x + total_width, pos_y + (label_size.y + style.FramePadding.y * 2.0))
ImGui.ItemSize(total_bb, style.FramePadding.y)
local is_visible = ImGui.ItemAdd(total_bb, id)
local is_multi_select = (bit32.band(g.LastItemData.ItemFlags, 4194304 --[[ImGuiItemFlags.IsMultiSelect]]) ~= 0)
if not is_visible then
if not is_multi_select or not g.BoxSelectState.UnclipMode or not g.BoxSelectState.UnclipRect:Overlaps(total_bb) then -- Extra layer of "no logic clip" for box-select support
-- IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0))
return false, v
end
end
-- Range-Selection/Multi-selection support (header)
local checked = v
if is_multi_select then
checked = ImGui.MultiSelectItemHeader(id, checked, nil)
end
local pressed, hovered, held = ImGui.ButtonBehavior(total_bb, id)
-- Range-Selection/Multi-selection support (footer)
if is_multi_select then
checked, pressed = ImGui.MultiSelectItemFooter(id, checked, pressed, 1048576 --[[ImGuiMultiSelectFlags.CheckboxMode_]])
elseif pressed then
checked = not checked
end
if v ~= checked then
v = checked
pressed = true -- return value
ImGui.MarkItemEdited(id)
end
local check_bb = ImRect(pos_x, pos_y, pos_x + square_sz, pos_y + square_sz)
local mixed_value = (bit32.band(g.LastItemData.ItemFlags, 4096 --[[ImGuiItemFlags.MixedValue]]) ~= 0)
if is_visible then
ImGui.RenderNavCursor(total_bb, id)
local bg_col = ImGui.GetColorU32((held and hovered) and 9 --[[ImGuiCol.FrameBgActive]] or (hovered and 8 --[[ImGuiCol.FrameBgHovered]] or ((mixed_value or checked) and 19 --[[ImGuiCol.CheckboxSelectedBg]] or 7 --[[ImGuiCol.FrameBg]])))
local check_col = ImGui.GetColorU32(18 --[[ImGuiCol.CheckMark]])
ImGui.RenderFrame(check_bb.Min, check_bb.Max, bg_col, true, style.FrameRounding)
if mixed_value then
-- Undocumented tristate/mixed/indeterminate checkbox (#2644)
-- This may seem awkwardly designed because the aim is to make ImGuiItemFlags.MixedValue supported by all widgets (not just checkbox)
local pad_val = ImMax(1.0, IM_TRUNC(square_sz / 3.6))
local pad = ImVec2(pad_val, pad_val)
window.DrawList:AddRectFilled(check_bb.Min + pad, check_bb.Max - pad, check_col, style.FrameRounding)
elseif v then
local pad = ImMax(1.0, IM_TRUNC(square_sz / 6.0))
ImGui.RenderCheckMark(window.DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0)
end
end
if g.LogEnabled then
-- ImGui.LogRenderedText(label_pos, mixed_value and "[~]" or (v and "[x]" or "[ ]"))
end
if is_visible and label_size.x > 0.0 then
local label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y) -- [Luau] only built when used
ImGui.RenderText(label_pos, label, 1, label_end, false)
end
-- IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0))
return pressed, v
end
--- @param label string
--- @param flags int
--- @param flags_value int
--- @return bool is_pressed
--- @return int flags_new # Updated `flags`
function ImGui.CheckboxFlags(label, flags, flags_value)
local all_on = bit32.band(flags, flags_value) == flags_value
local any_on = bit32.band(flags, flags_value) ~= 0
local pressed
if not all_on and any_on then
local g = GImGui
g.NextItemData.ItemFlagsSet = bit32.bor(g.NextItemData.ItemFlagsSet, 4096 --[[ImGuiItemFlags.MixedValue]])
pressed, all_on = ImGui.Checkbox(label, all_on)
else
pressed, all_on = ImGui.Checkbox(label, all_on)
end
if pressed then
if all_on then
flags = bit32.bor(flags, flags_value)
else
flags = bit32.band(flags, bit32.bnot(flags_value))
end
end
return pressed, flags
end
ImGui.CheckboxFlagsT = ImGui.CheckboxFlags
--- @param label string
--- @param active bool
--- @return bool is_pressed
function ImGui.RadioButtonEx(label, active)
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
local g = GImGui
local style = g.Style
local id = window:GetID(label)
local label_end = ImGui.FindRenderedTextEnd(label)
local label_size = ImGui.CalcTextSize(label, label_end, false)
local square_sz = g.FontSize + style.FramePadding.y * 2.0 -- [Luau] inline GetFrameHeight()
local pos_x, pos_y = window.DC.CursorPos.x, window.DC.CursorPos.y -- [Luau] scalar pos, no temporary ImVec2
local check_bb = ImRect(pos_x, pos_y, pos_x + square_sz, pos_y + square_sz)
local total_bb = ImRect(pos_x, pos_y, pos_x + (square_sz + (label_size.x > 0.0 and style.ItemInnerSpacing.x + label_size.x or 0.0)), pos_y + (label_size.y + style.FramePadding.y * 2.0))
ImGui.ItemSize(total_bb, style.FramePadding.y)
if not ImGui.ItemAdd(total_bb, id) then
return false
end
local center = check_bb:GetCenter()
center.x = IM_ROUND(center.x)
center.y = IM_ROUND(center.y)
local radius = (square_sz - 1.0) * 0.5
local pressed, hovered, held = ImGui.ButtonBehavior(total_bb, id)
if (pressed) then
ImGui.MarkItemEdited(id)
end
ImGui.RenderNavCursor(total_bb, id)
local num_segment = window.DrawList:_CalcCircleAutoSegmentCount(radius)
local col
if held and hovered then
col = ImGui.GetColorU32(9 --[[ImGuiCol.FrameBgActive]])
else
if hovered then
col = ImGui.GetColorU32(8 --[[ImGuiCol.FrameBgHovered]])
else
col = ImGui.GetColorU32(7 --[[ImGuiCol.FrameBg]])
end
end
window.DrawList:AddCircleFilled(center, radius, col, num_segment)
if active then
local pad = ImMax(1.0, IM_TRUNC(square_sz / 6.0))
window.DrawList:AddCircleFilled(center, radius - pad, ImGui.GetColorU32(18 --[[ImGuiCol.CheckMark]]))
end
if style.FrameBorderSize > 0.0 then
window.DrawList:AddCircle(center + ImVec2(1, 1), radius, ImGui.GetColorU32(6 --[[ImGuiCol.BorderShadow]]), num_segment, style.FrameBorderSize)
window.DrawList:AddCircle(center, radius, ImGui.GetColorU32(5 --[[ImGuiCol.Border]]), num_segment, style.FrameBorderSize)
end
local label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y)
if g.LogEnabled then
-- ImGui.LogRenderedText(label_pos, active and "(x)" or "( )")
end
if label_size.x > 0.0 then
ImGui.RenderText(label_pos, label, 1, label_end, false)
end
return pressed
end
-- `rawequal` is used here to check if v == v_button
--- @param label string
--- @param v any
--- @param v_button any
--- @return bool is_pressed
--- @return any v # Updated v
function ImGui.RadioButton(label, v, v_button)
local pressed = ImGui.RadioButtonEx(label, rawequal(v, v_button))
if pressed then
v = v_button
end
return pressed, v
end
-- size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size
--- @param fraction float
--- @param size_arg? ImVec2
--- @param overlay? string
function ImGui.ProgressBar(fraction, size_arg, overlay)
if size_arg == nil then size_arg = ImVec2(-1.1754943508223e-38 --[[FLT_MIN]], 0) end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return
end
local g = GImGui
local style = g.Style
local pos = ImVec2()
ImVec2_Copy(pos, window.DC.CursorPos)
local size = ImGui.CalcItemSize(size_arg, ImGui.CalcItemWidth(), g.FontSize + style.FramePadding.y * 2.0)
local bb = ImRect(pos, pos + size)
ImGui.ItemSize(size, style.FramePadding.y)
if not ImGui.ItemAdd(bb, 0) then
return
end
-- Fraction < 0.0f will display an indeterminate progress bar animation
-- The value must be animated along with time, so e.g. passing '-1.0f * ImGui.GetTime()' as fraction works.
local is_indeterminate = (fraction < 0.0)
if not is_indeterminate then
fraction = ImSaturate(fraction)
end
-- Out of courtesy we accept a NaN fraction without crashing
local fill_n0 = 0.0
local fill_n1 = (fraction == fraction) and fraction or 0.0
if is_indeterminate then
local fill_width_n = 0.2
fill_n0 = ImFmod(-fraction, 1.0) * (1.0 + fill_width_n) - fill_width_n
fill_n1 = ImSaturate(fill_n0 + fill_width_n)
fill_n0 = ImSaturate(fill_n0)
end
-- Render
ImGui.RenderFrame(bb.Min, bb.Max, ImGui.GetColorU32(7 --[[ImGuiCol.FrameBg]]), true, style.FrameRounding)
bb:Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize))
local fill_x0 = ImLerp(bb.Min.x, bb.Max.x, fill_n0)
local fill_x1 = ImLerp(bb.Min.x, bb.Max.x, fill_n1)
if fill_x0 < fill_x1 then
ImGui.RenderRectFilledInRangeH(window.DrawList, bb, ImGui.GetColorU32(46 --[[ImGuiCol.PlotHistogram]]), fill_x0, fill_x1, style.FrameRounding)
end
-- Default displaying the fraction as percentage string, but user can override it
-- Don't display text for indeterminate bars by default
if not is_indeterminate or overlay ~= nil then
if overlay == nil then
overlay = ImFormatString("%.0f%%", fraction * 100 + 0.01)
end
local overlay_size = ImGui.CalcTextSize(overlay, nil)
if overlay_size.x > 0.0 then
local text_x = is_indeterminate and ((bb.Min.x + bb.Max.x - overlay_size.x) * 0.5) or (fill_x1 + style.ItemSpacing.x)
ImGui.RenderTextClipped(ImVec2(ImClamp(text_x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, nil, overlay_size, ImVec2(0.0, 0.5), bb)
end
end
end
function ImGui.Bullet()
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return
end
local g = GImGui
local style = g.Style
local line_height = ImMax(ImMin(window.DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), g.FontSize)
local bb = ImRect(window.DC.CursorPos, window.DC.CursorPos + ImVec2(g.FontSize, line_height))
ImGui.ItemSize(bb)
if not ImGui.ItemAdd(bb, 0) then
ImGui.SameLine(0, style.FramePadding.x * 2)
return
end
-- Render and stay on same line
local text_col = ImGui.GetColorU32(0 --[[ImGuiCol.Text]])
ImGui.RenderBullet(window.DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5, line_height * 0.5), text_col)
ImGui.SameLine(0, style.FramePadding.x * 2.0)
end
-- This is provided as a convenience for being an often requested feature.
-- FIXME-STYLE: we delayed adding as there is a larger plan to revamp the styling system.
-- Because of this we currently don't provide many styling options for this widget
-- (e.g. hovered/active colors are automatically inferred from a single color).
--- @param label string
function ImGui.TextLink(label)
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
local g = GImGui
local id = window:GetID(label)
local label_end = ImGui.FindRenderedTextEnd(label)
local pos = ImVec2(window.DC.CursorPos.x, window.DC.CursorPos.y + window.DC.CurrLineTextBaseOffset)
local size = ImGui.CalcTextSize(label, label_end, false)
local bb = ImRect(pos, pos + size)
ImGui.ItemSize(size, 0.0)
if not ImGui.ItemAdd(bb, id) then
return false
end
local pressed, hovered, held = ImGui.ButtonBehavior(bb, id)
ImGui.RenderNavCursor(bb, id)
if hovered then
ImGui.SetMouseCursor(7 --[[ImGuiMouseCursor.Hand]])
end
local text_colf = ImVec4()
ImVec4_Copy(text_colf, g.Style.Colors[53 --[[ImGuiCol.TextLink]]])
local line_colf = ImVec4()
ImVec4_Copy(line_colf, text_colf)
do
-- FIXME-STYLE: Read comments above. This widget is NOT written in the same style as some earlier widgets,
-- as we are currently experimenting/planning a different styling system.
local h, s, v = ImGui.ColorConvertRGBtoHSV(text_colf.x, text_colf.y, text_colf.z)
if held or hovered then
v = ImSaturate(v + (held and 0.4 or 0.3))
h = ImFmod(h + 0.02, 1.0)
end
text_colf.x, text_colf.y, text_colf.z = ImGui.ColorConvertHSVtoRGB(h, s, v)
v = ImSaturate(v - 0.20)
line_colf.x, line_colf.y, line_colf.z = ImGui.ColorConvertHSVtoRGB(h, s, v)
end
local line_y = bb.Max.y + ImFloor(g.FontBaked.Descent * g.FontBakedScale * 0.20)
window.DrawList:AddLineH(bb.Min.x, bb.Max.x, line_y, ImGui.GetColorU32(line_colf), 1.0 * ImTrunc(g.Style._MainScale)) -- FIXME-TEXT: Underline mode -- FIXME-DPI
ImGui.PushStyleColor(0 --[[ImGuiCol.Text]], ImGui.GetColorU32(text_colf))
ImGui.RenderText(bb.Min, label, 1, label_end, false)
ImGui.PopStyleColor()
-- IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags)
return pressed
end
--- @param label string
--- @param url string
function ImGui.TextLinkOpenURL(label, url)
local g = GImGui
if url == nil then
url = label
end
local pressed = ImGui.TextLink(label)
if pressed and g.PlatformIO.Platform_OpenInShellFn ~= nil then
g.PlatformIO.Platform_OpenInShellFn(g, url)
end
ImGui.SetItemTooltip(ImGui.LocalizeGetMsg(11 --[[ImGuiLocKey.OpenLink_s]]), url) -- It is more reassuring for user to _always_ display URL when it is the same as the label.
if ImGui.BeginPopupContextItem() then
if ImGui.MenuItem(ImGui.LocalizeGetMsg(12 --[[ImGuiLocKey.CopyLink]])) then
ImGui.SetClipboardText(url)
end
ImGui.EndPopup()
end
return pressed
end
-------------------------------------------------------------------------------
-- [SECTION] Widgets: Low-level Layout helpers
-------------------------------------------------------------------------------
-- - Spacing()
-- - Dummy()
-- - NewLine()
-- - AlignTextToFramePadding()
-- - SeparatorEx() [Internal]
-- - Separator()
-------------------------------------------------------------------------------
local LayoutHelpers_size = ImVec2() -- [Luau] scratch for ItemSize() arguments
function ImGui.Spacing()
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return
end
ImGui.ItemSize(LAYOUT_ZERO_SIZE)
end
--- @param size ImVec2
function ImGui.Dummy(size)
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return
end
local cursor = window.DC.CursorPos
local bb = ImRect(cursor.x, cursor.y, cursor.x + size.x, cursor.y + size.y) -- [Luau] no temporary ImVec2
ImGui.ItemSize(size)
ImGui.ItemAdd(bb, 0)
end
function ImGui.NewLine()
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return
end
local g = GImGui
local backup_layout_type = window.DC.LayoutType
window.DC.LayoutType = 1 --[[ImGuiLayoutType.Vertical]]
window.DC.IsSameLine = false
if window.DC.CurrLineSize.y > 0.0 then -- In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height.
ImGui.ItemSize(ImVec2(0, 0))
else
ImGui.ItemSize(ImVec2(0.0, g.FontSize))
end
window.DC.LayoutType = backup_layout_type
end
function ImGui.AlignTextToFramePadding()
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return
end
local g = GImGui
window.DC.CurrLineSize.y = ImMax(window.DC.CurrLineSize.y, g.FontSize + g.Style.FramePadding.y * 2)
window.DC.CurrLineTextBaseOffset = ImMax(window.DC.CurrLineTextBaseOffset, g.Style.FramePadding.y)
end
-- Horizontal/vertical separating line
-- FIXME: Surprisingly, this seemingly trivial widget is a victim of many different legacy/tricky layout issues.
-- Note how thickness == 1.0f is handled specifically as not moving CursorPos by 'thickness', but other values are.
--- @param flags ImGuiSeparatorFlags
--- @param thickness float
function ImGui.SeparatorEx(flags, thickness)
if thickness == nil then thickness = 1.0 end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return
end
local g = GImGui
assert(ImIsPowerOfTwo(bit32.band(flags, bit32.bor(1 --[[ImGuiSeparatorFlags.Horizontal]], 2 --[[ImGuiSeparatorFlags.Vertical]])))) -- Check that only 1 option is selected
assert(thickness > 0.0)
if bit32.band(flags, 2 --[[ImGuiSeparatorFlags.Vertical]]) ~= 0 then
-- Vertical separator, for menu bars (use current line height).
local y1 = window.DC.CursorPos.y
local y2 = window.DC.CursorPos.y + window.DC.CurrLineSize.y
local bb = ImRect(window.DC.CursorPos.x, y1, window.DC.CursorPos.x + thickness, y2) -- [Luau] no temporary ImVec2
local item_size = LayoutHelpers_size; item_size.x = thickness; item_size.y = 0.0
ImGui.ItemSize(item_size)
if not ImGui.ItemAdd(bb, 0) then
return
end
-- Draw
window.DrawList:AddRectFilled(bb.Min, bb.Max, ImGui.GetColorU32(28 --[[ImGuiCol.Separator]]))
if g.LogEnabled then
ImGui.LogText(" |")
end
elseif bit32.band(flags, 1 --[[ImGuiSeparatorFlags.Horizontal]]) ~= 0 then
-- Horizontal Separator
local x1 = window.DC.CursorPos.x
local x2 = window.WorkRect.Max.x
-- Preserve legacy behavior inside Columns()
-- Before Tables API happened, we relied on Separator() to span all columns of a Columns() set.
-- We currently don't need to provide the same feature for tables because tables naturally have border features.
local columns = (bit32.band(flags, 4 --[[ImGuiSeparatorFlags.SpanAllColumns]]) ~= 0) and window.DC.CurrentColumns or nil
if columns then
x1 = window.Pos.x + window.DC.Indent.x -- Used to be Pos.x before 2023/10/03
x2 = window.Pos.x + window.Size.x
ImGui.PushColumnsBackground()
end
-- We don't provide our width to the layout so that it doesn't get feed back into AutoFit
-- FIXME: This prevents ->CursorMaxPos based bounding box evaluation from working (e.g. TableEndCell)
-- Between 1.71 and 1.92.7, we maintained a hack where a 1.0f thin Separator() would not impact layout.
-- This was mostly chosen to allow backward compatibility with user's code assuming zero-height when calculating height for layout (e.g. bottom alignment of a status bar).
-- In order to handle scaling we need to scale separator thickness and it would not makes sense to have a disparity depending on height.
----float thickness_for_layout = (thickness == 1.0f) ? 0.0f : thickness; // FIXME: See 1.70/1.71 Separator() change: makes legacy 1-px separator not affect layout yet. Should change.
local bb = ImRect(x1, window.DC.CursorPos.y, x2, window.DC.CursorPos.y + thickness) -- [Luau] no temporary ImVec2
local item_size = LayoutHelpers_size; item_size.x = 0.0; item_size.y = thickness
ImGui.ItemSize(item_size)
if ImGui.ItemAdd(bb, 0) then
-- Draw
window.DrawList:AddRectFilled(bb.Min, bb.Max, ImGui.GetColorU32(28 --[[ImGuiCol.Separator]]))
if g.LogEnabled then
ImGui.LogRenderedText(bb.Min, "--------------------------------")
end
end
if columns then
ImGui.PopColumnsBackground()
columns.LineMinY = window.DC.CursorPos.y
end
end
end
function ImGui.Separator()
local g = GImGui
local window = g.CurrentWindow
if window.SkipItems then
return
end
-- Those flags should eventually be configurable by the user
local flags
if window.DC.LayoutType == 0 --[[ImGuiLayoutType.Horizontal]] then
flags = 2 --[[ImGuiSeparatorFlags.Vertical]]
else
flags = 1 --[[ImGuiSeparatorFlags.Horizontal]]
end
-- Only applies to legacy Columns() api as they relied on Separator() a lot.
if window.DC.CurrentColumns then
flags = bit32.bor(flags, 4 --[[ImGuiSeparatorFlags.SpanAllColumns]])
end
ImGui.SeparatorEx(flags, ImMax(g.Style.SeparatorSize, 1.0))
end
--- @param id ImGuiID
--- @param label string
--- @param label_end? int
--- @param extra_w float
function ImGui.SeparatorTextEx(id, label, label_end, extra_w)
local g = GImGui
local window = g.CurrentWindow
local style = g.Style
local label_size = ImGui.CalcTextSize(label, label_end, false)
local pos = ImVec2(window.DC.CursorPos.x, window.DC.CursorPos.y)
local padding = style.SeparatorTextPadding
local separator_thickness = style.SeparatorTextBorderSize
local min_size = ImVec2(label_size.x + extra_w + padding.x * 2.0, ImMax(label_size.y + padding.y * 2.0, separator_thickness))
local bb = ImRect(pos, ImVec2(window.WorkRect.Max.x, pos.y + min_size.y))
local text_baseline_y = ImTrunc((bb:GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.999) --ImMax(padding.y, ImTrunc((style.SeparatorTextSize - label_size.y) * 0.5f))
ImGui.ItemSize(min_size, text_baseline_y)
if not ImGui.ItemAdd(bb, id) then
return
end
local sep1_x1 = pos.x
local sep2_x2 = bb.Max.x
local seps_y = ImTrunc((bb.Min.y + bb.Max.y) * 0.5) + 1.0
local label_avail_w = ImMax(0.0, sep2_x2 - sep1_x1 - padding.x * 2.0)
local label_pos = ImVec2(pos.x + padding.x + ImMax(0.0, (label_avail_w - label_size.x - extra_w) * style.SeparatorTextAlign.x), pos.y + text_baseline_y) -- FIXME-ALIGN
-- This allows using SameLine() to position something in the 'extra_w'
window.DC.CursorPosPrevLine.x = label_pos.x + label_size.x
local separator_col = ImGui.GetColorU32(28 --[[ImGuiCol.Separator]])
if label_size.x > 0.0 then
local sep1_x2 = label_pos.x - style.ItemSpacing.x
local sep2_x1 = label_pos.x + label_size.x + extra_w + style.ItemSpacing.x
if sep1_x2 > sep1_x1 and separator_thickness > 0.0 then
window.DrawList:AddLineH(sep1_x1, sep1_x2, seps_y, separator_col, separator_thickness, 393216 --[[ImDrawFlags.StrokeCenterBiased]])
end
if sep2_x2 > sep2_x1 and separator_thickness > 0.0 then
window.DrawList:AddLineH(sep2_x1, sep2_x2, seps_y, separator_col, separator_thickness, 393216 --[[ImDrawFlags.StrokeCenterBiased]])
end
if g.LogEnabled then
ImGui.LogSetNextTextDecoration("---", nil)
end
ImGui.RenderTextEllipsis(window.DrawList, label_pos, ImVec2(bb.Max.x, bb.Max.y + style.ItemSpacing.y), bb.Max.x, label, label_end, label_size)
else
if g.LogEnabled then
ImGui.LogText("---")
end
if separator_thickness > 0.0 then
window.DrawList:AddLineH(sep1_x1, sep2_x2, seps_y, separator_col, separator_thickness, 393216 --[[ImDrawFlags.StrokeCenterBiased]])
end
end
end
--- @param label string
function ImGui.SeparatorText(label)
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return
end
-- The SeparatorText() vs SeparatorTextEx() distinction is designed to be considerate that we may want:
-- - allow separator-text to be draggable items (would require a stable ID + a noticeable highlight)
-- - this high-level entry point to allow formatting? (which in turns may require ID separate from formatted string)
-- - because of this we probably can't turn 'string label' into 'string fmt, ...'
-- Otherwise, we can decide that users wanting to drag this would layout a dedicated drag-item,
-- and then we can turn this into a format function.
ImGui.SeparatorTextEx(0, label, ImGui.FindRenderedTextEnd(label), 0.0)
end
-------------------------------------------------------------------------------
-- [SECTION] Widgets: ComboBox
-------------------------------------------------------------------------------
-- - CalcMaxPopupHeightFromItemCount() [Internal]
-- - BeginCombo()
-- - BeginComboPopup() [Internal]
-- - EndCombo()
-- - BeginComboPreview() [Internal]
-- - EndComboPreview() [Internal]
-- - Combo()
-------------------------------------------------------------------------------
--- @param items_count float
--- @return float
local function CalcMaxPopupHeightFromItemCount(items_count)
local g = GImGui
if items_count <= 0 then
return 3.4028234663853e+38 --[[FLT_MAX]]
end
return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2)
end
--- @param label string
--- @param preview_value? string
--- @param flags? ImGuiComboFlags
function ImGui.BeginCombo(label, preview_value, flags)
if flags == nil then flags = 0 end
local g = GImGui
local window = ImGui.GetCurrentWindow()
local backup_next_window_data_flags = g.NextWindowData.HasFlags
g.NextWindowData:ClearFlags() -- We behave like Begin() and need to consume those values
if window.SkipItems then
return false
end
local style = g.Style
local id = window:GetID(label)
assert(bit32.band(flags, bit32.bor(32 --[[ImGuiComboFlags.NoArrowButton]], 64 --[[ImGuiComboFlags.NoPreview]])) ~= bit32.bor(32 --[[ImGuiComboFlags.NoArrowButton]], 64 --[[ImGuiComboFlags.NoPreview]])) -- Can't use both flags together
if bit32.band(flags, 128 --[[ImGuiComboFlags.WidthFitPreview]]) ~= 0 then
assert(bit32.band(flags, 64 --[[ImGuiComboFlags.NoPreview]]) == 0)
end
local arrow_size
if (bit32.band(flags, 32 --[[ImGuiComboFlags.NoArrowButton]]) ~= 0) then
arrow_size = 0.0
else
arrow_size = ImGui.GetFrameHeight()
end
local label_end = ImGui.FindRenderedTextEnd(label)
local label_size = ImGui.CalcTextSize(label, label_end, false)
local preview_width
if ((bit32.band(flags, 128 --[[ImGuiComboFlags.WidthFitPreview]]) ~= 0) and (preview_value ~= nil)) then
preview_width = ImGui.CalcTextSize(preview_value, nil, false).x
else
preview_width = 0.0
end
local w
if bit32.band(flags, 64 --[[ImGuiComboFlags.NoPreview]]) ~= 0 then
w = arrow_size
elseif bit32.band(flags, 128 --[[ImGuiComboFlags.WidthFitPreview]]) ~= 0 then
w = arrow_size + preview_width + style.FramePadding.x * 2.0
else
w = ImGui.CalcItemWidth()
end
local bb = ImRect(window.DC.CursorPos, window.DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0))
local label_offset = (label_size.x > 0.0) and (style.ItemInnerSpacing.x + label_size.x) or 0.0
local total_bb = ImRect(bb.Min, bb.Max + ImVec2(label_offset, 0.0))
ImGui.ItemSize(total_bb, style.FramePadding.y)
if not ImGui.ItemAdd(total_bb, id, bb) then
return false
end
-- Open on click
local pressed, hovered, held = ImGui.ButtonBehavior(bb, id)
local popup_id = ImHashStr("##ComboPopup", nil, id)
local popup_open = ImGui.IsPopupOpen(popup_id, 0 --[[ImGuiPopupFlags.None]])
if pressed and not popup_open then
ImGui.OpenPopupEx(popup_id, 0 --[[ImGuiPopupFlags.None]])
popup_open = true
end
-- Render shape
local frame_col = ImGui.GetColorU32(hovered and 8 --[[ImGuiCol.FrameBgHovered]] or 7 --[[ImGuiCol.FrameBg]])
local value_x2 = ImMax(bb.Min.x, bb.Max.x - arrow_size)
ImGui.RenderNavCursor(bb, id)
if bit32.band(flags, 64 --[[ImGuiComboFlags.NoPreview]]) == 0 then
window.DrawList:AddRectFilled(bb.Min, ImVec2(value_x2, bb.Max.y), frame_col, style.FrameRounding, (bit32.band(flags, 32 --[[ImGuiComboFlags.NoArrowButton]]) ~= 0) and 240 --[[ImDrawFlags.RoundAll]] or 80 --[[ImDrawFlags.RoundLeft]])
end
if bit32.band(flags, 32 --[[ImGuiComboFlags.NoArrowButton]]) == 0 then
local bg_col = ImGui.GetColorU32((popup_open or hovered) and 23 --[[ImGuiCol.ButtonHovered]] or 22 --[[ImGuiCol.Button]])
local text_col = ImGui.GetColorU32(0 --[[ImGuiCol.Text]])
window.DrawList:AddRectFilled(ImVec2(value_x2, bb.Min.y), bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) and 240 --[[ImDrawFlags.RoundAll]] or 160 --[[ImDrawFlags.RoundRight]])
if value_x2 + arrow_size - style.FramePadding.x <= bb.Max.x then
ImGui.RenderArrow(window.DrawList, ImVec2(value_x2 + style.FramePadding.y, bb.Min.y + style.FramePadding.y), text_col, 3 --[[ImGuiDir.Down]], 1.0)
end
end
ImGui.RenderFrameBorder(bb.Min, bb.Max, style.FrameRounding)
-- Store geometry for BeginComboPreview() - only necessary when visible.
if bit32.band(flags, 64 --[[ImGuiComboFlags.NoPreview]]) ~= 0 then
g.ComboPreviewData.PreviewRect = ImRect()
else
g.ComboPreviewData.PreviewRect = ImRect(bb.Min.x, bb.Min.y, value_x2, bb.Max.y)
end
-- Render preview and label
if preview_value ~= nil and bit32.band(flags, 64 --[[ImGuiComboFlags.NoPreview]]) == 0 then
if g.LogEnabled then
ImGui.LogSetNextTextDecoration("{", "}")
end
ImGui.RenderTextClipped(bb.Min + style.FramePadding, ImVec2(value_x2, bb.Max.y), preview_value, nil, nil)
end
if label_size.x > 0 then
ImGui.RenderText(ImVec2(bb.Max.x + style.ItemInnerSpacing.x, bb.Min.y + style.FramePadding.y), label, 1, label_end, false)
end
if not popup_open then
return false
end
g.NextWindowData.HasFlags = backup_next_window_data_flags
return ImGui.BeginComboPopup(popup_id, bb, flags)
end
--- @param popup_id ImGuiID
--- @param bb ImRect
--- @param flags ImGuiComboFlags
function ImGui.BeginComboPopup(popup_id, bb, flags)
local g = GImGui
if not ImGui.IsPopupOpen(popup_id, 0 --[[ImGuiPopupFlags.None]]) then
g.NextWindowData:ClearFlags()
return false
end
-- Set popup size
local w = bb:GetWidth()
if bit32.band(g.NextWindowData.HasFlags, 16 --[[ImGuiNextWindowDataFlags.HasSizeConstraint]]) ~= 0 then
g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w)
else
if bit32.band(flags, 30 --[[ImGuiComboFlags.HeightMask_]]) == 0 then
flags = bit32.bor(flags, 4 --[[ImGuiComboFlags.HeightRegular]])
end
assert(ImIsPowerOfTwo(bit32.band(flags, 30 --[[ImGuiComboFlags.HeightMask_]]))) -- Only one
local popup_max_height_in_items = -1
if bit32.band(flags, 4 --[[ImGuiComboFlags.HeightRegular]]) ~= 0 then
popup_max_height_in_items = 8
elseif bit32.band(flags, 2 --[[ImGuiComboFlags.HeightSmall]]) ~= 0 then
popup_max_height_in_items = 4
elseif bit32.band(flags, 8 --[[ImGuiComboFlags.HeightLarge]]) ~= 0 then
popup_max_height_in_items = 20
end
local constraint_min = ImVec2(0.0, 0.0)
local constraint_max = ImVec2(3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]])
if bit32.band(g.NextWindowData.HasFlags, 2 --[[ImGuiNextWindowDataFlags.HasSize]]) == 0 or g.NextWindowData.SizeVal.x <= 0.0 then -- Don't apply constraints if user specified a size
constraint_min.x = w
end
if bit32.band(g.NextWindowData.HasFlags, 2 --[[ImGuiNextWindowDataFlags.HasSize]]) == 0 or g.NextWindowData.SizeVal.y <= 0.0 then
constraint_max.y = CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)
end
ImGui.SetNextWindowSizeConstraints(constraint_min, constraint_max)
end
-- This is essentially a specialized version of BeginPopupEx()
local name = ImFormatString("##Combo_%02d", g.BeginComboDepth) -- Recycle windows based on depth
-- Set position given a custom constraint (peak into expected window size so we can position it)
-- FIXME: This might be easier to express with an hypothetical SetNextWindowPosConstraints() function?
-- FIXME: This might be moved to Begin() or at least around the same spot where Tooltips and other Popups are calling FindBestWindowPosForPopupEx()?
local popup_window = ImGui.FindWindowByName(name)
if popup_window then
if popup_window.WasActive then
-- Always override 'AutoPosLastDirection' to not leave a chance for a past value to affect us.
local size_expected = ImGui.CalcWindowNextAutoFitSize(popup_window)
popup_window.AutoPosLastDirection = (bit32.band(flags, 1 --[[ImGuiComboFlags.PopupAlignLeft]]) ~= 0) and 0 --[[ImGuiDir.Left]] or 3 --[[ImGuiDir.Down]] -- Left = "Below, Toward Left", Down = "Below, Toward Right (default)"
local r_outer = ImGui.GetPopupAllowedExtentRect(popup_window)
local pos
pos, popup_window.AutoPosLastDirection = ImGui.FindBestWindowPosForPopupEx(bb:GetBL(), size_expected, popup_window.AutoPosLastDirection, r_outer, bb, 1 --[[ImGuiPopupPositionPolicy.ComboBox]])
ImGui.SetNextWindowPos(pos)
end
end
-- We don't use BeginPopupEx() solely because we have a custom name string, which we could make an argument to BeginPopupEx()
local window_flags = bit32.bor(64 --[[ImGuiWindowFlags.AlwaysAutoResize]], 67108864 --[[ImGuiWindowFlags.Popup]], 1 --[[ImGuiWindowFlags.NoTitleBar]], 2 --[[ImGuiWindowFlags.NoResize]], 256 --[[ImGuiWindowFlags.NoSavedSettings]], 4 --[[ImGuiWindowFlags.NoMove]])
ImGui.PushStyleVarX(2 --[[ImGuiStyleVar.WindowPadding]], g.Style.FramePadding.x) -- Horizontally align ourselves with the framed text
local _, ret = ImGui.Begin(name, nil, window_flags)
ImGui.PopStyleVar()
if not ret then
ImGui.EndPopup()
if not g.IO.ConfigDebugBeginReturnValueOnce and not g.IO.ConfigDebugBeginReturnValueLoop then
-- Begin may only return false with those debug tools activated.
assert(false) -- This should never happen as we tested for IsPopupOpen() above
end
return false
end
g.BeginComboDepth = g.BeginComboDepth + 1
ImRect_Copy(g.CurrentWindowStack:back().ParentLastComboPreviewRect, g.ComboPreviewData.PreviewRect)
return true
end
function ImGui.EndCombo()
local g = GImGui
g.BeginComboDepth = g.BeginComboDepth - 1
ImRect_Copy(g.ComboPreviewData.PreviewRect, g.CurrentWindowStack:back().ParentLastComboPreviewRect)
local name = ImFormatString("##Combo_%02d", g.BeginComboDepth) -- FIXME: Move those to helpers?
if g.CurrentWindow.Name ~= name then
IM_ASSERT_USER_ERROR_RET(false, "Calling EndCombo() in wrong window!")
end
ImGui.EndPopup()
end
-- Submit preview contents for BeginCombo()/EndCombo(), to display contents that's more than just a text label.
-- - [BETA] See GitHub issues: #1658, #4168.
-- - Make sure you call this after EndCombo().
-- - The preview is designed to only host non-interactive elements.
-- - Not compatible with ImGuiComboFlags.WidthFitPreview.
-- - 2026-09-08 (1.93.0): removed ImGuiComboFlags.CustomPreview. You can use BeginComboPreview()/EndComboPreview() without an extra flag.
function ImGui.BeginComboPreview()
local g = GImGui
local window = g.CurrentWindow
if window.SkipItems or bit32.band(g.LastItemData.StatusFlags, 256 --[[ImGuiItemStatusFlags.Visible]]) == 0 then
return false
end
local preview_data = g.ComboPreviewData
if not window.ClipRect:Overlaps(preview_data.PreviewRect) or preview_data.PreviewRect:GetWidth() <= 0.0 then -- Narrower test (optional) + handle _NoPreview
return false
end
if not (g.LastItemData.Rect.Min.x == preview_data.PreviewRect.Min.x and g.LastItemData.Rect.Min.y == preview_data.PreviewRect.Min.y) then -- IM_ASSERT_USER_ERROR_RETV()
IM_ASSERT_USER_ERROR(false, "Call BeginComboPreview() after BeginCombo(), not after EndCombo()!") -- Calling after EndCombo() works only if you don't nest combos.
return false
end
if preview_data.WithinPreview then
IM_ASSERT_USER_ERROR(false, "Cannot recurse BeginComboPreview(): call EndComboPreview() before opening another combo.")
return false
end
-- FIXME: This could be contained in a PushWorkRect() api
ImVec2_Copy(preview_data.BackupCursorPos, window.DC.CursorPos)
ImVec2_Copy(preview_data.BackupCursorMaxPos, window.DC.CursorMaxPos)
ImVec2_Copy(preview_data.BackupCursorPosPrevLine, window.DC.CursorPosPrevLine)
preview_data.BackupPrevLineTextBaseOffset = window.DC.PrevLineTextBaseOffset
preview_data.BackupLayout = window.DC.LayoutType
preview_data.WithinPreview = true
preview_data.BackupWorkRectMaxX = window.WorkRect.Max.x
preview_data.BackupContentRectMaxX = window.ContentRegionRect.Max.x
window.WorkRect.Max.x = preview_data.PreviewRect.Max.x - g.Style.FramePadding.x
window.ContentRegionRect.Max.x = window.WorkRect.Max.x
ImVec2_Copy(window.DC.CursorPos, preview_data.PreviewRect.Min + g.Style.FramePadding)
ImVec2_Copy(window.DC.CursorMaxPos, window.DC.CursorPos)
window.DC.LayoutType = 0 --[[ImGuiLayoutType.Horizontal]]
window.DC.IsSameLine = false
ImGui.PushClipRect(preview_data.PreviewRect.Min, preview_data.PreviewRect.Max, true)
return true
end
function ImGui.EndComboPreview()
local g = GImGui
local window = g.CurrentWindow
local preview_data = g.ComboPreviewData
-- FIXME: Using CursorMaxPos approximation instead of correct AABB which we will store in ImDrawCmd in the future
local draw_list = window.DrawList
if window.DC.CursorMaxPos.x < preview_data.PreviewRect.Max.x and window.DC.CursorMaxPos.y < preview_data.PreviewRect.Max.y then
if draw_list.CmdBuffer.Size > 1 then -- Unlikely case that the PushClipRect() didn't create a command
ImVec4_Copy(draw_list.CmdBuffer.Data[draw_list.CmdBuffer.Size].ClipRect, draw_list.CmdBuffer.Data[draw_list.CmdBuffer.Size - 1].ClipRect)
ImVec4_Copy(draw_list._CmdHeader.ClipRect, draw_list.CmdBuffer.Data[draw_list.CmdBuffer.Size].ClipRect)
draw_list:_TryMergeDrawCmds()
end
end
ImGui.PopClipRect()
ImVec2_Copy(window.DC.CursorPos, preview_data.BackupCursorPos)
ImVec2_Copy(window.DC.CursorMaxPos, ImMax(window.DC.CursorMaxPos, preview_data.BackupCursorMaxPos)) -- No need to do the same with IdealMaxPos
ImVec2_Copy(window.DC.CursorPosPrevLine, preview_data.BackupCursorPosPrevLine)
window.DC.PrevLineTextBaseOffset = preview_data.BackupPrevLineTextBaseOffset
window.WorkRect.Max.x = preview_data.BackupWorkRectMaxX
window.ContentRegionRect.Max.x = preview_data.BackupContentRectMaxX
window.DC.LayoutType = preview_data.BackupLayout
window.DC.IsSameLine = false
preview_data.WithinPreview = false
end
-- Getter for the old Combo() API: Lua array of strings (1-based), `idx` is 0-based
local function Items_ArrayGetter(data, idx)
return data[idx + 1]
end
-- Getter for the old Combo() API: "item1\0item2\0item3\0"
local function Items_SingleStringGetter(data, idx)
local items_count = 0
local p = 1
while p <= #data and string.byte(data, p) ~= 0 do
local e = string.find(data, "\0", p, true) or (#data + 1)
if idx == items_count then
return string.sub(data, p, e - 1)
end
p = e + 1
items_count = items_count + 1
end
return nil
end
local function Items_SingleStringCount(data)
local items_count = 0
local p = 1
while p <= #data and string.byte(data, p) ~= 0 do
p = (string.find(data, "\0", p, true) or #data) + 1
items_count = items_count + 1
end
return items_count
end
-- Normalizes the 3 upstream overloads to (getter, user_data, items_count, height)
-- - (items: string[], items_count?, height?)
-- - (items_separated_by_zeros: string, height?)
-- - (getter: fun(user_data, idx): string?, user_data, items_count, height?)
local function Items_ResolveArgs(a, b, c, d)
if type(a) == "function" then
return a, b, c, d
elseif type(a) == "string" then
return Items_SingleStringGetter, a, Items_SingleStringCount(a), b
else
return Items_ArrayGetter, a, b or #a, c
end
end
-- Old API, prefer using BeginCombo() nowadays if you can.
--- @param label string
--- @param current_item int # 0-based
--- @return int current_item
--- @return bool value_changed
function ImGui.Combo(label, current_item, a, b, c, d)
local g = GImGui
local getter, user_data, items_count, popup_max_height_in_items = Items_ResolveArgs(a, b, c, d)
if popup_max_height_in_items == nil then popup_max_height_in_items = -1 end
-- Call the getter to obtain the preview string which is a parameter to BeginCombo()
local preview_value = nil
if bit32.band(bit32.bor(g.NextItemData.ItemFlagsSet, g.CurrentItemFlags), 4096 --[[ImGuiItemFlags.MixedValue]]) ~= 0 then
preview_value = ""
elseif current_item >= 0 and current_item < items_count then
preview_value = getter(user_data, current_item)
end
-- The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't have/need it, but we emulate it here.
if popup_max_height_in_items ~= -1 and bit32.band(g.NextWindowData.HasFlags, 16 --[[ImGuiNextWindowDataFlags.HasSizeConstraint]]) == 0 then
ImGui.SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(3.4028234663853e+38 --[[FLT_MAX]], CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)))
end
if not ImGui.BeginCombo(label, preview_value, 0 --[[ImGuiComboFlags.None]]) then
return current_item, false
end
-- Display items
local value_changed = false
local clipper = ImGuiListClipper()
clipper:Begin(items_count)
clipper:IncludeItemByIndex(current_item)
while clipper:Step() do
for i = clipper.DisplayStart, clipper.DisplayEnd - 1 do
local item_text = getter(user_data, i)
if item_text == nil then
item_text = "*Unknown item*"
end
ImGui.PushID(i)
local item_selected = (i == current_item)
if ImGui.Selectable(item_text, item_selected) and current_item ~= i then
value_changed = true
current_item = i
end
if item_selected then
ImGui.SetItemDefaultFocus()
end
ImGui.PopID()
end
end
ImGui.EndCombo()
if value_changed then
ImGui.MarkItemEdited(g.LastItemData.ID)
end
return current_item, value_changed
end
-------------------------------------------------------------------------------
-- [SECTION] Data Type and Data Formatting Helpers [Internal]
-------------------------------------------------------------------------------
-- - DataTypeGetInfo()
-- - DataTypeFormatString()
-- - DataTypeApplyOp()
-- - DataTypeApplyFromText()
-- - DataTypeCompare()
-- - DataTypeClamp()
-- - GetMinimumStepAtDecimalPrecision
-- - RoundScalarWithFormat<>()
-------------------------------------------------------------------------------
local function float(val) return val end
local function double(val) return val end
local GDefaultRgbaColorMarkers = {
IM_COL32(240, 20, 20, 255), IM_COL32(20, 240, 20, 255), IM_COL32(20, 20, 240, 255), IM_COL32(140, 140, 140, 255)
}
--- @param size size_t
--- @param name string
--- @param print_fmt string
--- @param scan_fmt string
--- @return ImGuiDataTypeInfo
--- @nodiscard
--- @package
local function ImGuiDataTypeInfo(size, name, print_fmt, scan_fmt)
return { Size = size, Name = name, PrintFmt = print_fmt, ScanFmt = scan_fmt }
end
local GDataTypeInfo = {
ImGuiDataTypeInfo(1, "S8", "%d", "%d"), -- ImGuiDataType.S8
ImGuiDataTypeInfo(1, "U8", "%u", "%u"),
ImGuiDataTypeInfo(2, "S16", "%d", "%d"), -- ImGuiDataType.S16
ImGuiDataTypeInfo(2, "U16", "%u", "%u"),
ImGuiDataTypeInfo(4, "S32", "%d", "%d"), -- ImGuiDataType.S32
ImGuiDataTypeInfo(4, "U32", "%u", "%u"),
ImGuiDataTypeInfo(4, "float", "%.3f", "%f"), -- ImGuiDataType.Float
ImGuiDataTypeInfo(8, "double", "%f", "%lf"), -- ImGuiDataType.Double
ImGuiDataTypeInfo(1, "bool", "%d", "%d"), -- ImGuiDataType.Bool
ImGuiDataTypeInfo(0, "string", "%s", "%s") -- ImGuiDataType.String
}
--- @param data_type ImGuiDataType
function ImGui.DataTypeGetInfo(data_type)
assert(data_type >= 0 and data_type < 10 --[[ImGuiDataType.COUNT]])
return GDataTypeInfo[data_type + 1]
end
--- @param buf char[]
--- @param buf_size int
--- @param data_type ImGuiDataType
--- @param data number
--- @param format string
function ImGui.DataTypeFormatString(buf, buf_size, data_type, data, format)
local str
if data_type == 4 --[[ImGuiDataType.S32]] then
str = ImFormatString(format, (ImS32)(data))
elseif data_type == 5 --[[ImGuiDataType.U32]] then
str = ImFormatString(format, (ImU32)(data))
elseif data_type == 6 --[[ImGuiDataType.Float]] then
str = ImFormatString(format, data)
elseif data_type == 7 --[[ImGuiDataType.Double]] then
str = ImFormatString(format, data)
elseif data_type == 0 --[[ImGuiDataType.S8]] then
str = ImFormatString(format, (ImS8)(data))
elseif data_type == 1 --[[ImGuiDataType.U8]] then
str = ImFormatString(format, (ImU8)(data))
elseif data_type == 2 --[[ImGuiDataType.S16]] then
str = ImFormatString(format, (ImS16)(data))
elseif data_type == 3 --[[ImGuiDataType.U16]] then
str = ImFormatString(format, (ImU16)(data))
else
assert(false)
end
ImStd.ImStrncpy(buf, 1, { string.byte(str, 1, #str) }, 1, ImMin(#str + 1, buf_size)) -- TODO: update this when new ImFormatString is implemented
end
--- @param data_type ImGuiDataType
--- @param op int
--- @param arg1 number
--- @param arg2 number
function ImGui.DataTypeApplyOp(data_type, op, arg1, arg2)
assert(op == 43 or op == 45) -- '+' or '-'
if data_type == 0 --[[ImGuiDataType.S8]] then
if op == 43 then
return ImAddClampOverflow(arg1, arg2, IM_S8_MIN, IM_S8_MAX)
elseif op == 45 then
return ImSubClampOverflow(arg1, arg2, IM_S8_MIN, IM_S8_MAX)
end
elseif data_type == 1 --[[ImGuiDataType.U8]] then
if op == 43 then
return ImAddClampOverflow(arg1, arg2, IM_U8_MIN, IM_U8_MAX)
elseif op == 45 then
return ImSubClampOverflow(arg1, arg2, IM_U8_MIN, IM_U8_MAX)
end
elseif data_type == 2 --[[ImGuiDataType.S16]] then
if op == 43 then
return ImAddClampOverflow((ImS16)(arg1), (ImS16)(arg2), IM_S16_MIN, IM_S16_MAX)
elseif op == 45 then
return ImSubClampOverflow((ImS16)(arg1), (ImS16)(arg2), IM_S16_MIN, IM_S16_MAX)
end
elseif data_type == 3 --[[ImGuiDataType.U16]] then
if op == 43 then
return ImAddClampOverflow((ImU16)(arg1), (ImU16)(arg2), IM_U16_MIN, IM_U16_MAX)
elseif op == 45 then
return ImSubClampOverflow((ImU16)(arg1), (ImU16)(arg2), IM_U16_MIN, IM_U16_MAX)
end
elseif data_type == 4 --[[ImGuiDataType.S32]] then
if op == 43 then
return ImAddClampOverflow(arg1, arg2, IM_S32_MIN, IM_S32_MAX)
elseif op == 45 then
return ImSubClampOverflow(arg1, arg2, IM_S32_MIN, IM_S32_MAX)
end
elseif data_type == 5 --[[ImGuiDataType.U32]] then
if op == 43 then
return ImAddClampOverflow(arg1, arg2, IM_U32_MIN, IM_U32_MAX)
elseif op == 45 then
return ImSubClampOverflow(arg1, arg2, IM_U32_MIN, IM_U32_MAX)
end
elseif data_type == 6 --[[ImGuiDataType.Float]] then
if op == 43 then
return arg1 + arg2
elseif op == 45 then
return arg1 - arg2
end
elseif data_type == 7 --[[ImGuiDataType.Double]] then
if op == 43 then
return arg1 + arg2
elseif op == 45 then
return arg1 - arg2
end
end
assert(false)
end
local ImParseFormatSanitizeForScanning
-- User can input math operators (e.g. +100) to edit a numerical values.
-- NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess..
--- @param buf char[]
--- @param data_type ImGuiDataType
--- @param data number
--- @param format string
--- @param data_when_empty? number
function ImGui.DataTypeApplyFromText(buf, data_type, data, format, data_when_empty)
-- Copy the value so we can compare at the end of the function if it changed at all.
local type_info = ImGui.DataTypeGetInfo(data_type)
local data_backup = data
local p = 1
while ImCharIsBlankA(buf[p]) do
p = p + 1
end
if buf[p] == 0 then
if data_when_empty ~= nil then
data = data_when_empty
return data, data_backup ~= data
end
return data, false
end
-- Sanitize format
-- - For float/double we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in, so force them into %f and %lf
local format_sanitized, format_sanitized_size = {}, 32
if (data_type == 6 --[[ImGuiDataType.Float]] or data_type == 7 --[[ImGuiDataType.Double]]) then
format = type_info.ScanFmt
else
format = ImParseFormatSanitizeForScanning(format, format_sanitized, format_sanitized_size)
if format[1] == 0 then
format = type_info.ScanFmt -- Format doesn't want us to show the number currently, but we still need to parse the resulting input
end
end
-- Small types need a 32-bit buffer to receive the result from scanf()
local v32 = 0
local res = {}
if ImStd.sscanf(buf, 1, format, res) < 1 then
return data, false
end
if type_info.Size >= 4 then
data = res[1]
else
v32 = res[1]
end
if type_info.Size < 4 then
if data_type == 0 --[[ImGuiDataType.S8]] then
data = (ImS8)(ImClamp(v32, IM_S8_MIN, IM_S8_MAX))
elseif data_type == 1 --[[ImGuiDataType.U8]] then
data = (ImU8)(ImClamp(v32, IM_U8_MIN, IM_U8_MAX))
elseif data_type == 2 --[[ImGuiDataType.S16]] then
data = (ImS16)(ImClamp(v32, IM_S16_MIN, IM_S16_MAX))
elseif data_type == 3 --[[ImGuiDataType.U16]] then
data = (ImU16)(ImClamp(v32, IM_U16_MIN, IM_U16_MAX))
else
assert(false)
end
end
return data, data_backup ~= data
end
--- @generic T : number
--- @param lhs T
--- @param rhs T
local function DataTypeCompareT(lhs, rhs)
if lhs < rhs then return -1 end
if lhs > rhs then return 1 end
return 0
end
--- @param data_type ImGuiDataType
--- @param arg1 number
--- @param arg2 number
function ImGui.DataTypeCompare(data_type, arg1, arg2)
if data_type == 0 --[[ImGuiDataType.S8]] then return DataTypeCompareT((ImS8)(arg1), (ImS8)(arg2))
elseif data_type == 1 --[[ImGuiDataType.U8]] then return DataTypeCompareT((ImU8)(arg1), (ImU8)(arg2))
elseif data_type == 2 --[[ImGuiDataType.S16]] then return DataTypeCompareT((ImS16)(arg1), (ImS16)(arg2))
elseif data_type == 3 --[[ImGuiDataType.U16]] then return DataTypeCompareT((ImU16)(arg1), (ImU16)(arg2))
elseif data_type == 4 --[[ImGuiDataType.S32]] then return DataTypeCompareT((ImS32)(arg1), (ImS32)(arg2))
elseif data_type == 5 --[[ImGuiDataType.U32]] then return DataTypeCompareT((ImU32)(arg1), (ImU32)(arg2))
elseif data_type == 6 --[[ImGuiDataType.Float]] then return DataTypeCompareT(arg1, arg2)
elseif data_type == 7 --[[ImGuiDataType.Double]] then return DataTypeCompareT(arg1, arg2)
end
assert(false)
return 0
end
--- @generic T : number
--- @param v T
--- @param v_min? T
--- @param v_max? T
--- @return T, boolean
local function DataTypeClampT(v, v_min, v_max)
-- Clamp, both sides are optional, return true if modified
if v_min and v < v_min then return v_min, true end
if v_max and v > v_max then return v_max, true end
return v, false
end
--- @param data_type ImGuiDataType
--- @param data number
--- @param min? number
--- @param max? number
function ImGui.DataTypeClamp(data_type, data, min, max)
if data_type == 0 --[[ImGuiDataType.S8]] then return DataTypeClampT((ImS8)(data), min and (ImS8)(min), max and (ImS8)(max))
elseif data_type == 1 --[[ImGuiDataType.U8]] then return DataTypeClampT((ImU8)(data), min and (ImU8)(min), max and (ImU8)(max))
elseif data_type == 2 --[[ImGuiDataType.S16]] then return DataTypeClampT((ImS16)(data), min and (ImS16)(min), max and (ImS16)(max))
elseif data_type == 3 --[[ImGuiDataType.U16]] then return DataTypeClampT((ImU16)(data), min and (ImU16)(min), max and (ImU16)(max))
elseif data_type == 4 --[[ImGuiDataType.S32]] then return DataTypeClampT((ImS32)(data), min and (ImS32)(min), max and (ImS32)(max))
elseif data_type == 5 --[[ImGuiDataType.U32]] then return DataTypeClampT((ImU32)(data), min and (ImU32)(min), max and (ImU32)(max))
elseif data_type == 6 --[[ImGuiDataType.Float]] then return DataTypeClampT(data, min, max)
elseif data_type == 7 --[[ImGuiDataType.Double]] then return DataTypeClampT(data, min, max)
end
assert(false)
return data, false
end
--- @param data_type ImGuiDataType
--- @param data number
function ImGui.DataTypeIsZero(data_type, data)
local g = GImGui
return ImGui.DataTypeCompare(data_type, data, g.DataTypeZeroValue) == 0
end
local GetMinimumStepAtDecimalPrecision do
local min_steps = { 1.0, 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001, 0.00000001, 0.000000001 }
--- @param decimal_precision int
--- @return float
function GetMinimumStepAtDecimalPrecision(decimal_precision)
if decimal_precision < 0 then
return 1.1754943508223e-38 --[[FLT_MIN]]
end
if decimal_precision < #min_steps then
return min_steps[decimal_precision + 1]
else
return ImPow(10.0, -decimal_precision)
end
end
end
local ImParseFormatFindStart
--- @param format string
--- @param data_type ImGuiDataType
--- @param v number
function ImGui.RoundScalarWithFormatT(format, data_type, v)
-- IM_UNUSED(data_type)
assert(data_type == 6 --[[ImGuiDataType.Float]] or data_type == 7 --[[ImGuiDataType.Double]])
local fmt_start = ImParseFormatFindStart(format)
if string.byte(format, fmt_start) ~= 37 or string.byte(format, fmt_start + 1) == 37 then
return v -- Don't apply if the value is not visible in the format string
end
-- Sanitize format
local fmt_sanitized = ImParseFormatSanitizeForPrinting(string.sub(format, fmt_start), nil, 32)
-- Format value with our rounding, and read back
local v_str = ImFormatString(fmt_sanitized, v)
-- [Luau] leading spaces: ImAtof() skips them
v = ImAtof(v_str) or v
return v
end
-------------------------------------------------------------------------------
-- [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc.
-------------------------------------------------------------------------------
-- - ImParseFormatFindStart() [Internal]
-- - ImParseFormatFindEnd() [Internal]
-- - ImParseFormatTrimDecorations() [Internal]
-- - ImParseFormatSanitizeForScanning() [Internal]
-- - ImParseFormatPrecision() [Internal]
-- - TempInputText() [Internal]
-- - TempInputScalar() [Internal]
-- - InputScalar()
-- - InputScalarN()
-- - InputFloat()
-- - InputFloat2()
-- - InputFloat3()
-- - InputFloat4()
-- - InputInt()
-- - InputInt2()
-- - InputInt3()
-- - InputInt4()
-- - InputDouble()
-------------------------------------------------------------------------------
-- We don't use strchr() because our strings are usually very short and often start with '%'
--- @param fmt string
function ImParseFormatFindStart(fmt)
local len = #fmt
local i = 1
local c
while i < len do
c = string.byte(fmt, i)
if c == 37 and string.byte(fmt, i + 1) ~= 37 then -- '%'
return i
elseif c == 37 then
i = i + 1
end
i = i + 1
end
return nil
end
--- @param fmt string
--- @param pos int
local function ImParseFormatFindEnd(fmt, pos)
-- Printf/scanf types modifiers: I/L/h/j/l/t/w/z. Other uppercase letters qualify as types aka end of the format.
if string.byte(fmt, pos) ~= 37 then -- '%'
return pos
end
local ignored_uppercase_mask = bit32.bor(bit32.lshift(1, 73 - 65), bit32.lshift(1, 76 - 65))
local ignored_lowercase_mask = bit32.bor(bit32.lshift(1, 104 - 97), bit32.lshift(1, 106 - 97), bit32.lshift(1, 108 - 97), bit32.lshift(1, 116 - 97), bit32.lshift(1, 119 - 97), bit32.lshift(1, 122 - 97))
local len = #fmt
local c
for i = pos, len do
c = string.byte(fmt, i)
if c >= 65 and c <= 90 and (bit32.band(bit32.lshift(1, c - 65), ignored_uppercase_mask) == 0) then
return i + 1
end
if c >= 97 and c <= 122 and (bit32.band(bit32.lshift(1, c - 97), ignored_lowercase_mask) == 0) then
return i + 1
end
end
return len + 1
end
-- Extract the format out of a format string with leading or trailing decorations
-- fmt = "blah blah" -> return ""
-- fmt = "%.3f" -> return "%.3f"
-- fmt = "hello %.3f" -> return "%.3f"
-- fmt = "%.3f hello" -> return "%.3f"
--- @param fmt string
local function ImParseFormatTrimDecorations(fmt)
local fmt_start = ImParseFormatFindStart(fmt)
if fmt_start == nil or string.byte(fmt, fmt_start) ~= 37 then -- '%'
return ""
end
local fmt_end = ImParseFormatFindEnd(fmt, fmt_start)
if fmt_end > #fmt then -- If we only have leading decoration, we don't need to copy the data.
return string.sub(fmt, fmt_start)
end
return string.sub(fmt, fmt_start, fmt_end - 1)
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)
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"
--- @param fmt_in string
--- @param fmt_out char[]
--- @param fmt_out_size size_t
function ImParseFormatSanitizeForScanning(fmt_in, fmt_out, fmt_out_size)
local fmt_end = ImParseFormatFindEnd(fmt_in, 1)
-- IM_UNUSED(fmt_out_size)
assert(fmt_end < fmt_out_size) -- Format is too long, let us know if this happens to you!
local has_type = false
local fmt_in_begin = 1
local fmt_out_begin = 1
while fmt_in_begin < fmt_end do
repeat
local c = string.byte(fmt_in, fmt_in_begin, fmt_in_begin)
fmt_in_begin = fmt_in_begin + 1
if (not has_type and ((c >= 48 and c <= 57) or c == 46 or c == 43 or c == 35)) then
break
end
has_type = has_type or ((c >= 97 and c <= 122) or (c >= 65 and c <= 90)) -- Stop skipping digits
if c ~= 39 and c ~= 36 and c ~= 95 then -- Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '.
fmt_out[fmt_out_begin] = c
fmt_out_begin = fmt_out_begin + 1
end
until true
end
-- fmt_out[fmt_out_begin] = 0 -- Zero-terminate
return string.char(unpack(fmt_out)) -- FIXME: not ideal
end
--- @param str string
--- @param pos int
local function ImAtoi(str, pos)
local negative = false
if string.byte(str, pos) == 45 then -- '-'
negative = true
pos = pos + 1
end
if string.byte(str, pos) == 43 then -- '+'
pos = pos + 1
end
local len = #str
local v = 0
local c = string.byte(str, pos)
while c >= 48 and c <= 57 do
v = v * 10 + c - 48
pos = pos + 1
c = string.byte(str, pos)
if pos > len then break end
end
return negative and -v or v, pos
end
-- Parse display precision back from the display format string
-- FIXME: This is still used by some navigation code path to infer a minimum tweak step, but we should aim to rework widgets so it isn't needed.
--- @param fmt string
--- @param default_precision int
local function ImParseFormatPrecision(fmt, default_precision)
local pos = ImParseFormatFindStart(fmt)
if not pos then
return default_precision
end
pos = pos + 1
local len = #fmt
local c = string.byte(fmt, pos)
while c >= 48 and c <= 57 do
pos = pos + 1
c = string.byte(fmt, pos)
if pos > len then break end
end
local precision = 2147483647 --[[INT_MAX]]
if c == 46 then -- '.'
precision, pos = ImAtoi(fmt, pos + 1)
if precision < 0 or precision > 99 then
precision = default_precision
end
end
c = string.byte(fmt, pos)
if c == 101 or c == 69 then -- Maximum precision with scientific notation
precision = -1
end
if (c == 103 or c == 71) and precision == 2147483647 --[[INT_MAX]] then
precision = -1
end
return (precision == 2147483647 --[[INT_MAX]]) and default_precision or precision
end
-- Create text input in place of another active widget (e.g. used when doing a Ctrl+Click on drag/slider widgets)
-- - This must be submitted right after the item it is overlaying.
-- FIXME: Facilitate using this in variety of other situations.
--- @param bb ImRect
--- @param id ImGuiID
--- @param label string
--- @param buf char[]
--- @param buf_size int
--- @param flags? ImGuiInputTextFlags
--- @param callback? ImGuiInputTextCallback
--- @param user_data any
function ImGui.TempInputText(bb, id, label, buf, buf_size, flags, callback, user_data)
if flags == nil then flags = 0 end
-- On the first frame, g.TempInputTextId == 0, then on subsequent frames it becomes == id.
-- We clear ActiveID on the first frame to allow the InputText() taking it back.
local g = GImGui
local window = g.CurrentWindow
local is_deactivated = (g.InputTextDeactivatedState.ID == id)
local is_active = (g.TempInputId == id)
if (not is_active and not is_deactivated) then
ImGui.ClearActiveID()
end
local backup_pos = ImVec2()
ImVec2_Copy(backup_pos, window.DC.CursorPos)
ImVec2_Copy(window.DC.CursorPos, bb.Min)
g.LastItemData.ItemFlags = bit32.bor(g.LastItemData.ItemFlags, 32 --[[ImGuiItemFlags.AllowDuplicateId]]) -- Using ImGuiInputTextFlags.MergedItem above will skip ItemAdd() so we poke here.
local value_changed = ImGui.InputTextEx(label, nil, buf, buf_size, bb:GetSize(), bit32.bor(flags, 134217728 --[[ImGuiInputTextFlags.TempInput]], 4096 --[[ImGuiInputTextFlags.AutoSelectAll]]), callback, user_data)
ImGui.KeepAliveID(id) -- Not done because of ImGuiInputTextFlags.TempInput
if (not is_active and not is_deactivated) then
-- First frame we started displaying the InputText widget, we expect it to take the active id.
assert(g.ActiveId == id)
g.TempInputId = g.ActiveId
end
if is_active and g.ActiveId ~= id then
g.TempInputId = 0
end
ImVec2_Copy(window.DC.CursorPos, backup_pos)
return value_changed
end
-- Note that Drag/Slider functions are only forwarding the min/max values clamping values if the ImGuiSliderFlags.AlwaysClamp flag is set!
-- This is intended: this way we allow Ctrl+Click manual input to set a value out of bounds, for maximum flexibility.
-- However this may not be ideal for all uses, as some user code may break on out of bound values.
--- @param bb ImRect
--- @param id ImGuiID
--- @param label string
--- @param data_type ImGuiDataType
--- @param data number
--- @param format string
--- @param clamp_min? number
--- @param clamp_max? number
function ImGui.TempInputScalar(bb, id, label, data_type, data, format, clamp_min, clamp_max)
-- FIXME: May need to clarify display behavior if format doesn't contain %.
-- "%d" -> "%d" / "There are %d items" -> "%d" / "items" -> "%d" (fallback). Also see #6405
local g = GImGui
local type_info = ImGui.DataTypeGetInfo(data_type)
local data_buf, data_buf_size = {}, 32
format = ImParseFormatTrimDecorations(format)
if format == "" then
format = type_info.PrintFmt
end
ImGui.DataTypeFormatString(data_buf, data_buf_size, data_type, data, format)
ImStd.ImStrTrimBlanks(data_buf)
local flags = bit32.bor(4096 --[[ImGuiInputTextFlags.AutoSelectAll]], 268435456 --[[ImGuiInputTextFlags.LocalizeDecimalPoint]])
g.LastItemData.ItemFlags = bit32.bor(g.LastItemData.ItemFlags, 65536 --[[ImGuiItemFlags.NoMarkEdited]]) -- Because TempInputText() uses ImGuiInputTextFlags.MergedItem it doesn't submit a new item, so we poke LastItemData.
g.LastItemData.ItemFlags = (bit32.band(g.LastItemData.ItemFlags, 256 --[[ImGuiItemFlags.LiveEditOnInputScalar]]) ~= 0) and bit32.bor(g.LastItemData.ItemFlags, 128 --[[ImGuiItemFlags.LiveEditOnInputText]]) or bit32.band(g.LastItemData.ItemFlags, bit32.bnot(128 --[[ImGuiItemFlags.LiveEditOnInputText]]))
if not ImGui.TempInputText(bb, id, label, data_buf, data_buf_size, flags) then
return data, false
end
-- Backup old value
local data_backup = data
-- Apply new value (or operations) then clamp
data = ImGui.DataTypeApplyFromText(data_buf, data_type, data, format, nil)
if clamp_min or clamp_max then
if clamp_min and clamp_max and ImGui.DataTypeCompare(data_type, clamp_min, clamp_max) > 0 then
clamp_min, clamp_max = clamp_max, clamp_min
end
data = ImGui.DataTypeClamp(data_type, data, clamp_min, clamp_max)
end
-- Only mark as edited if new value is different
g.LastItemData.ItemFlags = bit32.band(g.LastItemData.ItemFlags, bit32.bnot(65536 --[[ImGuiItemFlags.NoMarkEdited]]))
local value_changed = data_backup ~= data
if bit32.band(g.LastItemData.ItemFlags, 4096 --[[ImGuiItemFlags.MixedValue]]) ~= 0 then
local state = ImGui.GetInputTextState(g.LastItemData.ID)
if state ~= nil then
value_changed = value_changed or ((bit32.band(g.LastItemData.ItemFlags, 256 --[[ImGuiItemFlags.LiveEditOnInputScalar]]) ~= 0) and (state.EditedThisFrame or state.ValidatedThisFrame) or state.ValidatedThisFrame)
end
end
if value_changed then
ImGui.MarkItemEdited(id)
end
return data, value_changed
end
-- Note: data is passed by value and the new value is returned: Returns (data, value_changed). For an Input widget, step and step_fast are optional.
-- Read code of e.g. InputFloat(), InputInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly.
--- @param label string
--- @param data_type ImGuiDataType
--- @param data number
--- @param step? number
--- @param step_fast? number
--- @param format? string
--- @param flags? ImGuiInputTextFlags
function ImGui.InputScalar(label, data_type, data, step, step_fast, format, flags)
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return data, false
end
local g = GImGui
local style = g.Style
-- IM_ASSERT((flags & ImGuiInputTextFlags.EnterReturnsTrue) == 0) -- Not supported by InputScalar(). Please open an issue if you this would be useful to you. Otherwise use IsItemDeactivatedAfterEdit()!
if format == nil then
format = ImGui.DataTypeGetInfo(data_type).PrintFmt
end
local data_default = (bit32.band(g.NextItemData.HasFlags, 8 --[[ImGuiNextItemDataFlags.HasRefVal]]) ~= 0) and g.NextItemData.RefVal or g.DataTypeZeroValue
local buf, buf_size = {}, 64 -- TODO: IM_COUNTOF()
if bit32.band(flags, 16384 --[[ImGuiInputTextFlags.DisplayEmptyRefVal]]) ~= 0 and ImGui.DataTypeCompare(data_type, data, data_default) == 0 then
buf[1] = 0
else
ImGui.DataTypeFormatString(buf, buf_size, data_type, data, format)
end
-- Disable the MarkItemEdited() call in InputText but keep ImGuiItemStatusFlags.Edited.
-- We call MarkItemEdited() ourselves by comparing the actual data rather than the string.
g.NextItemData.ItemFlagsSet = bit32.bor(g.NextItemData.ItemFlagsSet, 65536 --[[ImGuiItemFlags.NoMarkEdited]])
flags = bit32.bor(flags, 4096 --[[ImGuiInputTextFlags.AutoSelectAll]], 268435456 --[[ImGuiInputTextFlags.LocalizeDecimalPoint]])
local has_step_buttons = (step ~= nil)
local button_size = has_step_buttons and ImGui.GetFrameHeight() or 0.0
local ret
if has_step_buttons then
-- With Step Buttons
ImGui.BeginGroup() -- The only purpose of the group here is to allow the caller to query item data e.g. IsItemActive()
ImGui.PushID(label)
ImGui.SetNextItemWidth(ImMax(1.0, ImGui.CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2))
ret = ImGui.InputText("", buf, buf_size, flags) -- PushID(label) + "" gives us the expected ID from outside point of view
-- IMGUI_TEST_ENGINE_ITEM_INFO()
else
-- Without Step Buttons
ret = ImGui.InputText(label, buf, buf_size, flags)
end
-- Apply
local value_changed = false
if bit32.band(g.LastItemData.ItemFlags, 256 --[[ImGuiItemFlags.LiveEditOnInputScalar]]) ~= 0 then
local input_edited = bit32.band(g.LastItemData.StatusFlags, 2048 --[[ImGuiItemStatusFlags.EditedInternal]]) ~= 0 -- We would be using 'ret' if ImGuiInputTextFlags.EnterReturnsTrue was not involved.
if input_edited then
data, value_changed = ImGui.DataTypeApplyFromText(buf, data_type, data, format, bit32.band(flags, 8192 --[[ImGuiInputTextFlags.ParseEmptyRefVal]]) ~= 0 and data_default or nil)
end
else
-- g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_Edited
if (g.DeactivatedItemData.ID == g.LastItemData.ID) then
-- g.DeactivatedItemData.HasBeenEditedBefore = false; // Will be set below by MarkItemEdited()
-- if (IsItemDeactivated()) // Should be unnecessary
data, value_changed = ImGui.DataTypeApplyFromText(buf, data_type, data, format, bit32.band(flags, 8192 --[[ImGuiInputTextFlags.ParseEmptyRefVal]]) ~= 0 and data_default or nil)
end
end
if bit32.band(g.LastItemData.ItemFlags, 4096 --[[ImGuiItemFlags.MixedValue]]) ~= 0 then
local state = ImGui.GetInputTextState(g.LastItemData.ID)
if state ~= nil then
value_changed = value_changed or ((bit32.band(g.LastItemData.ItemFlags, 256 --[[ImGuiItemFlags.LiveEditOnInputScalar]]) ~= 0) and (state.EditedThisFrame or state.ValidatedThisFrame) or state.ValidatedThisFrame)
end
end
-- Step buttons
if has_step_buttons then
local backup_frame_padding = ImVec2()
ImVec2_Copy(backup_frame_padding, style.FramePadding)
style.FramePadding.x = style.FramePadding.y
if bit32.band(flags, 512 --[[ImGuiInputTextFlags.ReadOnly]]) ~= 0 then
ImGui.BeginDisabled()
end
ImGui.PushItemFlag(8 --[[ImGuiItemFlags.ButtonRepeat]], true)
ImGui.SameLine(0, style.ItemInnerSpacing.x)
--- @cast step number
if ImGui.ButtonEx("-", ImVec2(button_size, button_size)) then
data = ImGui.DataTypeApplyOp(data_type, 45, data, ((g.IO.KeyCtrl and step_fast) and step_fast or step))
value_changed = true
ret = true
end
ImGui.SameLine(0, style.ItemInnerSpacing.x)
if ImGui.ButtonEx("+", ImVec2(button_size, button_size)) then
data = ImGui.DataTypeApplyOp(data_type, 43, data, ((g.IO.KeyCtrl and step_fast) and step_fast or step))
value_changed = true
ret = true
end
ImGui.PopItemFlag()
if bit32.band(flags, 512 --[[ImGuiInputTextFlags.ReadOnly]]) ~= 0 then
ImGui.EndDisabled()
end
local label_end = ImGui.FindRenderedTextEnd(label)
if label_end ~= 1 then
ImGui.SameLine(0, style.ItemInnerSpacing.x)
ImGui.TextEx(label, label_end)
end
ImVec2_Copy(style.FramePadding, backup_frame_padding)
ImGui.PopID()
ImGui.EndGroup()
end
g.LastItemData.ItemFlags = bit32.band(g.LastItemData.ItemFlags, bit32.bnot(65536 --[[ImGuiItemFlags.NoMarkEdited]]))
if value_changed then
ImGui.MarkItemEdited(g.LastItemData.ID)
end
if bit32.band(flags, 64 --[[ImGuiInputTextFlags.EnterReturnsTrue]]) ~= 0 then
return data, ret
end
return data, value_changed
end
--- @param label string
--- @param data_type ImGuiDataType
--- @param data number[]
--- @param components int
--- @param step? number
--- @param step_fast? number
--- @param format? string
--- @param flags? ImGuiInputTextFlags
function ImGui.InputScalarN(label, data_type, data, components, step, step_fast, format, flags)
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
local g = GImGui
local value_changed = false
ImGui.BeginGroup()
ImGui.PushID(label)
ImGui.PushMultiItemsWidths(components, ImGui.CalcItemWidth())
for i = 1, components do
ImGui.PushID(i - 1)
if i > 1 then
ImGui.SameLine(0, g.Style.ItemInnerSpacing.x)
end
local changed
data[i], changed = ImGui.InputScalar("", data_type, data[i], step, step_fast, format, flags)
if changed then
value_changed = true
end
ImGui.PopID()
ImGui.PopItemWidth()
end
ImGui.PopID()
local label_end = ImGui.FindRenderedTextEnd(label)
if label_end > 1 then
ImGui.SameLine(0.0, g.Style.ItemInnerSpacing.x)
ImGui.TextEx(label, label_end)
end
ImGui.EndGroup()
return value_changed
end
--- @param label string
--- @param v [float, float, float]
--- @param format? string
--- @param flags? ImGuiInputTextFlags
function ImGui.InputFloat3(label, v, format, flags)
if format == nil then format = "%.3f" end
return ImGui.InputScalarN(label, 6 --[[ImGuiDataType.Float]], v, 3, nil, nil, format, flags)
end
function ImGui.InputFloat2(label, v, format, flags)
return ImGui.InputScalarN(label, 6 --[[ImGuiDataType.Float]], v, 2, nil, nil, format or "%.3f", flags)
end
function ImGui.InputFloat4(label, v, format, flags)
return ImGui.InputScalarN(label, 6 --[[ImGuiDataType.Float]], v, 4, nil, nil, format or "%.3f", flags)
end
function ImGui.InputInt2(label, v, flags)
return ImGui.InputScalarN(label, 4 --[[ImGuiDataType.S32]], v, 2, nil, nil, "%d", flags)
end
function ImGui.InputInt3(label, v, flags)
return ImGui.InputScalarN(label, 4 --[[ImGuiDataType.S32]], v, 3, nil, nil, "%d", flags)
end
function ImGui.InputInt4(label, v, flags)
return ImGui.InputScalarN(label, 4 --[[ImGuiDataType.S32]], v, 4, nil, nil, "%d", flags)
end
--- @param label string
--- @param v int
--- @param step? int
--- @param step_fast? int
--- @param flags? ImGuiInputTextFlags
function ImGui.InputInt(label, v, step, step_fast, flags)
if step == nil then step = 1 end
if step_fast == nil then step_fast = 100 end
if flags == nil then flags = 0 end
-- Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes.
local format = (bit32.band(flags, 2 --[[ImGuiInputTextFlags.CharsHexadecimal]]) ~= 0) and "%08X" or "%d"
return ImGui.InputScalar(label, 4 --[[ImGuiDataType.S32]], v, (step > 0) and step or nil, (step_fast > 0) and step_fast or nil, format, flags)
end
--- @param label string
--- @param v float
--- @param step? float
--- @param step_fast? float
--- @param format? string
--- @param flags? ImGuiInputTextFlags
function ImGui.InputFloat(label, v, step, step_fast, format, flags)
if step == nil then step = .0 end
if step_fast == nil then step_fast = .0 end
if format == nil then format = "%.3f" end
return ImGui.InputScalar(label, 6 --[[ImGuiDataType.Float]], v, (step > 0.0 and step or nil), (step_fast > 0.0 and step_fast or nil), format, flags)
end
--- @param label string
--- @param v double
--- @param step? double
--- @param step_fast? double
--- @param format? string
--- @param flags? ImGuiInputTextFlags
function ImGui.InputDouble(label, v, step, step_fast, format, flags)
if step == nil then step = .0 end
if step_fast == nil then step_fast = .0 end
if format == nil then format = "%.6f" end
return ImGui.InputScalar(label, 7 --[[ImGuiDataType.Double]], v, (step > 0.0 and step or nil), (step_fast > 0.0 and step_fast or nil), format, flags)
end
-------------------------------------------------------------------------------
-- [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc.
-------------------------------------------------------------------------------
-- - DragBehaviorT<>() [Internal]
-- - DragBehavior() [Internal]
-- - DragScalar()
-- - DragScalarN()
-- - DragFloat()
-- - DragFloat2()
-- - DragFloat3()
-- - DragFloat4()
-- - DragFloatRange2()
-- - DragInt()
-- - DragInt2()
-- - DragInt3()
-- - DragInt4()
-- - DragIntRange2()
-------------------------------------------------------------------------------
-- This is called by DragBehavior() when the widget is active (held by mouse or being manipulated with Nav controls)
--- @generic T : number
--- @param TYPE fun(val: number): T
--- @param SIGNEDTYPE fun(val: number): T
--- @param data_type ImGuiDataType
--- @param v T
--- @param v_speed float
--- @param v_min T
--- @param v_max T
--- @param format string
--- @param flags ImGuiSliderFlags
--- @return T new_v # Updated `v`
--- @return bool changed
function ImGui.DragBehaviorT(TYPE, SIGNEDTYPE, data_type, v, v_speed, v_min, v_max, format, flags)
local g = GImGui
local axis = (bit32.band(flags, 1048576 --[[ImGuiSliderFlags.Vertical]]) ~= 0) and 2 --[[ImGuiAxis.Y]] or 1 --[[ImGuiAxis.X]]
local is_bounded = (v_min < v_max) or ((v_min == v_max) and (v_min ~= 0.0 or (bit32.band(flags, 1024 --[[ImGuiSliderFlags.ClampZeroRange]]) ~= 0)))
local is_wrapped = is_bounded and (bit32.band(flags, 256 --[[ImGuiSliderFlags.WrapAround]]) ~= 0)
local is_logarithmic = bit32.band(flags, 32 --[[ImGuiSliderFlags.Logarithmic]]) ~= 0
local is_floating_point = (data_type == 6 --[[ImGuiDataType.Float]]) or (data_type == 7 --[[ImGuiDataType.Double]])
-- Default tweak speed
if v_speed == 0.0 and is_bounded and (v_max - v_min < math.huge) then
v_speed = (v_max - v_min) * g.DragSpeedDefaultRatio
end
-- Inputs accumulates into g.DragCurrentAccum, which is flushed into the current value as soon as it makes a difference with our precision settings
local adjust_delta = 0.0
if g.ActiveIdSource == 1 --[[ImGuiInputSource.Mouse]] and ImGui.IsMousePosValid() and ImGui.IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR) then
adjust_delta = g.IO.MouseDelta[axis]
if g.IO.KeyAlt and bit32.band(flags, 2048 --[[ImGuiSliderFlags.NoSpeedTweaks]]) == 0 then
adjust_delta = adjust_delta / 100.0
end
if g.IO.KeyShift and bit32.band(flags, 2048 --[[ImGuiSliderFlags.NoSpeedTweaks]]) == 0 then
adjust_delta = adjust_delta * 10.0
end
elseif g.ActiveIdSource == 2 --[[ImGuiInputSource.Keyboard]] or g.ActiveIdSource == 3 --[[ImGuiInputSource.Gamepad]] then
local decimal_precision
if is_floating_point then
decimal_precision = ImParseFormatPrecision(format, 3)
else
decimal_precision = 0
end
local slow_key = (g.NavInputSource == 3 --[[ImGuiInputSource.Gamepad]]) and 642 --[[ImGuiKey.NavGamepadTweakSlow]] or 4096 --[[ImGuiKey.NavKeyboardTweakSlow]]
local fast_key = (g.NavInputSource == 3 --[[ImGuiInputSource.Gamepad]]) and 643 --[[ImGuiKey.NavGamepadTweakFast]] or 8192 --[[ImGuiKey.NavKeyboardTweakFast]]
local tweak_factor
if bit32.band(flags, 2048 --[[ImGuiSliderFlags.NoSpeedTweaks]]) ~= 0 then
tweak_factor = 1.0
elseif ImGui.IsKeyDown(slow_key) then
tweak_factor = 1.0 / 10.0
elseif ImGui.IsKeyDown(fast_key) then
tweak_factor = 10.0
else
tweak_factor = 1.0
end
adjust_delta = ImGui.GetNavTweakPressedAmount(axis) * tweak_factor
v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision))
end
adjust_delta = adjust_delta * v_speed
-- For vertical drag we currently assume that Up=higher value (like we do with vertical sliders). This may become a parameter.
if axis == 2 --[[ImGuiAxis.Y]] then
adjust_delta = -adjust_delta
end
-- For logarithmic use our range is effectively 0..1 so scale the delta into that range
if is_logarithmic and (v_max - v_min < 3.4028234663853e+38 --[[FLT_MAX]]) and (v_max - v_min > 0.000001) then -- Epsilon to avoid /0
adjust_delta = adjust_delta / (v_max - v_min)
end
-- Clear current value on activation
-- Avoid altering values and clamping when we are _already_ past the limits and heading in the same direction, so e.g. if range is 0..255, current value is 300 and we are pushing to the right side, keep the 300.
local is_just_activated = g.ActiveIdIsJustActivated
local is_already_past_limits_and_pushing_outward = is_bounded and not is_wrapped and ((v >= v_max and adjust_delta > 0.0) or (v <= v_min and adjust_delta < 0.0))
if is_just_activated or is_already_past_limits_and_pushing_outward then
g.DragCurrentAccum = 0.0
g.DragCurrentAccumDirty = false
elseif adjust_delta ~= 0.0 then
g.DragCurrentAccum = g.DragCurrentAccum + adjust_delta
g.DragCurrentAccumDirty = true
end
if not g.DragCurrentAccumDirty then
return v, false
end
local v_cur = (TYPE)(v)
local v_old_ref_for_accum_remainder = 0.0
local logarithmic_zero_epsilon = 0.0 -- Only valid when is_logarithmic is true
local zero_deadzone_halfsize = 0.0 -- Drag widgets have no deadzone (as it doesn't make sense)
if is_logarithmic then
-- When using logarithmic sliders, we need to clamp to avoid hitting zero, but our choice of clamp value greatly affects slider precision. We attempt to use the specified precision to estimate a good lower bound.
local decimal_precision
if is_floating_point then
decimal_precision = ImParseFormatPrecision(format, 3)
else
decimal_precision = 1
end
logarithmic_zero_epsilon = ImPow(0.1, decimal_precision)
-- Convert to parametric space, apply delta, convert back
local v_old_parametric = ImGui.ScaleRatioFromValueT(data_type, v_cur, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize)
local v_new_parametric = v_old_parametric + g.DragCurrentAccum
v_cur = ImGui.ScaleValueFromRatioT(TYPE, SIGNEDTYPE, data_type, v_new_parametric, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize)
v_old_ref_for_accum_remainder = v_old_parametric
else
v_cur = (TYPE)(v_cur + (SIGNEDTYPE)(g.DragCurrentAccum))
end
-- Round to user desired precision based on format string
if is_floating_point and bit32.band(flags, 64 --[[ImGuiSliderFlags.NoRoundToFormat]]) == 0 then
v_cur = ImGui.RoundScalarWithFormatT(format, data_type, v_cur)
end
-- Preserve remainder after rounding has been applied. This also allow slow tweaking of values.
g.DragCurrentAccumDirty = false
if is_logarithmic then
-- Convert to parametric space, apply delta, convert back
local v_new_parametric = ImGui.ScaleRatioFromValueT(data_type, v_cur, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize)
g.DragCurrentAccum = g.DragCurrentAccum - (v_new_parametric - v_old_ref_for_accum_remainder)
else
g.DragCurrentAccum = g.DragCurrentAccum - ((SIGNEDTYPE)(v_cur) - (SIGNEDTYPE)(v))
end
-- Lose zero sign for float/double
if v_cur == (TYPE)(-0) then
v_cur = (TYPE)(0)
end
if v ~= v_cur and is_bounded then
if is_wrapped then
-- Wrap values
if v_cur < v_min then
v_cur = (TYPE)(v_cur + (TYPE)((TYPE)(v_max - v_min) + (is_floating_point and 0 or 1)))
end
if v_cur > v_max then
v_cur = (TYPE)(v_cur - (TYPE)((TYPE)(v_max - v_min) - (is_floating_point and 0 or 1)))
end
else
-- Clamp values + handle overflow/wrap-around for integer types.
if v_cur < v_min or (v_cur > v and adjust_delta < 0.0 and not is_floating_point) then
v_cur = (TYPE)(v_min)
end
if v_cur > v_max or (v_cur < v and adjust_delta > 0.0 and not is_floating_point) then
v_cur = (TYPE)(v_max)
end
end
end
-- Apply result
if v == v_cur then
return v, false
end
v = v_cur
return v, true
end
local function ShortcutsForCancel(id)
local g = GImGui
local is_cancel_with_keyboard = ImGui.Shortcut(526 --[[ImGuiKey.Escape]], 0 --[[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, 2 --[[ImGuiConfigFlags.NavEnableGamepad]]) ~= 0 and bit32.band(g.IO.BackendFlags, 1 --[[ImGuiBackendFlags.HasGamepad]]) ~= 0 and ImGui.Shortcut(635 --[[ImGuiKey.GamepadFaceRight]], 0 --[[ImGuiInputFlags.None]], id)
local is_cancel_with_mouse = ImGui.IsMouseReleased(1 --[[ImGuiMouseButton.Right]], id)
if is_cancel_with_mouse then
ImGui.SetKeyOwner(657 --[[ImGuiKey.MouseRight]], id)
end
return is_cancel_with_keyboard or is_cancel_with_gamepad or is_cancel_with_mouse
end
--- @param id ImGuiID
--- @param data_type ImGuiDataType
--- @param v number
--- @param v_speed float
--- @param min number
--- @param max number
--- @param format string
--- @param flags ImGuiSliderFlags
@native
function ImGui.DragBehavior(id, data_type, v, v_speed, min, max, format, flags)
-- Read imgui.cpp "API BREAKING CHANGES" section for 1.78 if you hit this assert.
assert((flags == 1 or bit32.band(flags, 1879048207 --[[ImGuiSliderFlags.InvalidMask_]]) == 0), "Invalid ImGuiSliderFlags flags! Has the legacy 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead.")
-- Things we can do easily outside the DragBehaviorT<> template, saves code generation.
local g = GImGui
if g.ActiveId == id then
if g.ActiveIdSource == 1 --[[ImGuiInputSource.Mouse]] and not g.IO.MouseDown[0] then
ImGui.ClearActiveID()
elseif (g.ActiveIdSource == 2 --[[ImGuiInputSource.Keyboard]] or g.ActiveIdSource == 3 --[[ImGuiInputSource.Gamepad]]) and g.NavActivatePressedId == id and not g.ActiveIdIsJustActivated then
ImGui.ClearActiveID()
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 g.ActiveId ~= id then
return v, false
end
if bit32.band(g.LastItemData.ItemFlags, 2048 --[[ImGuiItemFlags.ReadOnly]]) ~= 0 or bit32.band(flags, 2097152 --[[ImGuiSliderFlags.ReadOnly]]) ~= 0 then
return v, false
end
if data_type == 0 --[[ImGuiDataType.S8]] then
return ImGui.DragBehaviorT(ImS32, ImS32, 4 --[[ImGuiDataType.S32]], v, v_speed, min and min or IM_S8_MIN, max and max or IM_S8_MAX, format, flags)
elseif data_type == 1 --[[ImGuiDataType.U8]] then
return ImGui.DragBehaviorT(ImU32, ImS32, 5 --[[ImGuiDataType.U32]], v, v_speed, min and min or IM_U8_MIN, max and max or IM_U8_MAX, format, flags)
elseif data_type == 2 --[[ImGuiDataType.S16]] then
return ImGui.DragBehaviorT(ImS32, ImS32, 4 --[[ImGuiDataType.S32]], v, v_speed, min and min or IM_S16_MIN, max and max or IM_S16_MAX, format, flags)
elseif data_type == 3 --[[ImGuiDataType.U16]] then
return ImGui.DragBehaviorT(ImU32, ImS32, 5 --[[ImGuiDataType.U32]], v, v_speed, min and min or IM_U16_MIN, max and max or IM_U16_MAX, format, flags)
elseif data_type == 4 --[[ImGuiDataType.S32]] then
return ImGui.DragBehaviorT(ImS32, ImS32, data_type, v, v_speed, min and min or IM_S32_MIN, max and max or IM_S32_MAX, format, flags)
elseif data_type == 5 --[[ImGuiDataType.U32]] then
return ImGui.DragBehaviorT(ImU32, ImS32, data_type, v, v_speed, min and min or IM_U32_MIN, max and max or IM_U32_MAX, format, flags)
elseif data_type == 6 --[[ImGuiDataType.Float]] then
return ImGui.DragBehaviorT(float, float, data_type, v, v_speed, min and min or -3.4028234663853e+38 --[[FLT_MAX]], max and max or 3.4028234663853e+38 --[[FLT_MAX]], format, flags)
elseif data_type == 7 --[[ImGuiDataType.Double]] then
return ImGui.DragBehaviorT(double, double, data_type, v, v_speed, min and min or -1.7976931348623e+308 --[[DBL_MAX]], max and max or 1.7976931348623e+308 --[[DBL_MAX]], format, flags)
end
assert(false)
return v, false
end
-- Only clamp Ctrl+Click input when ImGuiSliderFlags.ClampOnInput is set (generally via ImGuiSliderFlags.AlwaysClamp)
--- @param flags ImGuiSliderFlags
--- @param data_type ImGuiDataType
--- @param min number
--- @param max number
local function TempInputIsClampEnabled(flags, data_type, min, max)
if bit32.band(flags, 512 --[[ImGuiSliderFlags.ClampOnInput]]) ~= 0 and (min ~= nil or max ~= nil) then
local clamp_range_dir = 0
if min ~= nil and max ~= nil then
clamp_range_dir = ImGui.DataTypeCompare(data_type, min, max) -- -1 when min < max, == 0 when min == max
end
if min == nil or max == nil or clamp_range_dir < 0 then
return true
end
if clamp_range_dir == 0 then
return ImGui.DataTypeIsZero(data_type, min) and (bit32.band(flags, 1024 --[[ImGuiSliderFlags.ClampZeroRange]]) ~= 0) or true
end
end
return false
end
--- @param label string
--- @param data_type ImGuiDataType
--- @param data number
--- @param v_speed float
--- @param min number
--- @param max number
--- @param format string
--- @param flags ImGuiSliderFlags
-- [Luau] memo of string.format(format, value) for drag/slider value text (same values are redrawn every frame).
-- ponytail: whole-memo reset at 4096 entries, LRU if values churn
local fmt_memo, fmt_memo_n = {}, 0
local SliderScalar_total_bb, SliderScalar_grab_bb = ImRect(), ImRect() -- [Luau] scratch for SliderScalar()/DragScalar(), never escape
local DRAG_TEXT_ALIGN = ImVec2(0.5, 0.5)
function FormatCached(format, v)
local m = fmt_memo[format]
if m == nil then m = {}; fmt_memo[format] = m end
local r = m[v]
if r == nil then
if fmt_memo_n >= 4096 then table.clear(fmt_memo); m = {}; fmt_memo[format] = m; fmt_memo_n = 0 end
r = ImFormatString(format, v); m[v] = r; fmt_memo_n = fmt_memo_n + 1
end
return r
end
-- Note: data is passed by value and the new value is returned: Returns (data, value_changed). For a Drag widget, min and max are optional.
-- Read code of e.g. DragFloat(), DragInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly.
@native
function ImGui.DragScalar(label, data_type, data, v_speed, min, max, format, flags)
if v_speed == nil then v_speed = 1.0 end
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return data, false
end
local g = GImGui
local style = g.Style
local id = window:GetID(label)
local w = ImGui.CalcItemWidth()
local color_marker = (bit32.band(g.NextItemData.HasFlags, 32 --[[ImGuiNextItemDataFlags.HasColorMarker]]) ~= 0) and g.NextItemData.ColorMarker or 0
local label_end = ImGui.FindRenderedTextEnd(label)
local label_size = ImGui.CalcTextSize(label, label_end, false)
local cursor = window.DC.CursorPos
local frame_bb = ImRect(cursor.x, cursor.y, cursor.x + w, cursor.y + (label_size.y + style.FramePadding.y * 2.0)) -- [Luau] no temporary ImVec2
local total_bb = SliderScalar_total_bb -- [Luau] scratch: ItemSize()/ItemAdd() only read it
total_bb.Min.x = frame_bb.Min.x; total_bb.Min.y = frame_bb.Min.y
total_bb.Max.x = frame_bb.Max.x + ((label_size.x > 0.0) and (style.ItemInnerSpacing.x + label_size.x) or 0.0); total_bb.Max.y = frame_bb.Max.y + 0.0
local temp_input_allowed = (bit32.band(flags, 128 --[[ImGuiSliderFlags.NoInput]]) == 0)
ImGui.ItemSize(total_bb, style.FramePadding.y)
if not ImGui.ItemAdd(total_bb, id, frame_bb, temp_input_allowed and 1048576 --[[ImGuiItemFlags.Inputable]] or 0) then
return data, false
end
-- Default format string when passing NULL
if format == nil then
format = ImGui.DataTypeGetInfo(data_type).PrintFmt
end
local hovered = ImGui.ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags)
local temp_input_is_active = temp_input_allowed and ImGui.TempInputIsActive(id)
if not temp_input_is_active then
-- Tabbing or Ctrl+Click on Drag turns it into an InputText
local clicked = hovered and ImGui.IsMouseClickedEx(0, 0 --[[ImGuiInputFlags.None]], id)
local double_clicked = (hovered and g.IO.MouseClickedCount[0] == 2 and ImGui.TestKeyOwner(656 --[[ImGuiKey.MouseLeft]], id))
local make_active = (clicked or double_clicked or g.NavActivateId == id)
if make_active and (clicked or double_clicked) then
ImGui.SetKeyOwner(656 --[[ImGuiKey.MouseLeft]], id)
end
if make_active and temp_input_allowed then
if (clicked and g.IO.KeyCtrl) or double_clicked or (g.NavActivateId == id and bit32.band(g.NavActivateFlags, 1 --[[ImGuiActivateFlags.PreferInput]]) ~= 0) then
temp_input_is_active = true
end
end
-- (Optional) simple click (without moving) turns Drag into an InputText
if g.IO.ConfigDragClickToInputText and temp_input_allowed and not temp_input_is_active then
if g.ActiveId == id and hovered and g.IO.MouseReleased[0] and not ImGui.IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR) then
g.NavActivateId = id
g.NavActivateFlags = 1 --[[ImGuiActivateFlags.PreferInput]]
temp_input_is_active = true
end
end
-- Store initial value (not used by main lib but available as a convenience but some mods e.g. to revert)
if make_active then
g.ActiveIdValueOnActivation = data
end
if make_active and not temp_input_is_active then
ImGui.SetActiveID(id, window)
ImGui.SetFocusID(id, window)
ImGui.FocusWindow(window)
g.ActiveIdUsingNavDirMask = bit32.bor(bit32.lshift(1, 0 --[[ImGuiDir.Left]]), bit32.lshift(1, 1 --[[ImGuiDir.Right]]))
end
end
if temp_input_is_active then
local clamp_enabled = TempInputIsClampEnabled(flags, data_type, min, max)
return ImGui.TempInputScalar(frame_bb, id, label, data_type, data, format, clamp_enabled and min or nil, clamp_enabled and max or nil)
end
-- Draw frame
local frame_col = ImGui.GetColorU32(g.ActiveId == id and 9 --[[ImGuiCol.FrameBgActive]] or hovered and 8 --[[ImGuiCol.FrameBgHovered]] or 7 --[[ImGuiCol.FrameBg]])
ImGui.RenderNavCursor(frame_bb, id)
ImGui.RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, false, style.FrameRounding)
if color_marker ~= 0 and style.ColorMarkerSize > 0.0 then
ImGui.RenderColorComponentMarker(frame_bb, ImGui.GetColorU32(color_marker, nil, true), style.FrameRounding)
end
ImGui.RenderFrameBorder(frame_bb.Min, frame_bb.Max, g.Style.FrameRounding)
-- Drag behavior
local value_changed
data, value_changed = ImGui.DragBehavior(id, data_type, data, v_speed, min, max, format, flags)
if value_changed then
ImGui.MarkItemEdited(id)
end
-- TODO: ImGui.DataTypeFormatString() should be used here instead of ImFormatString()
-- Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
ImGui.RenderTextClipped(frame_bb.Min, frame_bb.Max, FormatCached(format, data), nil, nil, DRAG_TEXT_ALIGN)
if label_size.x > 0.0 then
ImGui.RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label, 1, label_end, false)
end
return data, value_changed
end
--- @param label string
--- @param v float
--- @param v_speed? float
--- @param v_min? float
--- @param v_max? float
--- @param format? string
--- @param flags? ImGuiSliderFlags
function ImGui.DragFloat(label, v, v_speed, v_min, v_max, format, flags)
if v_speed == nil then v_speed = 1.0 end
if v_min == nil then v_min = 0.0 end
if v_max == nil then v_max = 0.0 end
if format == nil then format = "%.3f" end
if flags == nil then flags = 0 end
return ImGui.DragScalar(label, 6 --[[ImGuiDataType.Float]], v, v_speed, v_min, v_max, format, flags)
end
-- NB: v_speed is float to allow adjusting the drag speed with more precision
--- @param label string
--- @param v int
--- @param v_speed? float
--- @param v_min? int
--- @param v_max? int
--- @param format? string
--- @param flags? ImGuiSliderFlags
@native
function ImGui.DragInt(label, v, v_speed, v_min, v_max, format, flags)
if v_speed == nil then v_speed = 1.0 end
if v_min == nil then v_min = 0 end
if v_max == nil then v_max = 0 end
if format == nil then format = "%d" end
if flags == nil then flags = 0 end
return ImGui.DragScalar(label, 4 --[[ImGuiDataType.S32]], v, v_speed, v_min, v_max, format, flags)
end
-- `v` is mutated in place (1-based), returns value_changed
function ImGui.DragScalarN(label, data_type, v, components, v_speed, v_min, v_max, format, flags)
if v_speed == nil then v_speed = 1.0 end
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
local g = GImGui
local value_changed = false
ImGui.BeginGroup()
ImGui.PushID(label)
ImGui.PushMultiItemsWidths(components, ImGui.CalcItemWidth())
for i = 1, components do
ImGui.PushID(i - 1)
if i > 1 then
ImGui.SameLine(0, g.Style.ItemInnerSpacing.x)
end
if bit32.band(flags, 4096 --[[ImGuiSliderFlags.ColorMarkers]]) ~= 0 then
ImGui.SetNextItemColorMarker(GDefaultRgbaColorMarkers[i])
end
local changed
v[i], changed = ImGui.DragScalar("", data_type, v[i], v_speed, v_min, v_max, format, flags)
value_changed = value_changed or changed
ImGui.PopID()
ImGui.PopItemWidth()
end
ImGui.PopID()
local label_end = ImGui.FindRenderedTextEnd(label)
if label_end > 1 then
ImGui.SameLine(0, g.Style.ItemInnerSpacing.x)
ImGui.TextEx(label, label_end)
end
ImGui.EndGroup()
return value_changed
end
local function DragFloatN(n)
return function(label, v, v_speed, v_min, v_max, format, flags)
return ImGui.DragScalarN(label, 6 --[[ImGuiDataType.Float]], v, n, v_speed or 1.0, v_min or 0.0, v_max or 0.0, format or "%.3f", flags or 0)
end
end
local function DragIntN(n)
return function(label, v, v_speed, v_min, v_max, format, flags)
return ImGui.DragScalarN(label, 4 --[[ImGuiDataType.S32]], v, n, v_speed or 1.0, v_min or 0, v_max or 0, format or "%d", flags or 0)
end
end
ImGui.DragFloat2 = DragFloatN(2)
ImGui.DragFloat3 = DragFloatN(3)
ImGui.DragFloat4 = DragFloatN(4)
ImGui.DragInt2 = DragIntN(2)
ImGui.DragInt3 = DragIntN(3)
ImGui.DragInt4 = DragIntN(4)
-- NB: You likely want to specify the ImGuiSliderFlags.AlwaysClamp when using this.
--- @return float v_current_min
--- @return float v_current_max
--- @return bool value_changed
local function DragRange2(data_type, label, v_current_min, v_current_max, v_speed, v_min, v_max, format, format_max, flags)
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return v_current_min, v_current_max, false
end
local g = GImGui
local lo = (data_type == 6 --[[ImGuiDataType.Float]]) and -3.4028234663853e+38 --[[FLT_MAX]] or (-2147483648) --[[INT_MIN]]
local hi = (data_type == 6 --[[ImGuiDataType.Float]]) and 3.4028234663853e+38 --[[FLT_MAX]] or 2147483647 --[[INT_MAX]]
ImGui.PushID(label)
ImGui.BeginGroup()
ImGui.PushMultiItemsWidths(2, ImGui.CalcItemWidth())
local min_min = (v_min >= v_max) and lo or v_min
local min_max = (v_min >= v_max) and v_current_max or ImMin(v_max, v_current_max)
local min_flags = bit32.bor(flags, (min_min == min_max) and 2097152 --[[ImGuiSliderFlags.ReadOnly]] or 0)
local value_changed, changed
v_current_min, value_changed = ImGui.DragScalar("##min", data_type, v_current_min, v_speed, min_min, min_max, format, min_flags)
ImGui.PopItemWidth()
ImGui.SameLine(0, g.Style.ItemInnerSpacing.x)
local max_min = (v_min >= v_max) and v_current_min or ImMax(v_min, v_current_min)
local max_max = (v_min >= v_max) and hi or v_max
local max_flags = bit32.bor(flags, (max_min == max_max) and 2097152 --[[ImGuiSliderFlags.ReadOnly]] or 0)
v_current_max, changed = ImGui.DragScalar("##max", data_type, v_current_max, v_speed, max_min, max_max, format_max or format, max_flags)
value_changed = value_changed or changed
ImGui.PopItemWidth()
ImGui.SameLine(0, g.Style.ItemInnerSpacing.x)
ImGui.TextEx(label, ImGui.FindRenderedTextEnd(label))
ImGui.EndGroup()
ImGui.PopID()
return v_current_min, v_current_max, value_changed
end
function ImGui.DragFloatRange2(label, v_current_min, v_current_max, v_speed, v_min, v_max, format, format_max, flags)
return DragRange2(6 --[[ImGuiDataType.Float]], label, v_current_min, v_current_max, v_speed or 1.0, v_min or 0.0, v_max or 0.0, format or "%.3f", format_max, flags or 0)
end
function ImGui.DragIntRange2(label, v_current_min, v_current_max, v_speed, v_min, v_max, format, format_max, flags)
return DragRange2(4 --[[ImGuiDataType.S32]], label, v_current_min, v_current_max, v_speed or 1.0, v_min or 0, v_max or 0, format or "%d", format_max, flags or 0)
end
-------------------------------------------------------------------------------
-- [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc.
-------------------------------------------------------------------------------
-- - ScaleRatioFromValueT<> [Internal]
-- - ScaleValueFromRatioT<> [Internal]
-- - SliderBehaviorT<>() [Internal]
-- - SliderBehavior() [Internal]
-- - SliderScalar()
-- - SliderScalarN()
-- - SliderFloat()
-- - SliderFloat2()
-- - SliderFloat3()
-- - SliderFloat4()
-- - SliderAngle()
-- - SliderInt()
-- - SliderInt2()
-- - SliderInt3()
-- - SliderInt4()
-- - VSliderScalar()
-- - VSliderFloat()
-- - VSliderInt()
-------------------------------------------------------------------------------
-- Convert a value v in the output space of a slider into a parametric position on the slider itself (the logical opposite of ScaleValueFromRatioT)
--- @param data_type ImGuiDataType
--- @param v number
--- @param v_min number
--- @param v_max number
--- @param logarithmic_zero_epsilon float
--- @param zero_deadzone_halfsize float
function ImGui.ScaleRatioFromValueT(data_type, v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize)
if v_min == v_max then
return 0.0
end
-- IM_UNUSED(data_type)
local v_clamped
if v_min < v_max then
v_clamped = ImClamp(v, v_min, v_max)
else
v_clamped = ImClamp(v, v_max, v_min)
end
if logarithmic_zero_epsilon > 0.0 then -- == is_logarithmic from caller
local flipped = v_max < v_min
if flipped then -- Handle the case where the range is backwards
v_min, v_max = v_max, v_min
end
-- Fudge min/max to avoid getting close to log(0)
local v_min_fudged
if ImAbs(v_min) < logarithmic_zero_epsilon then
v_min_fudged = (v_min < 0.0) and -logarithmic_zero_epsilon or logarithmic_zero_epsilon
else
v_min_fudged = v_min
end
local v_max_fudged
if ImAbs(v_max) < logarithmic_zero_epsilon then
v_max_fudged = (v_max < 0.0) and -logarithmic_zero_epsilon or logarithmic_zero_epsilon
else
v_max_fudged = v_max
end
-- Awkward special cases - we need ranges of the form (-100 .. 0) to convert to (-100 .. -epsilon), not (-100 .. epsilon)
if v_min == 0.0 and v_max < 0.0 then
v_min_fudged = -logarithmic_zero_epsilon
elseif v_max == 0.0 and v_min < 0.0 then
v_max_fudged = -logarithmic_zero_epsilon
end
local result
if v_clamped <= v_min_fudged then
result = 0.0 -- Workaround for values that are in-range but below our fudge
elseif v_clamped >= v_max_fudged then
result = 1.0 -- Workaround for values that are in-range but above our fudge
elseif v_min * v_max < 0.0 then -- Range crosses zero, so split into two portions
local zero_point_center = (-v_min) / (v_max - v_min) -- The zero point in parametric space. There's an argument we should take the logarithmic nature into account when calculating this, but for now this should do (and the most common case of a symmetrical range works fine)
local zero_point_snap_L = zero_point_center - zero_deadzone_halfsize
local zero_point_snap_R = zero_point_center + zero_deadzone_halfsize
if v == 0.0 then
result = zero_point_center -- Special case for exactly zero
elseif v < 0.0 then
result = (1.0 - (ImLog(-v_clamped / logarithmic_zero_epsilon) / ImLog(-v_min_fudged / logarithmic_zero_epsilon))) * zero_point_snap_L
else
result = zero_point_snap_R + ((ImLog(v_clamped / logarithmic_zero_epsilon) / ImLog(v_max_fudged / logarithmic_zero_epsilon)) * (1.0 - zero_point_snap_R))
end
elseif v_min < 0.0 or v_max < 0.0 then -- Entirely negative slider
result = 1.0 - (ImLog(-v_clamped / -v_max_fudged) / ImLog(-v_min_fudged / -v_max_fudged))
else
result = ImLog(v_clamped / v_min_fudged) / ImLog(v_max_fudged / v_min_fudged)
end
return flipped and (1.0 - result) or result
else
-- Linear slider
return (v_clamped - v_min) / (v_max - v_min)
end
end
-- Convert a parametric position on a slider into a value v in the output space (the logical opposite of ScaleRatioFromValueT)
--- @param TYPE fun(val: number): number
--- @param SIGNEDTYPE fun(val: number): number
--- @param data_type ImGuiDataType
--- @param t float
--- @param v_min number
--- @param v_max number
--- @param logarithmic_zero_epsilon float
--- @param zero_deadzone_halfsize float
function ImGui.ScaleValueFromRatioT(TYPE, SIGNEDTYPE, data_type, t, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize)
-- We special-case the extents because otherwise our logarithmic fudging can lead to "mathematically correct"
-- but non-intuitive behaviors like a fully-left slider not actually reaching the minimum value. Also generally simpler.
if t <= 0.0 or v_min == v_max then
return v_min
end
if t >= 1.0 then
return v_max
end
local result = 0
if logarithmic_zero_epsilon > 0.0 then -- == is_logarithmic from caller
-- Fudge min/max to avoid getting silly results close to zero
local v_min_fudged
if ImAbs(v_min) < logarithmic_zero_epsilon then
v_min_fudged = (v_min < 0.0) and -logarithmic_zero_epsilon or logarithmic_zero_epsilon
else
v_min_fudged = v_min
end
local v_max_fudged
if ImAbs(v_max) < logarithmic_zero_epsilon then
v_max_fudged = (v_max < 0.0) and -logarithmic_zero_epsilon or logarithmic_zero_epsilon
else
v_max_fudged = v_max
end
local flipped = v_max < v_min -- Check if range is "backwards"
if flipped then
v_min_fudged, v_max_fudged = v_max_fudged, v_min_fudged
end
-- Awkward special case - we need ranges of the form (-100 .. 0) to convert to (-100 .. -epsilon), not (-100 .. epsilon)
if v_max == 0.0 and v_min < 0.0 then
v_max_fudged = -logarithmic_zero_epsilon
end
local t_with_flip = flipped and (1.0 - t) or t -- t, but flipped if necessary to account for us flipping the range
if v_min * v_max < 0.0 then -- Range crosses zero, so we have to do this in two parts
local zero_point_center = (-ImMin(v_min, v_max)) / ImAbs(v_max - v_min) -- The zero point in parametric space
local zero_point_snap_L = zero_point_center - zero_deadzone_halfsize
local zero_point_snap_R = zero_point_center + zero_deadzone_halfsize
if t_with_flip >= zero_point_snap_L and t_with_flip <= zero_point_snap_R then
result = (TYPE)(0.0) -- Special case to make getting exactly zero possible (the epsilon prevents it otherwise)
elseif t_with_flip < zero_point_center then
result = (TYPE)(-(logarithmic_zero_epsilon * ImPow(-v_min_fudged / logarithmic_zero_epsilon, 1.0 - (t_with_flip / zero_point_snap_L))))
else
result = (TYPE)(logarithmic_zero_epsilon * ImPow(v_max_fudged / logarithmic_zero_epsilon, (t_with_flip - zero_point_snap_R) / (1.0 - zero_point_snap_R)))
end
elseif v_min < 0.0 or v_max < 0.0 then -- Entirely negative slider
result = (TYPE)(-(-v_max_fudged * ImPow(-v_min_fudged / -v_max_fudged, 1.0 - t_with_flip)))
else
result = (TYPE)(v_min_fudged * ImPow(v_max_fudged / v_min_fudged, t_with_flip))
end
else
-- Linear slider
local is_floating_point = (data_type == 6 --[[ImGuiDataType.Float]]) or (data_type == 7 --[[ImGuiDataType.Double]])
if is_floating_point then
result = ImLerp(v_min, v_max, t)
elseif t < 1.0 then -- t is already < 1.0 here
-- - For integer values we want the clicking position to match the grab box so we round above
-- This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property..
-- - Not doing a *1.0 multiply at the end of a range as it tends to be lossy. While absolute aiming at a large s64/u64
-- range is going to be imprecise anyway, with this check we at least make the edge values matches expected limits.
local v_new_off_f = (SIGNEDTYPE)((v_max - v_min)) * t
result = (TYPE)((SIGNEDTYPE)(v_min) + (SIGNEDTYPE)(v_new_off_f + 0)) -- LUA: + 0 instead of + ((v_min > v_max) and -0.5 or 0.5)
end
end
return result
end
-- FIXME: Try to move more of the code into shared SliderBehavior()
--- @generic T : number
--- @param TYPE fun(val: number): T
--- @param SIGNEDTYPE fun(val: number): T
--- @param bb ImRect
--- @param id ImGuiID
--- @param data_type ImGuiDataType
--- @param v T
--- @param v_min T
--- @param v_max T
--- @param format string
--- @param flags ImGuiSliderFlags
--- @param out_grab_bb ImRect
--- @return T v # updated `v` passed in
--- @return bool value_changed
function ImGui.SliderBehaviorT(TYPE, SIGNEDTYPE, bb, id, data_type, v, v_min, v_max, format, flags, out_grab_bb)
local g = GImGui
local style = g.Style
local axis = (bit32.band(flags, 1048576 --[[ImGuiSliderFlags.Vertical]]) ~= 0) and 2 --[[ImGuiAxis.Y]] or 1 --[[ImGuiAxis.X]]
local axis_k = (axis == 1 --[[ImGuiAxis.X]]) and "x" or "y" -- [Luau] named key: ImVec2[axis] goes through IM_VEC2.__index
local is_logarithmic = bit32.band(flags, 32 --[[ImGuiSliderFlags.Logarithmic]]) ~= 0
local is_floating_point = (data_type == 6 --[[ImGuiDataType.Float]]) or (data_type == 7 --[[ImGuiDataType.Double]])
local v_range_f = (v_min < v_max) and (v_max - v_min) or (v_min - v_max) -- We don't need high precision for what we do with it.
-- Calculate bounds
local grab_padding = 2.0 -- FIXME: Should be part of style.
local slider_sz = (bb.Max[axis_k] - bb.Min[axis_k]) - grab_padding * 2.0
local grab_sz = style.GrabMinSize
if not is_floating_point and v_range_f >= 0.0 then -- v_range_f < 0 may happen on integer overflows
grab_sz = ImMax(slider_sz / (v_range_f + 1), style.GrabMinSize) -- For integer sliders: if possible have the grab size represent 1 unit
end
grab_sz = ImMin(grab_sz, slider_sz)
local slider_usable_sz = slider_sz - grab_sz
local slider_usable_pos_min = bb.Min[axis_k] + grab_padding + grab_sz * 0.5
local slider_usable_pos_max = bb.Max[axis_k] - grab_padding - grab_sz * 0.5
local logarithmic_zero_epsilon = 0.0 -- Only valid when is_logarithmic is true
local zero_deadzone_halfsize = 0.0 -- Only valid when is_logarithmic is true
if is_logarithmic then
-- When using logarithmic sliders, we need to clamp to avoid hitting zero, but our choice of clamp value greatly affects slider precision. We attempt to use the specified precision to estimate a good lower bound.
local decimal_precision = is_floating_point and ImParseFormatPrecision(format, 3) or 1
logarithmic_zero_epsilon = ImPow(0.1, decimal_precision)
zero_deadzone_halfsize = (style.LogSliderDeadzone * 0.5) / ImMax(slider_usable_sz, 1.0)
end
-- Process interacting with the slider
local value_changed = false
if g.ActiveId == id then
local set_new_value = false
local clicked_t = 0.0
if g.ActiveIdSource == 1 --[[ImGuiInputSource.Mouse]] then
do
local mouse_abs_pos = g.IO.MousePos[axis_k]
if g.ActiveIdIsJustActivated then
local grab_t = ImGui.ScaleRatioFromValueT(data_type, v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize)
if axis == 2 --[[ImGuiAxis.Y]] then
grab_t = 1.0 - grab_t
end
local grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t)
local clicked_around_grab = (mouse_abs_pos >= grab_pos - grab_sz * 0.5 - 1.0) and (mouse_abs_pos <= grab_pos + grab_sz * 0.5 + 1.0) -- No harm being extra generous here.
g.SliderGrabClickOffset = (clicked_around_grab and is_floating_point) and (mouse_abs_pos - grab_pos) or 0.0
end
if slider_usable_sz > 0.0 then
clicked_t = ImSaturate((mouse_abs_pos - g.SliderGrabClickOffset - slider_usable_pos_min) / slider_usable_sz)
end
if axis == 2 --[[ImGuiAxis.Y]] then
clicked_t = 1.0 - clicked_t
end
set_new_value = true
end
elseif g.ActiveIdSource == 2 --[[ImGuiInputSource.Keyboard]] or g.ActiveIdSource == 3 --[[ImGuiInputSource.Gamepad]] then
local input_delta = (axis == 1 --[[ImGuiAxis.X]]) and ImGui.GetNavTweakPressedAmount(axis) or -ImGui.GetNavTweakPressedAmount(axis)
if input_delta ~= 0.0 then
local tweak_slow = ImGui.IsKeyDown((g.NavInputSource == 3 --[[ImGuiInputSource.Gamepad]]) and 642 --[[ImGuiKey.NavGamepadTweakSlow]] or 4096 --[[ImGuiKey.NavKeyboardTweakSlow]])
local tweak_fast = ImGui.IsKeyDown((g.NavInputSource == 3 --[[ImGuiInputSource.Gamepad]]) and 643 --[[ImGuiKey.NavGamepadTweakFast]] or 8192 --[[ImGuiKey.NavKeyboardTweakFast]])
local decimal_precision = is_floating_point and ImParseFormatPrecision(format, 3) or 0
if decimal_precision > 0 then
input_delta = input_delta / 100.0 -- Keyboard/Gamepad tweak speeds in % of slider bounds
if tweak_slow then
input_delta = input_delta / 10.0
end
else
if (v_range_f >= -100.0 and v_range_f <= 100.0 and v_range_f ~= 0.0) or tweak_slow then
input_delta = ((input_delta < 0.0) and -1.0 or 1.0) / v_range_f -- Keyboard/Gamepad tweak speeds in integer steps
else
input_delta = input_delta / 100.0
end
end
if tweak_fast then
input_delta = input_delta * 10.0
end
g.SliderCurrentAccum = g.SliderCurrentAccum + input_delta
g.SliderCurrentAccumDirty = true
end
local delta = g.SliderCurrentAccum
if g.SliderCurrentAccumDirty then
clicked_t = ImGui.ScaleRatioFromValueT(data_type, v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize)
if (clicked_t >= 1.0 and delta > 0.0) or (clicked_t <= 0.0 and delta < 0.0) then -- This is to avoid applying the saturation when already past the limits
set_new_value = false
g.SliderCurrentAccum = 0.0 -- If pushing up against the limits, don't continue to accumulate
else
set_new_value = true
local old_clicked_t = clicked_t
clicked_t = ImSaturate(clicked_t + delta)
-- Calculate what our "new" clicked_t will be, and thus how far we actually moved the slider, and subtract this from the accumulator
local v_new = ImGui.ScaleValueFromRatioT(TYPE, SIGNEDTYPE, data_type, clicked_t, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize)
if is_floating_point and bit32.band(flags, 64 --[[ImGuiSliderFlags.NoRoundToFormat]]) == 0 then
v_new = ImGui.RoundScalarWithFormatT(format, data_type, v_new)
end
local new_clicked_t = ImGui.ScaleRatioFromValueT(data_type, v_new, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize)
if delta > 0 then
g.SliderCurrentAccum = g.SliderCurrentAccum - ImMin(new_clicked_t - old_clicked_t, delta)
else
g.SliderCurrentAccum = g.SliderCurrentAccum - ImMax(new_clicked_t - old_clicked_t, delta)
end
end
g.SliderCurrentAccumDirty = false
end
end
if set_new_value then
if (bit32.band(g.LastItemData.ItemFlags, 2048 --[[ImGuiItemFlags.ReadOnly]]) ~= 0) or (bit32.band(flags, 2097152 --[[ImGuiSliderFlags.ReadOnly]]) ~= 0) then
set_new_value = false
end
end
if set_new_value then
local v_new = ImGui.ScaleValueFromRatioT(TYPE, SIGNEDTYPE, data_type, clicked_t, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize)
-- Round to user desired precision based on format string
if is_floating_point and bit32.band(flags, 64 --[[ImGuiSliderFlags.NoRoundToFormat]]) == 0 then
v_new = ImGui.RoundScalarWithFormatT(format, data_type, v_new)
end
-- Apply result
if v ~= v_new then
v = v_new
value_changed = true
end
end
end
-- [Luau] write out_grab_bb fields directly, no temporary ImRect
local og_min, og_max = out_grab_bb.Min, out_grab_bb.Max
if slider_sz < 1.0 then
og_min.x = bb.Min.x; og_min.y = bb.Min.y; og_max.x = bb.Min.x; og_max.y = bb.Min.y
else
-- Output grab position so it can be displayed by the caller
local grab_t = ImGui.ScaleRatioFromValueT(data_type, v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize)
if axis == 2 --[[ImGuiAxis.Y]] then
grab_t = 1.0 - grab_t
end
local grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t)
if axis == 1 --[[ImGuiAxis.X]] then
og_min.x = grab_pos - grab_sz * 0.5; og_min.y = bb.Min.y + grab_padding; og_max.x = grab_pos + grab_sz * 0.5; og_max.y = bb.Max.y - grab_padding
else
og_min.x = bb.Min.x + grab_padding; og_min.y = grab_pos - grab_sz * 0.5; og_max.x = bb.Max.x - grab_padding; og_max.y = grab_pos + grab_sz * 0.5
end
end
return v, value_changed
end
-- For 32-bit and larger types, slider bounds are limited to half the natural type range.
-- So e.g. an integer Slider between INT_MAX-10 and INT_MAX will fail, but an integer Slider between INT_MAX/2-10 and INT_MAX/2 will be ok.
-- It would be possible to lift that limitation with some work but it doesn't seem to be worth it for sliders.
--- @generic T : number
--- @param bb ImRect
--- @param id ImGuiID
--- @param data_type ImGuiDataType
--- @param v T
--- @param min T
--- @param max T
--- @param format string
--- @param flags ImGuiSliderFlags
--- @param out_grab_bb ImRect
--- @return T v
--- @return bool value_changed
function ImGui.SliderBehavior(bb, id, data_type, v, min, max, format, flags, out_grab_bb)
-- Read imgui.cpp "API BREAKING CHANGES" section for 1.78 if you hit this assert.
assert((flags == 1 or bit32.band(flags, 1879048207 --[[ImGuiSliderFlags.InvalidMask_]]) == 0), "Invalid ImGuiSliderFlags flags! Has the legacy 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead.")
assert(bit32.band(flags, 256 --[[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 == 1 --[[ImGuiInputSource.Mouse]] and not g.IO.MouseDown[0] then
ImGui.ClearActiveID()
end
if g.ActiveIdSource == 2 --[[ImGuiInputSource.Keyboard]] or g.ActiveIdSource == 3 --[[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 == 0 --[[ImGuiDataType.S8]] then
local v32 = (ImS32)((ImS8)(v)); local r
v32, r = ImGui.SliderBehaviorT(ImS32, ImS32, bb, id, 4 --[[ImGuiDataType.S32]], v32, min, max, format, flags, out_grab_bb)
if r then v = (ImS8)(v32) end
return v, r
elseif data_type == 1 --[[ImGuiDataType.U8]] then
local v32 = (ImU32)((ImU8)(v)); local r
v32, r = ImGui.SliderBehaviorT(ImU32, ImS32, bb, id, 5 --[[ImGuiDataType.U32]], v32, min, max, format, flags, out_grab_bb)
if r then v = (ImU8)(v32) end
return v, r
elseif data_type == 2 --[[ImGuiDataType.S16]] then
local v32 = (ImS32)((ImS16)(v)); local r
v32, r = ImGui.SliderBehaviorT(ImS32, ImS32, bb, id, 4 --[[ImGuiDataType.S32]], v32, min, max, format, flags, out_grab_bb)
if r then v = (ImS16)(v32) end
return v, r
elseif data_type == 3 --[[ImGuiDataType.U16]] then
local v32 = (ImU32)((ImU16)(v)); local r
v32, r = ImGui.SliderBehaviorT(ImU32, ImS32, bb, id, 5 --[[ImGuiDataType.U32]], v32, min, max, format, flags, out_grab_bb)
if r then v = (ImU16)(v32) end
return v, r
elseif data_type == 4 --[[ImGuiDataType.S32]] then
assert(min >= math.floor(IM_S32_MIN / 2) and max <= math.floor(IM_S32_MAX / 2))
return ImGui.SliderBehaviorT(ImS32, ImS32, bb, id, data_type, v, min, max, format, flags, out_grab_bb)
elseif data_type == 5 --[[ImGuiDataType.U32]] then
assert(max <= math.floor(IM_U32_MAX / 2))
return ImGui.SliderBehaviorT(ImU32, ImS32, bb, id, data_type, v, min, max, format, flags, out_grab_bb)
elseif data_type == 6 --[[ImGuiDataType.Float]] then
assert(min >= -3.4028234663853e+38 --[[FLT_MAX]] / 2.0 and max <= 3.4028234663853e+38 --[[FLT_MAX]] / 2.0)
return ImGui.SliderBehaviorT(float, float, bb, id, data_type, v, min, max, format, flags, out_grab_bb)
elseif data_type == 7 --[[ImGuiDataType.Double]] then
assert(min >= -1.7976931348623e+308 --[[DBL_MAX]] / 2.0 and max <= 1.7976931348623e+308 --[[DBL_MAX]] / 2.0)
return ImGui.SliderBehaviorT(double, double, bb, id, data_type, v, min, max, format, flags, out_grab_bb)
end
assert(false)
return v, false
end
-- Note: data is passed by value and the new value is returned: Returns (data, value_changed). For a slider, min and max are required.
-- Read code of e.g. SliderFloat(), SliderInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly.
--- @generic T : number
--- @param label string
--- @param data_type ImGuiDataType
--- @param data T
--- @param min T
--- @param max T
--- @param format? string
--- @param flags? ImGuiSliderFlags
function ImGui.SliderScalar(label, data_type, data, min, max, format, flags)
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return data, false
end
local g = GImGui
local style = g.Style
local id = window:GetID(label)
local w = ImGui.CalcItemWidth()
local color_marker = (bit32.band(g.NextItemData.HasFlags, 32 --[[ImGuiNextItemDataFlags.HasColorMarker]]) ~= 0) and g.NextItemData.ColorMarker or 0
local label_end = ImGui.FindRenderedTextEnd(label)
local label_size = ImGui.CalcTextSize(label, label_end, false)
local cursor = window.DC.CursorPos
local frame_bb = ImRect(cursor.x, cursor.y, cursor.x + w, cursor.y + (label_size.y + style.FramePadding.y * 2.0)) -- [Luau] no temporary ImVec2
local total_bb_x = frame_bb.Max.x + ((label_size.x > 0.0) and (style.ItemInnerSpacing.x + label_size.x) or 0.0)
local total_bb = SliderScalar_total_bb -- [Luau] scratch: ItemSize()/ItemAdd() only read it
total_bb.Min.x = frame_bb.Min.x; total_bb.Min.y = frame_bb.Min.y; total_bb.Max.x = total_bb_x; total_bb.Max.y = frame_bb.Max.y
local temp_input_allowed = bit32.band(flags, 128 --[[ImGuiSliderFlags.NoInput]]) == 0
ImGui.ItemSize(total_bb, style.FramePadding.y)
local item_flags = temp_input_allowed and 1048576 --[[ImGuiItemFlags.Inputable]] or 0
if not ImGui.ItemAdd(total_bb, id, frame_bb, item_flags) then
return data, false
end
-- Default format string when passing NULL
if format == nil then
format = ImGui.DataTypeGetInfo(data_type).PrintFmt
end
local hovered = ImGui.ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags)
local temp_input_is_active = temp_input_allowed and ImGui.TempInputIsActive(id)
if not temp_input_is_active then
-- Tabbing or Ctrl+Click on Slider turns it into an input box
local clicked = hovered and ImGui.IsMouseClickedEx(0, 0 --[[ImGuiInputFlags.None]], id)
local make_active = clicked or g.NavActivateId == id
if make_active and clicked then
ImGui.SetKeyOwner(656 --[[ImGuiKey.MouseLeft]], id)
end
if make_active and temp_input_allowed then
if (clicked and g.IO.KeyCtrl) or (g.NavActivateId == id and bit32.band(g.NavActivateFlags, 1 --[[ImGuiActivateFlags.PreferInput]]) ~= 0) then
temp_input_is_active = true
end
end
-- Store initial value (not used by main lib but available as a convenience but some mods e.g. to revert)
if make_active then
g.ActiveIdValueOnActivation = data
end
if make_active and not temp_input_is_active then
ImGui.SetActiveID(id, window)
ImGui.SetFocusID(id, window)
ImGui.FocusWindow(window)
g.ActiveIdUsingNavDirMask = bit32.bor(g.ActiveIdUsingNavDirMask, bit32.lshift(1, 0 --[[ImGuiDir.Left]]), bit32.lshift(1, 1 --[[ImGuiDir.Right]]))
end
end
if temp_input_is_active then
-- Only clamp Ctrl+Click input when ImGuiSliderFlags.ClampOnInput is set (generally via ImGuiSliderFlags.AlwaysClamp)
local clamp_enabled = bit32.band(flags, 512 --[[ImGuiSliderFlags.ClampOnInput]]) ~= 0 -- Don't use TempInputIsClampEnabled()
return ImGui.TempInputScalar(frame_bb, id, label, data_type, data, format, clamp_enabled and min or nil, clamp_enabled and max or nil)
end
-- Draw frame
local frame_col = ImGui.GetColorU32((g.ActiveId == id) and 9 --[[ImGuiCol.FrameBgActive]] or (hovered and 8 --[[ImGuiCol.FrameBgHovered]] or 7 --[[ImGuiCol.FrameBg]]))
ImGui.RenderNavCursor(frame_bb, id)
ImGui.RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, false, style.FrameRounding)
if color_marker ~= 0 and style.ColorMarkerSize > 0.0 then
ImGui.RenderColorComponentMarker(frame_bb, ImGui.GetColorU32(color_marker, nil, true), style.FrameRounding)
end
ImGui.RenderFrameBorder(frame_bb.Min, frame_bb.Max, g.Style.FrameRounding)
-- Slider behavior
local grab_bb = SliderScalar_grab_bb -- [Luau] scratch: fully written by SliderBehavior(), only read below
local value_changed
data, value_changed = ImGui.SliderBehavior(frame_bb, id, data_type, data, min, max, format, flags, grab_bb)
if value_changed then
ImGui.MarkItemEdited(id)
end
-- Render grab
if grab_bb.Max.x > grab_bb.Min.x then
window.DrawList:AddRectFilled(grab_bb.Min, grab_bb.Max, ImGui.GetColorU32((g.ActiveId == id) and 21 --[[ImGuiCol.SliderGrabActive]] or 20 --[[ImGuiCol.SliderGrab]]), style.GrabRounding)
end
-- TODO: ImGui.DataTypeFormatString() should be used here instead of ImFormatString()
-- Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
-- if g.LogEnabled then
-- ImGui.LogSetNextTextDecoration("{", "}")
-- end
ImGui.RenderTextClipped(frame_bb.Min, frame_bb.Max, FormatCached(format, data), nil, nil, DRAG_TEXT_ALIGN)
if label_size.x > 0.0 then
ImGui.RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label, 1, label_end, false)
end
-- IMGUI_TEST_ENGINE_ITEM_INFO()
return data, value_changed
end
-- Add multiple sliders on 1 line for compact edition of multiple components
--- @param label string
--- @param data_type ImGuiDataType
--- @param v table
--- @param components int
--- @param v_min number
--- @param v_max number
--- @param format? string
--- @param flags? ImGuiSliderFlags
function ImGui.SliderScalarN(label, data_type, v, components, v_min, v_max, format, flags)
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
local g = GImGui
local value_changed = false
ImGui.BeginGroup()
ImGui.PushID(label)
ImGui.PushMultiItemsWidths(components, ImGui.CalcItemWidth())
for i = 1, components do
ImGui.PushID(i - 1)
if i > 1 then
ImGui.SameLine(0, g.Style.ItemInnerSpacing.x)
end
if bit32.band(flags, 4096 --[[ImGuiSliderFlags.ColorMarkers]]) ~= 0 then
ImGui.SetNextItemColorMarker(GDefaultRgbaColorMarkers[i])
end
local changed
v[i], changed = ImGui.SliderScalar("", data_type, v[i], v_min, v_max, format, flags)
value_changed = value_changed or changed
ImGui.PopID()
ImGui.PopItemWidth()
end
ImGui.PopID()
local label_end = ImGui.FindRenderedTextEnd(label)
if label_end > 1 then
ImGui.SameLine(0, g.Style.ItemInnerSpacing.x)
ImGui.TextEx(label, label_end)
end
ImGui.EndGroup()
return value_changed
end
--- @param label string
--- @param v [float, float]
--- @param v_min float
--- @param v_max float
--- @param format? string
--- @param flags? ImGuiSliderFlags
function ImGui.SliderFloat2(label, v, v_min, v_max, format, flags)
return ImGui.SliderScalarN(label, 6 --[[ImGuiDataType.Float]], v, 2, v_min, v_max, format, flags)
end
--- @param label string
--- @param v [float, float, float]
--- @param v_min float
--- @param v_max float
--- @param format? string
--- @param flags? ImGuiSliderFlags
function ImGui.SliderFloat3(label, v, v_min, v_max, format, flags)
return ImGui.SliderScalarN(label, 6 --[[ImGuiDataType.Float]], v, 3, v_min, v_max, format, flags)
end
--- @param label string
--- @param v [float, float, float, float]
--- @param v_min float
--- @param v_max float
--- @param format? string
--- @param flags? ImGuiSliderFlags
function ImGui.SliderFloat4(label, v, v_min, v_max, format, flags)
return ImGui.SliderScalarN(label, 6 --[[ImGuiDataType.Float]], v, 4, v_min, v_max, format, flags)
end
--- @param label string
--- @param v float
--- @param v_min float
--- @param v_max float
--- @param format? string
--- @param flags? ImGuiSliderFlags
function ImGui.SliderFloat(label, v, v_min, v_max, format, flags)
if format == nil then format = "%.3f" end
if flags == nil then flags = 0 end
return ImGui.SliderScalar(label, 6 --[[ImGuiDataType.Float]], v, v_min, v_max, format, flags)
end
--- @param label string
--- @param v_rad float
--- @param v_degrees_min? float
--- @param v_degrees_max? float
--- @param format? string
--- @param flags? ImGuiSliderFlags
--- @return float v_rad # updated `v_rad`
--- @return bool value_changed
function ImGui.SliderAngle(label, v_rad, v_degrees_min, v_degrees_max, format, flags)
if v_degrees_min == nil then v_degrees_min = -360.0 end
if v_degrees_max == nil then v_degrees_max = 360.0 end
if format == nil then format = "%.0f deg" end
if flags == nil then flags = 0 end
local v_deg = v_rad * 360.0 / (2 * 3.141592653589793 --[[IM_PI]])
local value_changed
v_deg, value_changed = ImGui.SliderFloat(label, v_deg, v_degrees_min, v_degrees_max, format, flags)
if value_changed then
v_rad = v_deg * (2 * 3.141592653589793 --[[IM_PI]]) / 360.0
end
return v_rad, value_changed
end
--- @param label string
--- @param v int
--- @param v_min int
--- @param v_max int
--- @param format? string
--- @param flags? ImGuiSliderFlags
function ImGui.SliderInt(label, v, v_min, v_max, format, flags)
if format == nil then format = "%d" end
if flags == nil then flags = 0 end
return ImGui.SliderScalar(label, 4 --[[ImGuiDataType.S32]], v, v_min, v_max, format, flags)
end
function ImGui.SliderInt2(label, v, v_min, v_max, format, flags)
return ImGui.SliderScalarN(label, 4 --[[ImGuiDataType.S32]], v, 2, v_min, v_max, format or "%d", flags)
end
function ImGui.SliderInt3(label, v, v_min, v_max, format, flags)
return ImGui.SliderScalarN(label, 4 --[[ImGuiDataType.S32]], v, 3, v_min, v_max, format or "%d", flags)
end
function ImGui.SliderInt4(label, v, v_min, v_max, format, flags)
return ImGui.SliderScalarN(label, 4 --[[ImGuiDataType.S32]], v, 4, v_min, v_max, format or "%d", flags)
end
--- @param label string
--- @param size ImVec2
--- @param data_type ImGuiDataType
--- @param data number
--- @param min number
--- @param max number
--- @param format? string
--- @param flags? ImGuiSliderFlags
function ImGui.VSliderScalar(label, size, data_type, data, min, max, format, flags)
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return data, false
end
local g = GImGui
local style = g.Style
local id = window:GetID(label)
local label_end = ImGui.FindRenderedTextEnd(label)
local label_size = ImGui.CalcTextSize(label, label_end, false)
local frame_bb = ImRect(window.DC.CursorPos, window.DC.CursorPos + size)
local bb = ImRect(frame_bb.Min, ImVec2(frame_bb.Max.x + (label_size.x > 0.0 and (style.ItemInnerSpacing.x + label_size.x) or 0.0), frame_bb.Max.y))
ImGui.ItemSize(bb, style.FramePadding.y)
if not ImGui.ItemAdd(frame_bb, id) then
return data, false
end
-- Default format string when passing NULL
if format == nil then
format = ImGui.DataTypeGetInfo(data_type).PrintFmt
end
local hovered = ImGui.ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags)
local clicked = hovered and ImGui.IsMouseClickedEx(0, 0 --[[ImGuiInputFlags.None]], id)
if clicked or g.NavActivateId == id then
if clicked then
ImGui.SetKeyOwner(656 --[[ImGuiKey.MouseLeft]], id)
end
ImGui.SetActiveID(id, window)
ImGui.SetFocusID(id, window)
ImGui.FocusWindow(window)
g.ActiveIdUsingNavDirMask = bit32.bor(g.ActiveIdUsingNavDirMask, bit32.lshift(1, 2 --[[ImGuiDir.Up]]), bit32.lshift(1, 3 --[[ImGuiDir.Down]]))
end
-- Draw frame
local frame_col = ImGui.GetColorU32((g.ActiveId == id) and 9 --[[ImGuiCol.FrameBgActive]] or (hovered and 8 --[[ImGuiCol.FrameBgHovered]] or 7 --[[ImGuiCol.FrameBg]]))
ImGui.RenderNavCursor(frame_bb, id)
ImGui.RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding)
-- Slider behavior
local grab_bb = ImRect()
local value_changed
data, value_changed = ImGui.SliderBehavior(frame_bb, id, data_type, data, min, max, format, bit32.bor(flags or 0, 1048576 --[[ImGuiSliderFlags.Vertical]]), grab_bb)
if value_changed then
ImGui.MarkItemEdited(id)
end
-- Render grab
if grab_bb.Max.y > grab_bb.Min.y then
window.DrawList:AddRectFilled(grab_bb.Min, grab_bb.Max, ImGui.GetColorU32((g.ActiveId == id) and 21 --[[ImGuiCol.SliderGrabActive]] or 20 --[[ImGuiCol.SliderGrab]]), style.GrabRounding)
end
-- Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
-- For the vertical slider we allow centered text to overlap the frame padding
-- local value_buf, value_buf_size = {}, 64
-- TODO: ImGui.DataTypeFormatString()
ImGui.RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, ImFormatString(format, data), nil, nil, ImVec2(0.5, 0.0))
if label_size.x > 0.0 then
ImGui.RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label, 1, label_end, false)
end
-- IMGUI_TEST_ENGINE_ITEM_INFO()
return data, value_changed
end
--- @param label string
--- @param size ImVec2
--- @param v float
--- @param v_min float
--- @param v_max float
--- @param format? string
--- @param flags? ImGuiSliderFlags
function ImGui.VSliderFloat(label, size, v, v_min, v_max, format, flags)
if format == nil then format = "%.3f" end
return ImGui.VSliderScalar(label, size, 6 --[[ImGuiDataType.Float]], v, v_min, v_max, format, flags)
end
--- @param label string
--- @param size ImVec2
--- @param v int
--- @param v_min int
--- @param v_max int
--- @param format? string
--- @param flags? ImGuiSliderFlags
function ImGui.VSliderInt(label, size, v, v_min, v_max, format, flags)
if format == nil then format = "%d" end
return ImGui.VSliderScalar(label, size, 4 --[[ImGuiDataType.S32]], v, v_min, v_max, format, flags)
end
-------------------------------------------------------------------------------
-- [SECTION] Widgets: InputText, InputTextMultiline, InputTextWithHint
-------------------------------------------------------------------------------
-- - imstb_textedit.h include
-- - InputText()
-- - InputTextWithHint()
-- - InputTextMultiline()
-- - InputTextEx() [Internal]
-- - DebugNodeInputTextState() [Internal]
-------------------------------------------------------------------------------
--- @param label string
--- @param buf char[]
--- @param buf_size int
--- @param flags? ImGuiInputTextFlags
--- @param callback? ImGuiInputTextCallback
--- @param user_data any
function ImGui.InputText(label, buf, buf_size, flags, callback, user_data)
if flags == nil then flags = 0 end
assert(bit32.band(flags, 67108864 --[[ImGuiInputTextFlags.Multiline]]) == 0) -- call InputTextMultiline()
return ImGui.InputTextEx(label, nil, buf, buf_size, ImVec2(0, 0), flags, callback, user_data)
end
-- [Luau] InputText edits a byte table in place (C++ char buf[N]). Helpers to make/read one:
-- local buf = ImGui.NewTextBuffer("Hello", 256); ImGui.InputText("label", buf, 256); print(ImGui.TextBufferToString(buf))
function ImGui.NewTextBuffer(str, size)
local t = {}
for i = 1, #str do t[i] = string.byte(str, i) end
for i = #str + 1, math.max(size or 0, #str + 1) do t[i] = 0 end
return t
end
function ImGui.TextBufferToString(buf)
local n = 0
while buf[n + 1] ~= nil and buf[n + 1] ~= 0 do n = n + 1 end
return n == 0 and "" or ImGui._ByteArrayToString(buf, 1, n + 1)
end
--- @param size? ImVec2
function ImGui.InputTextMultiline(label, buf, buf_size, size, flags, callback, user_data)
return ImGui.InputTextEx(label, nil, buf, buf_size, size or ImVec2(0, 0), bit32.bor(flags or 0, 67108864 --[[ImGuiInputTextFlags.Multiline]]), callback, user_data)
end
ImStb = {}
--- @module "imstb_textedit"
local stbte
local IMSTB_TEXTEDIT_GETWIDTH_NEWLINE = -1.0
ImStb.TEXTEDIT_memmove = ImStd.memmove
--- @param ctx ImGuiContext
--- @param text char[]
--- @param text_begin int
--- @param text_end_display int
--- @param text_end int
--- @param out_offset? ImVec2
--- @param flags? ImDrawTextFlags
local function InputTextCalcTextSize(ctx, text, text_begin, text_end_display, text_end, out_offset, flags)
if flags == nil then flags = 0 end
local g = ctx
local obj = g.InputTextState
assert(text_end_display >= text_begin and text_end_display <= text_end)
return ImFontCalcTextSizeEx(g.Font, g.FontSize, 3.4028234663853e+38 --[[FLT_MAX]], obj.WrapWidth, text, text_begin, text_end_display, text_end, out_offset, flags)
end
-- Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar)
-- With our UTF-8 use of stb_textedit:
-- - STB_TEXTEDIT_GETCHAR is nothing more than a a "GETBYTE". It's only used to compare to ascii or to copy blocks of text so we are fine.
-- - One exception is the STB_TEXTEDIT_IS_SPACE feature which would expect a full char in order to handle full-width space such as 0x3000 (see ImCharIsBlankW).
-- - ...but we don't use that feature.
--- @param obj ImGuiInputTextState
--- @return int
function ImStb.TEXTEDIT_STRINGLEN(obj) return obj.TextLen end
--- @param obj ImGuiInputTextState
--- @param idx int # 1-based
function ImStb.TEXTEDIT_GETCHAR(obj, idx) assert(idx >= 1 and idx <= obj.TextLen + 1); return obj.TextSrc[idx] end
--- @param obj ImGuiInputTextState
--- @param line_start_idx int # 1-based
--- @param char_idx int # 1-based
--- @return float
function ImStb.TEXTEDIT_GETWIDTH(obj, line_start_idx, char_idx) local _, c = ImStd.ImTextCharFromUtf8(obj.TextSrc, line_start_idx + char_idx - 1, obj.TextLen + 1); if c == 10 then return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE end; local g = obj.Ctx; return g.FontBaked:GetCharAdvance(c) * g.FontBakedScale end
--- @param r StbTexteditRow
--- @param obj ImGuiInputTextState
--- @param line_start_idx int # 1-based
function ImStb.TEXTEDIT_LAYOUTROW(r, obj, line_start_idx)
local text = obj.TextSrc
local size, text_remaining = InputTextCalcTextSize(obj.Ctx, text, line_start_idx, obj.TextLen + 1, obj.TextLen + 1, nil, bit32.bor(4 --[[ImDrawTextFlags.StopOnNewLine]], 2 --[[ImDrawTextFlags.WrapKeepBlanks]]))
r.x0 = 0.0
r.x1 = size.x
r.baseline_y_delta = size.y
r.ymin = 0.0
r.ymax = size.y
r.num_chars = text_remaining - line_start_idx
end
--- @param obj ImGuiInputTextState
--- @param idx int
function ImStb.TEXTEDIT_GETNEXTCHARINDEX(obj, idx)
if idx >= obj.TextLen then
return obj.TextLen + 1
end
return idx + ImStd.ImTextCharFromUtf8(obj.TextSrc, idx, obj.TextLen + 1)
end
--- @param obj ImGuiInputTextState
--- @param idx int
function ImStb.TEXTEDIT_GETPREVCHARINDEX(obj, idx)
if idx <= 1 then
return -1
end
local p = ImStd.ImTextFindPreviousUtf8Codepoint(obj.TextSrc, 1, idx)
return p
end
local ImCharIsSeparatorW do
local separator_list = {
44, 0x3001, 46, 0x3002, 59, 0xFF1B, 40, 0xFF08, 41, 0xFF09, 123, 0xFF5B, 125, 0xFF5D,
91, 0x300C, 93, 0x300D, 124, 0xFF5C, 33, 0xFF01, 92, 0xFFE5, 47, 0x30FB, 0xFF0F,
10, 13
}
--- @param c unsigned_int
function ImCharIsSeparatorW(c)
for i = 1, #separator_list do
if c == separator_list[i] then
return true
end
end
return false
end
end
--- @param obj ImGuiInputTextState
--- @param idx int
function ImStb.is_word_boundary_from_right(obj, idx)
-- When ImGuiInputTextFlags.Password is set, we don't want actions such as Ctrl+Arrow to leak the fact that underlying data are blanks or separators.
if bit32.band(obj.Flags, 1024 --[[ImGuiInputTextFlags.Password]]) ~= 0 or idx <= 1 then
return false
end
local prev = ImStd.ImTextFindPreviousUtf8Codepoint(obj.TextSrc, 1, idx)
local _, curr_c = ImStd.ImTextCharFromUtf8(obj.TextSrc, idx, obj.TextLen + 1)
local _, prev_c = ImStd.ImTextCharFromUtf8(obj.TextSrc, prev, obj.TextLen + 1)
local prev_white = ImCharIsBlankW(prev_c)
local prev_separ = ImCharIsSeparatorW(prev_c)
local curr_white = ImCharIsBlankW(curr_c)
local curr_separ = ImCharIsSeparatorW(curr_c)
return ((prev_white or prev_separ) and not (curr_separ or curr_white)) or (curr_separ and not prev_separ)
end
--- @param obj ImGuiInputTextState
--- @param idx int
function ImStb.is_word_boundary_from_left(obj, idx)
if bit32.band(obj.Flags, 1024 --[[ImGuiInputTextFlags.Password]]) ~= 0 or idx <= 1 then
return false
end
local prev = ImStd.ImTextFindPreviousUtf8Codepoint(obj.TextSrc, 1, idx)
local _, prev_c = ImStd.ImTextCharFromUtf8(obj.TextSrc, idx, obj.TextLen + 1)
local _, curr_c = ImStd.ImTextCharFromUtf8(obj.TextSrc, prev, obj.TextLen + 1)
local prev_white = ImCharIsBlankW(prev_c)
local prev_separ = ImCharIsSeparatorW(prev_c)
local curr_white = ImCharIsBlankW(curr_c)
local curr_separ = ImCharIsSeparatorW(curr_c)
return (prev_white and not (curr_separ or curr_white)) or (curr_separ and not prev_separ)
end
--- @param obj ImGuiInputTextState
--- @param idx int
function ImStb.TEXTEDIT_MOVEWORDLEFT(obj, idx)
idx = ImStb.TEXTEDIT_GETPREVCHARINDEX(obj, idx)
while idx >= 1 and not ImStb.is_word_boundary_from_right(obj, idx) do
idx = ImStb.TEXTEDIT_GETPREVCHARINDEX(obj, idx)
end
return (idx < 1) and 1 or idx
end
--- @param obj ImGuiInputTextState
--- @param idx int
function ImStb.TEXTEDIT_MOVEWORDRIGHT_MAC(obj, idx)
local len = obj.TextLen
idx = ImStb.TEXTEDIT_GETNEXTCHARINDEX(obj, idx)
while idx <= len and not ImStb.is_word_boundary_from_left(obj, idx) do
idx = ImStb.TEXTEDIT_GETNEXTCHARINDEX(obj, idx)
end
return (idx > len + 1) and len + 1 or idx
end
--- @param obj ImGuiInputTextState
--- @param idx int
function ImStb.TEXTEDIT_MOVEWORDRIGHT_WIN(obj, idx)
idx = ImStb.TEXTEDIT_GETNEXTCHARINDEX(obj, idx)
local len = obj.TextLen
while idx <= len and not ImStb.is_word_boundary_from_right(obj, idx) do
idx = ImStb.TEXTEDIT_GETNEXTCHARINDEX(obj, idx)
end
return (idx > len + 1) and len + 1 or idx
end
--- @param obj ImGuiInputTextState
--- @param idx int
function ImStb.TEXTEDIT_MOVEWORDRIGHT(obj, idx)
local g = obj.Ctx
if g.IO.ConfigMacOSXBehaviors then
return ImStb.TEXTEDIT_MOVEWORDRIGHT_MAC(obj, idx)
else
return ImStb.TEXTEDIT_MOVEWORDRIGHT_WIN(obj, idx)
end
end
-- Reimplementation of stb_textedit_move_line_start()/stb_textedit_move_line_end() which supports word-wrapping.
--- @param obj ImGuiInputTextState
--- @param state STB_TexteditState
--- @param cursor int
function ImStb.TEXTEDIT_MOVELINESTART(obj, state, cursor)
if state.single_line then
return 1
end
if obj.WrapWidth > 0.0 then
local g = obj.Ctx
local bol = ImStd.ImStrbol(obj.TextSrc, cursor, 1)
local p = bol
local text_end = obj.TextLen + 1 -- End of line would be enough
while p >= bol do
local eol = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, obj.TextSrc, p, text_end, obj.WrapWidth, 2 --[[ImDrawTextFlags.WrapKeepBlanks]])
if p == cursor then -- If we are already on a visible beginning-of-line, return real beginning-of-line (would be same as regular handler below)
return bol
end
if eol == cursor and obj.TextA[cursor] ~= 10 and obj.LastMoveDirectionLR == 0 --[[ImGuiDir.Left]] then
return bol
end
if eol >= cursor then
return p
end
p = (obj.TextSrc[eol] == 10) and eol + 1 or eol
end
end
-- Regular handler, same as stb_textedit_move_line_start()
while cursor > 1 do
local prev_cursor = ImStb.TEXTEDIT_GETPREVCHARINDEX(obj, cursor)
if (ImStb.TEXTEDIT_GETCHAR(obj, prev_cursor) == 10 --[[STB_TEXTEDIT_NEWLINE]]) then
break
end
cursor = prev_cursor
end
return cursor
end
--- @param obj ImGuiInputTextState
--- @param state STB_TexteditState
--- @param cursor int
function ImStb.TEXTEDIT_MOVELINEEND(obj, state, cursor)
local n = ImStb.TEXTEDIT_STRINGLEN(obj)
if state.single_line then
return n + 1
end
if obj.WrapWidth > 0.0 then
local g = obj.Ctx
local p = ImStd.ImStrbol(obj.TextSrc, cursor, 1)
local text_end = obj.TextLen + 1 -- End of line would be enough
while p < text_end do
local eol = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, obj.TextSrc, p, text_end, obj.WrapWidth, 2 --[[ImDrawTextFlags.WrapKeepBlanks]])
cursor = eol
if eol == cursor and obj.LastMoveDirectionLR ~= 0 --[[ImGuiDir.Left]] then -- If we are already on a visible end-of-line, switch to regular handle
break
end
if eol > cursor then
return cursor
end
p = (obj.TextSrc[eol] == 10) and eol + 1 or eol
end
end
-- Regular handler, same as stb_textedit_move_line_end()
while (cursor < n and ImStb.TEXTEDIT_GETCHAR(obj, cursor) ~= 10 --[[STB_TEXTEDIT_NEWLINE]]) do
cursor = ImStb.TEXTEDIT_GETNEXTCHARINDEX(obj, cursor)
end
return cursor
end
--- @param obj ImGuiInputTextState
--- @param pos int
--- @param n int
function ImStb.TEXTEDIT_DELETECHARS(obj, pos, n)
-- Offset remaining text (+ copy zero terminator)
assert(obj.TextSrc == obj.TextA.Data)
ImStd.memmove(obj.TextA.Data, pos, obj.TextA.Data, pos + n, obj.TextLen - n - pos + 2)
obj.EditedBefore = true
obj.EditedThisFrame = true
obj.TextLen = obj.TextLen - n
end
--- @param obj ImGuiInputTextState
--- @param pos int
--- @param new_text ImStringBuffer
--- @param new_text_pos int
--- @param new_text_len int
function ImStb.TEXTEDIT_INSERTCHARS(obj, pos, new_text, new_text_pos, new_text_len)
local is_resizable = bit32.band(obj.Flags, 4194304 --[[ImGuiInputTextFlags.CallbackResize]]) ~= 0
local text_len = obj.TextLen
assert(pos <= text_len + 1)
-- We support partial insertion (with a mod in stb_textedit.h)
local avail = obj.BufCapacity - 1 - obj.TextLen
if not is_resizable and new_text_len > avail then
new_text_len = math.floor(ImStd.ImTextFindValidUtf8CodepointEnd(new_text, new_text_pos, new_text_len + 1, avail) - new_text_pos) -- Truncate to closest UTF-8 codepoint. Alternative: return 0 to cancel insertion.
end
if new_text_len == 0 then
return 0
end
-- Grow internal buffer if needed
assert(obj.TextSrc == obj.TextA.Data)
if text_len + new_text_len + 1 > obj.TextA.Size and is_resizable then
obj.TextA:resize(text_len + ImClamp(new_text_len, 32, ImMax(256, new_text_len)) + 1)
obj.TextSrc = obj.TextA.Data
end
local text = obj.TextA.Data
if pos ~= text_len + 1 then
ImStd.memmove(text, pos + new_text_len, text, pos, text_len - pos + 1)
end
ImStd.memmove(text, pos, new_text, new_text_pos, new_text_len)
obj.EditedBefore = true
obj.EditedThisFrame = true
obj.TextLen = obj.TextLen + new_text_len
obj.TextA[obj.TextLen + 1] = 0
return new_text_len
end
stbte = IM_INCLUDE"imstb_textedit.lua"
-- stb_textedit internally allows for a single undo record to do addition and deletion, but somehow, calling
-- the stb_textedit_paste() function creates two separate records, so we perform it manually. (FIXME: Report to nothings/stb?)
--- @param str ImGuiInputTextState
--- @param state STB_TexteditState
--- @param text IMSTB_TEXTEDIT_CHARTYPE[]
--- @param text_len int
function ImStb.stb_textedit_replace(str, state, text, text_len)
stbte.makeundo_replace(str, state, 1, str.TextLen, text_len)
ImStb.TEXTEDIT_DELETECHARS(str, 1, str.TextLen)
state.cursor = 1
state.select_start = 1
state.select_end = 1
if text_len <= 0 then
return
end
local text_len_inserted = ImStb.TEXTEDIT_INSERTCHARS(str, 1, text, 1, text_len)
if text_len_inserted > 0 then
state.cursor = text_len + 1
state.select_start = text_len + 1
state.select_end = text_len + 1
state.has_preferred_x = false
return
end
assert(false) -- Failed to insert character, normally shouldn't happen because of how we currently use stb_textedit_replace()
end
local MT = ImGui.GetMetatables()
do
--- @param key int
function MT.ImGuiInputTextState:OnKeyPressed(key)
stbte.key(self, self.Stb, key)
self.CursorFollow = true
self:CursorAnimReset()
local key_u = bit32.band(key, bit32.bnot(4194304 --[[STB_TEXTEDIT_K_SHIFT]]))
if key_u == 2097152 --[[STB_TEXTEDIT_K_LEFT]] or key_u == 2097156 --[[STB_TEXTEDIT_K_LINESTART]] or key_u == 2097158 --[[STB_TEXTEDIT_K_TEXTSTART]] or key_u == 2097161 --[[STB_TEXTEDIT_K_BACKSPACE]] or key_u == 2097164 --[[STB_TEXTEDIT_K_WORDLEFT]] then
self.LastMoveDirectionLR = 0 --[[ImGuiDir.Left]]
elseif key_u == 2097153 --[[STB_TEXTEDIT_K_RIGHT]] or key_u == 2097157 --[[STB_TEXTEDIT_K_LINEEND]] or key_u == 2097159 --[[STB_TEXTEDIT_K_TEXTEND]] or key_u == 2097160 --[[STB_TEXTEDIT_K_DELETE]] or key_u == 2097165 --[[STB_TEXTEDIT_K_WORDRIGHT]] then
self.LastMoveDirectionLR = 1 --[[ImGuiDir.Right]]
end
end
local utf8 = {0, 0, 0, 0, 0}
--- @param c unsigned_int
function MT.ImGuiInputTextState:OnCharPressed(c)
for i = 1, 5 do utf8[i] = 0 end
-- Convert the key to a UTF8 byte sequence.
-- The changes we had to make to stb_textedit_key made it very much UTF-8 specific which is not too great.
ImStd.ImTextCharToUtf8(utf8, c)
stbte.text(self, self.Stb, utf8, ImStd.ImStrlen(utf8))
self.CursorFollow = true
self:CursorAnimReset()
end
-- Those functions are not inlined in imgui_internal.h, allowing us to hide ImStbTexteditState from that header.
function MT.ImGuiInputTextState:CursorAnimReset() self.CursorAnim = -0.30 end -- After a user-input the cursor stays on for a while without blinking
function MT.ImGuiInputTextState:CursorClamp()
self.Stb.cursor = ImMin(self.Stb.cursor, self.TextLen + 1)
self.Stb.select_start = ImMin(self.Stb.select_start, self.TextLen + 1)
self.Stb.select_end = ImMin(self.Stb.select_end, self.TextLen + 1)
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()
self.Stb.select_start = 1
self.Stb.cursor = self.TextLen + 1
self.Stb.select_end = self.TextLen + 1
self.Stb.has_preferred_x = false
end
function MT.ImGuiInputTextState:ReloadUserBufAndSelectAll() self.WantReloadUserBuf = true; self.ReloadSelectionStart = 1; self.ReloadSelectionEnd = 2147483647 --[[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 = 2147483647 --[[INT_MAX]]; self.ReloadSelectionEnd = 2147483647 --[[INT_MAX]] end
end
function ImGui.PushPasswordFont()
local g = GImGui
local backup = g.InputTextPasswordFontBackupBaked
assert(backup.IndexAdvanceX.Size == 0 and backup.IndexLookup.Size == 0)
local glyph = g.FontBaked:FindGlyph(42) -- '*'
g.InputTextPasswordFontBackupFlags = g.Font.Flags
backup.FallbackGlyphIndex = g.FontBaked.FallbackGlyphIndex
backup.FallbackAdvanceX = g.FontBaked.FallbackAdvanceX
backup.IndexLookup:swap(g.FontBaked.IndexLookup)
backup.IndexAdvanceX:swap(g.FontBaked.IndexAdvanceX)
g.Font.Flags = bit32.bor(g.Font.Flags, 4 --[[ImFontFlags.NoLoadGlyphs]])
g.FontBaked.FallbackGlyphIndex = g.FontBaked.Glyphs:index_from_ptr(glyph) + 1
g.FontBaked.FallbackAdvanceX = glyph.AdvanceX
end
function ImGui.PopPasswordFont()
local g = GImGui
local backup = g.InputTextPasswordFontBackupBaked
g.Font.Flags = g.InputTextPasswordFontBackupFlags
g.FontBaked.FallbackGlyphIndex = backup.FallbackGlyphIndex
g.FontBaked.FallbackAdvanceX = backup.FallbackAdvanceX
g.FontBaked.IndexLookup:swap(backup.IndexLookup)
g.FontBaked.IndexAdvanceX:swap(backup.IndexAdvanceX)
assert(backup.IndexAdvanceX.Size == 0 and backup.IndexLookup.Size == 0)
end
-- Return false to discard a character. Returns (char, ok).
--- @param ctx ImGuiContext
--- @param state ImGuiInputTextState
--- @param char unsigned_int
--- @param callback? ImGuiInputTextCallback
--- @param user_data any
--- @param input_source_is_clipboard? bool
--- @return unsigned_int out_char
--- @return bool
local function InputTextFilterCharacter(ctx, state, char, callback, user_data, input_source_is_clipboard)
if input_source_is_clipboard == nil then input_source_is_clipboard = false end
assert(state ~= nil)
local c = char
local flags = state.Flags
-- Filter non-printable (NB: isprint is unreliable! see #2467)
local apply_named_filters = true
if c < 0x20 then
local pass = false
pass = pass or (c == 10) and (bit32.band(flags, 67108864 --[[ImGuiInputTextFlags.Multiline]]) ~= 0) -- Note that an Enter KEY will emit \r and be ignored (we poll for KEY in InputText() code)
if c == 10 and input_source_is_clipboard and (bit32.band(flags, 67108864 --[[ImGuiInputTextFlags.Multiline]]) == 0) then -- In single line mode, replace \n with a space
char = 32
c = 32
pass = true
end
pass = pass or (c == 10) and (bit32.band(flags, 67108864 --[[ImGuiInputTextFlags.Multiline]]) ~= 0)
pass = pass or (c == 9) and (bit32.band(flags, 32 --[[ImGuiInputTextFlags.AllowTabInput]]) ~= 0) -- tab
if not pass then
return char, false
end
apply_named_filters = false -- Override named filters below so newline and tabs can still be inserted.
end
if not input_source_is_clipboard then
-- We ignore Ascii representation of delete (emitted from Backspace on OSX, see #2578, #2817)
if c == 127 then
return char, false
end
-- Filter private Unicode range. GLFW on OSX seems to send private characters for special keys like arrow keys (FIXME)
if c >= 0xE000 and c <= 0xF8FF then
return char, false
end
end
-- Filter Unicode ranges we are not handling in this build
if c > 65535 --[[IM_UNICODE_CODEPOINT_MAX]] then
return char, false
end
-- Generic named filters
if apply_named_filters and (bit32.band(flags, bit32.bor(1 --[[ImGuiInputTextFlags.CharsDecimal]], 2 --[[ImGuiInputTextFlags.CharsHexadecimal]], 8 --[[ImGuiInputTextFlags.CharsUppercase]], 16 --[[ImGuiInputTextFlags.CharsNoBlank]], 4 --[[ImGuiInputTextFlags.CharsScientific]], 268435456 --[[ImGuiInputTextFlags.LocalizeDecimalPoint]]))) ~= 0 then
-- The standard mandate that programs starts in the "C" locale where the decimal point is '.'.
-- We don't really intend to provide widespread support for it, but out of empathy for people stuck with using odd API, we support the bare minimum aka overriding the decimal point.
-- Change the default decimal_point with:
-- ImGui::GetPlatformIO()->Platform_LocaleDecimalPoint = *localeconv()->decimal_point;
-- Users of non-default decimal point (in particular ',') may be affected by word-selection logic (is_word_boundary_from_right/is_word_boundary_from_left) functions.
local g = ctx
local c_decimal_point = g.PlatformIO.Platform_LocaleDecimalPoint
if bit32.band(flags, bit32.bor(1 --[[ImGuiInputTextFlags.CharsDecimal]], 4 --[[ImGuiInputTextFlags.CharsScientific]], 268435456 --[[ImGuiInputTextFlags.LocalizeDecimalPoint]])) ~= 0 then
if c == 46 or c == 44 then -- '.' or ','
c = c_decimal_point
end
end
-- Full-width -> half-width conversion for numeric fields: https://en.wikipedia.org/wiki/Halfwidth_and_Fullwidth_Forms_(Unicode_block)
-- While this is mostly convenient, this has the side-effect for uninformed users accidentally inputting full-width characters that they may
-- scratch their head as to why it works in numerical fields vs in generic text fields it would require support in the font.
if bit32.band(flags, bit32.bor(1 --[[ImGuiInputTextFlags.CharsDecimal]], 4 --[[ImGuiInputTextFlags.CharsScientific]], 2 --[[ImGuiInputTextFlags.CharsHexadecimal]])) ~= 0 then
if c >= 0xFF01 and c <= 0xFF5E then
c = c - 0xFF01 + 0x21
end
end
-- Allow 0-9 . - + * /
if bit32.band(flags, 1 --[[ImGuiInputTextFlags.CharsDecimal]]) ~= 0 then
if not (c >= 48 and c <= 57) and (c ~= c_decimal_point) and (c ~= 45) and (c ~= 43) and (c ~= 42) and (c ~= 47) then -- 0-9 . - + * /
return char, false
end
end
-- Allow 0-9 . - + * / e E
if bit32.band(flags, 4 --[[ImGuiInputTextFlags.CharsScientific]]) ~= 0 then
if not (c >= 48 and c <= 57) and (c ~= c_decimal_point) and (c ~= 45) and (c ~= 43) and (c ~= 42) and (c ~= 47) and (c ~= 101) and (c ~= 69) then -- 0-9 . - + * / e E
return char, false
end
end
-- Allow 0-9 a-F A-F
if bit32.band(flags, 2 --[[ImGuiInputTextFlags.CharsHexadecimal]]) ~= 0 then
if not (c >= 48 and c <= 57) and not (c >= 97 and c <= 102) and not (c >= 65 and c <= 70) then -- 0-9 a-f A-F
return char, false
end
end
-- Turn a-z into A-Z
if bit32.band(flags, 8 --[[ImGuiInputTextFlags.CharsUppercase]]) ~= 0 then
if c >= 97 and c <= 122 then -- a-z
c = c + (65 - 97) -- 'A' - 'a'
end
end
if bit32.band(flags, 16 --[[ImGuiInputTextFlags.CharsNoBlank]]) ~= 0 then
if ImStd.ImCharIsBlankW(c) then
return char, false
end
end
char = c
end
-- Custom callback filter
if bit32.band(flags, 2097152 --[[ImGuiInputTextFlags.CallbackCharFilter]]) ~= 0 then
local g = GImGui
local callback_data = ImGuiInputTextCallbackData()
callback_data.Ctx = g
callback_data.ID = state.ID
callback_data.Flags = flags
callback_data.EventFlag = 2097152 --[[ImGuiInputTextFlags.CallbackCharFilter]]
callback_data.EventChar = c
callback_data.EventActivated = (state ~= nil and g.ActiveId == state.ID and g.ActiveIdIsJustActivated)
callback_data.CursorPos = state.Stb.cursor - 1 -- [Luau] callback positions are 0-based
callback_data.SelectionStart = state.Stb.select_start - 1
callback_data.SelectionEnd = state.Stb.select_end - 1
callback_data.UserData = user_data
if callback(callback_data) ~= 0 then
return char, false
end
char = callback_data.EventChar
if not callback_data.EventChar then
return char, false
end
end
return char, true
end
-- Find the shortest single replacement we can make to get from old_buf to new_buf
-- Note that this doesn't directly alter state->TextA, state->TextLen. They are expected to be made valid separately.
-- FIXME: Ideally we should transition toward (1) making InsertChars()/DeleteChars() update undo-stack (2) discourage (and keep reconcile) or obsolete (and remove reconcile) accessing buffer directly.
--- @param state ImGuiInputTextState
--- @param old_buf ImStringBuffer
--- @param old_length int
--- @param new_buf ImStringBuffer
--- @param new_length int
local function InputTextReconcileUndoState(state, old_buf, old_length, new_buf, new_length)
local shorter_length = ImMin(old_length, new_length)
local first_diff
for i = 1, shorter_length do
first_diff = i
if old_buf[first_diff] ~= new_buf[first_diff] then
break
end
end
if first_diff == old_length + 1 and first_diff == new_length + 1 then
return
end
local old_last_diff = old_length
local new_last_diff = new_length
while old_last_diff >= first_diff and new_last_diff >= first_diff do
if old_buf[old_last_diff] ~= new_buf[new_last_diff] then
break
end
old_last_diff = old_last_diff - 1
new_last_diff = new_last_diff - 1
end
local insert_len = new_last_diff - first_diff + 1
local delete_len = old_last_diff - first_diff + 1
if insert_len > 0 or delete_len > 0 then
local undostate = state.Stb.undostate
local p = stbte.createundo(undostate, first_diff, delete_len, insert_len)
if p then
for i = 0, delete_len - 1 do
undostate.undo_char[p + i] = old_buf[first_diff + i]
end
end
end
end
-- As InputText() retain textual data and we currently provide a path for user to not retain it (via local variables)
-- we need some form of hook to reapply data back to user buffer on deactivation frame. (#4714)
-- It would be more desirable that we discourage users from taking advantage of the "user not retaining data" trick,
-- but that more likely be attractive when we do have _NoLiveEdit flag available.
--- @param id ImGuiID
function ImGui.InputTextDeactivateHook(id)
local g = GImGui
local state = g.InputTextState
if id == 0 or state.ID ~= id or g.ActiveId ~= id then
return
end
if not state.EditedBefore then
return
end
-- IMGUI_DEBUG_LOG_ACTIVEID("InputTextDeactivateHook() id = 0x%08X\n", id);
g.InputTextDeactivatedState.ID = state.ID
g.InputTextDeactivatedState.ElapseFrame = g.FrameCount + 1
if bit32.band(state.Flags, 512 --[[ImGuiInputTextFlags.ReadOnly]]) ~= 0 then
g.InputTextDeactivatedState.TextA:resize(0) -- In theory this data won't be used, but clear to be neat.
else
assert(state.TextA.Data ~= nil)
-- IM_ASSERT(state.TextA[state.TextLen + 1] == 0)
g.InputTextDeactivatedState.TextA:resize(state.TextLen + 1)
ImStd.memmove(g.InputTextDeactivatedState.TextA.Data, 1, state.TextA.Data, 1, state.TextLen) -- state.TextLen + 1
end
end
-- FIXME-WORDWRAP: Bundle some of this into ImGuiTextIndex and/or extract as a different tool?
-- 'max_output_buffer_size' happens to be a meaningful optimization to avoid writing the full line_index when not necessarily needed (e.g. very large buffer, scrolled up, inactive)
--- @param flags ImGuiInputTextFlags
--- @param line_index ImGuiTextIndex
--- @param buf char[]
--- @param buf_end? int # nil: inactive multi-line path, end found with strchr()
--- @param wrap_width float
--- @param max_output_buffer_size int
--- @param want_out_buf_end boolean
--- @return int size
--- @return int? out_buf_end # [Port] 'const char** out_buf_end' output parameter becomes a 2nd return value (nil when not requested)
local function InputTextLineIndexBuild(flags, line_index, buf, buf_end, wrap_width, max_output_buffer_size, want_out_buf_end)
local g = GImGui
local size = 0
local s
local trailing_line_already_counted = false
-- [Port] 's' is a 1-based position in 'buf' (C++ s == buf + (s - 1)), Offsets store 0-based offsets like upstream.
if bit32.band(flags, 16777216 --[[ImGuiInputTextFlags.WordWrap]]) ~= 0 then
s = 1
while s < buf_end do
if size <= max_output_buffer_size then
line_index.Offsets:push_back(s - 1)
end
size = size + 1
s = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, buf, s, buf_end, wrap_width, 2 --[[ImDrawTextFlags.WrapKeepBlanks]])
if buf[s] == 10 then s = s + 1 end
end
elseif buf_end ~= nil then
s = 1
while s < buf_end do
if size <= max_output_buffer_size then
line_index.Offsets:push_back(s - 1)
end
size = size + 1
s = ImMemchr(buf, 10, s)
if s ~= nil and s >= buf_end then s = nil end -- [Port] ImMemchr() stops at the zero-terminator, not at buf_end
if s then s = s + 1 else s = buf_end end
end
else
-- Inactive path: we don't know buf_end ahead of time.
local s_eol
s = 1
while true do
if size <= max_output_buffer_size then
line_index.Offsets:push_back(s - 1)
end
size = size + 1
s_eol = ImMemchr(buf, 10, s)
if s_eol == nil then
s = s + ImStd.ImStrlen(buf, s)
trailing_line_already_counted = true
break
end
s = s_eol + 1
end
end
local out_buf_end = nil
if want_out_buf_end then
out_buf_end = s
buf_end = s
end
if size == 0 then
line_index.Offsets:push_back(0)
size = size + 1
end
if buf_end > 1 and buf[buf_end - 1] == 10 and not trailing_line_already_counted then
local old_size = size
size = size + 1
if old_size <= max_output_buffer_size then
line_index.Offsets:push_back(buf_end - 1)
end
end
return size, out_buf_end
end
--- @param t table
--- @param in_begin int
--- @param in_end int
--- @param v int
local function ImLowerBound(t, in_begin, in_end, v)
local in_p = in_begin
local count = in_end - in_begin
local floor = math.floor
local rshift = bit32.rshift
while count > 0 do
local count2 = floor(rshift(count, 1))
local mid = in_p + count2
if t[mid] < v then
in_p = mid + 1
count = count - count2 - 1
else
count = count2
end
end
return in_p
end
--- @param g ImGuiContext
--- @param state ImGuiInputTextState
--- @param line_index ImGuiTextIndex
--- @param buf char[]
--- @param buf_end int
--- @param cursor_n int
--- @return ImVec2
local function InputTextLineIndexGetPosOffset(g, state, line_index, buf, buf_end, cursor_n)
-- [Port] 'cursor_n' is a 1-based position (Stb.cursor) == C++ cursor_ptr; Offsets hold 0-based offsets, compare with cursor_n - 1.
local cursor_ptr = cursor_n
local cursor_off = cursor_n - 1
local offsets = line_index.Offsets
local it_begin = 1
local it_end = offsets.Size + 1
local it = ImLowerBound(offsets.Data, it_begin, it_end, cursor_off)
if it > it_begin then
if it == it_end or offsets.Data[it] ~= cursor_off or (state ~= nil and state.WrapWidth > 0.0 and state.LastMoveDirectionLR == 1 --[[ImGuiDir.Right]] and buf[cursor_ptr - 1] ~= 10 and buf[cursor_ptr - 1] ~= 0) then
it = it - 1
end
end
local line_no = (it == it_begin) and 0 or (it - 1) -- 0-based (index_from_ptr)
local line_start = line_index:get_line_begin(1, line_no)
local offset = ImVec2()
offset.x = InputTextCalcTextSize(g, buf, line_start, cursor_ptr, buf_end, nil, 2 --[[ImDrawTextFlags.WrapKeepBlanks]]).x
offset.y = (line_no + 1) * g.FontSize
return offset
end
-- Edit a string of text
-- - buf_size account for the zero-terminator, so a buf_size of 6 can hold "Hello" but not "Hello!".
-- - When active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while the InputText is active has no effect.
--- @param label string
--- @param hint? char[]
--- @param buf char[]
--- @param buf_size int
--- @param size_arg ImVec2
--- @param flags ImGuiInputTextFlags
--- @param callback? ImGuiInputTextCallback
--- @param callback_user_data any
local HintBufCache = setmetatable({}, { __mode = "k" })
local function HintToBuf(hint)
local t = HintBufCache[hint]
if t == nil then
t = {}
for i = 1, #hint do t[i] = string.byte(hint, i) end
t[#hint + 1] = 0
HintBufCache[hint] = t
end
return t
end
function ImGui.InputTextEx(label, hint, buf, buf_size, size_arg, flags, callback, callback_user_data)
if type(hint) == "string" then hint = HintToBuf(hint) end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
assert(buf ~= nil and buf_size >= 0)
assert(not (bit32.band(flags, 524288 --[[ImGuiInputTextFlags.CallbackHistory]]) ~= 0 and bit32.band(flags, 67108864 --[[ImGuiInputTextFlags.Multiline]]) ~= 0)) -- Can't use both together (they both use up/down keys)
assert(not (bit32.band(flags, 262144 --[[ImGuiInputTextFlags.CallbackCompletion]]) ~= 0 and bit32.band(flags, 32 --[[ImGuiInputTextFlags.AllowTabInput]]) ~= 0)) -- Can't use both together (they both use tab key)
assert(not (bit32.band(flags, 131072 --[[ImGuiInputTextFlags.ElideLeft]]) ~= 0 and bit32.band(flags, 67108864 --[[ImGuiInputTextFlags.Multiline]]) ~= 0)) -- Multiline does not work with left-trimming
assert(bit32.band(flags, 16777216 --[[ImGuiInputTextFlags.WordWrap]]) == 0 or bit32.band(flags, 1024 --[[ImGuiInputTextFlags.Password]]) == 0) -- WordWrap does not work with Password mode.
assert(bit32.band(flags, 16777216 --[[ImGuiInputTextFlags.WordWrap]]) == 0 or bit32.band(flags, 67108864 --[[ImGuiInputTextFlags.Multiline]]) ~= 0) -- WordWrap does not work in single-line mode.
local g = GImGui --[[@as ImGuiContext]]
local io = g.IO
local style = g.Style
local RENDER_SELECTION_WHEN_INACTIVE = false
local is_multiline = bit32.band(flags, 67108864 --[[ImGuiInputTextFlags.Multiline]]) ~= 0
if is_multiline then -- Open group before calling GetID() because groups tracks id created within their scope (including the scrollbar)
ImGui.BeginGroup()
end
local id = window:GetID(label)
local label_end = ImGui.FindRenderedTextEnd(label)
local label_size = ImGui.CalcTextSize(label, label_end, false)
local frame_size = ImGui.CalcItemSize(size_arg, ImGui.CalcItemWidth(), (is_multiline and g.FontSize * 8.0 or label_size.y) + style.FramePadding.y * 2.0) -- Arbitrary default of 8 lines high for multi-line
local total_size = ImVec2(frame_size.x + ((label_size.x > 0.0) and (style.ItemInnerSpacing.x + label_size.x) or 0.0), frame_size.y)
local frame_bb = ImRect(window.DC.CursorPos, window.DC.CursorPos + frame_size)
local total_bb = ImRect(frame_bb.Min, frame_bb.Min + total_size)
local draw_window = window
local inner_size = ImVec2()
ImVec2_Copy(inner_size, frame_size)
local item_data_backup = ImGuiLastItemData()
if is_multiline then
local backup_pos = ImVec2()
ImVec2_Copy(backup_pos, window.DC.CursorPos)
ImGui.ItemSize(total_bb, style.FramePadding.y)
local no_clip = (g.InputTextDeactivatedState.ID == id) or (g.ActiveId == id) or (id == g.NavActivateId) -- Mimic some of ItemAdd() logic + add InputTextDeactivatedState.ID check.
if not ImGui.ItemAdd(total_bb, id, frame_bb, 1048576 --[[ImGuiItemFlags.Inputable]]) and not no_clip then
ImGui.EndGroup()
return false
end
ImGuiLastItemData_Copy(item_data_backup, g.LastItemData)
ImVec2_Copy(window.DC.CursorPos, backup_pos)
-- Prevent NavActivation from explicit Tabbing when our widget accepts Tab inputs: this allows cycling through widgets without stopping.
if g.NavActivateId == id and (bit32.band(g.NavActivateFlags, 8 --[[ImGuiActivateFlags.FromTabbing]]) ~= 0) and (bit32.band(g.NavActivateFlags, 32 --[[ImGuiActivateFlags.FromFocusApi]]) == 0) and (bit32.band(flags, 32 --[[ImGuiInputTextFlags.AllowTabInput]]) ~= 0) then
g.NavActivateId = 0
end
-- Prevent NavActivate reactivating in BeginChild() when we are already active.
local backup_activate_id = g.NavActivateId
if g.ActiveId == id then -- Prevent reactivation
g.NavActivateId = 0
end
-- We reproduce the contents of BeginChildFrame() in order to provide 'label' so our window internal data are easier to read/debug.
ImGui.PushStyleColor(3 --[[ImGuiCol.ChildBg]], style.Colors[7 --[[ImGuiCol.FrameBg]]])
ImGui.PushStyleVar(7 --[[ImGuiStyleVar.ChildRounding]], style.FrameRounding)
ImGui.PushStyleVar(8 --[[ImGuiStyleVar.ChildBorderSize]], style.FrameBorderSize)
ImGui.PushStyleVar(2 --[[ImGuiStyleVar.WindowPadding]], ImVec2(0, 0)) -- Ensure no clip rect so mouse hover can reach FramePadding edges
local child_visible = ImGui.BeginChildEx(label, id, frame_bb:GetSize(), 1 --[[ImGuiChildFlags.Borders]], 4 --[[ImGuiWindowFlags.NoMove]])
g.NavActivateId = backup_activate_id
ImGui.PopStyleVar(3)
ImGui.PopStyleColor()
if not child_visible and not no_clip then
ImGui.EndChild()
ImGui.EndGroup()
return false
end
draw_window = g.CurrentWindow -- Child window
draw_window.DC.NavLayersActiveMaskNext = bit32.bor(draw_window.DC.NavLayersActiveMaskNext, bit32.lshift(1, draw_window.DC.NavLayerCurrent)) -- This is to ensure that EndChild() will display a navigation highlight so we can "enter" into it.
ImVec2_Copy(draw_window.DC.CursorPos, draw_window.DC.CursorPos + style.FramePadding)
inner_size.x = inner_size.x - draw_window.ScrollbarSizes.x
-- FIXME: Could this be a ImGuiChildFlags to affect the SetLastItemDataForWindow() call?
g.LastItemData.ID = id
g.LastItemData.ItemFlags = item_data_backup.ItemFlags
g.LastItemData.StatusFlags = item_data_backup.StatusFlags
else
-- Support for internal ImGuiInputTextFlags.MergedItem flag, which could be redesigned as an ItemFlags if needed (with test performed in ItemAdd)
ImGui.ItemSize(total_bb, style.FramePadding.y)
if bit32.band(flags, 134217728 --[[ImGuiInputTextFlags.TempInput]]) == 0 then
if not ImGui.ItemAdd(total_bb, id, frame_bb, 1048576 --[[ImGuiItemFlags.Inputable]]) then
return false
end
end
end
-- Ensure mouse cursor is set even after switching to keyboard/gamepad mode. May generalize further? (#6417)
local hovered = ImGui.ItemHoverable(frame_bb, id, bit32.bor(g.LastItemData.ItemFlags, 32768 --[[ImGuiItemFlags.NoNavDisableMouseHover]]))
if hovered then
ImGui.SetMouseCursor(1 --[[ImGuiMouseCursor.TextInput]])
end
if hovered and g.NavHighlightItemUnderNav then
hovered = false
end
-- We are only allowed to access the state if we are already the active widget.
local state = ImGui.GetInputTextState(id)
if bit32.band(g.LastItemData.ItemFlags, 2048 --[[ImGuiItemFlags.ReadOnly]]) ~= 0 then
flags = bit32.bor(flags, 512 --[[ImGuiInputTextFlags.ReadOnly]])
end
local is_readonly = bit32.band(flags, 512 --[[ImGuiInputTextFlags.ReadOnly]]) ~= 0
local is_password = bit32.band(flags, 1024 --[[ImGuiInputTextFlags.Password]]) ~= 0
local is_undoable = bit32.band(flags, 65536 --[[ImGuiInputTextFlags.NoUndoRedo]]) == 0
local is_resizable = bit32.band(flags, 4194304 --[[ImGuiInputTextFlags.CallbackResize]]) ~= 0
if is_resizable then
assert(callback ~= nil) -- Must provide a callback if you set the ImGuiInputTextFlags.CallbackResize flag!
end
-- Word-wrapping: enforcing a fixed width not altered by vertical scrollbar makes things easier, notably to track cursor reliably and avoid one-frame glitches.
-- Instead of using ImGuiWindowFlags_AlwaysVerticalScrollbar we account for that space if the scrollbar is not visible.
local is_wordwrap = bit32.band(flags, 16777216 --[[ImGuiInputTextFlags.WordWrap]]) ~= 0
local wrap_width = 0.0
if is_wordwrap then
wrap_width = ImMax(1.0, ImGui.GetContentRegionAvail().x + (draw_window.ScrollbarY and 0.0 or -g.Style.ScrollbarSize))
end
local user_clicked = hovered and io.MouseClicked[0]
local input_requested_by_nav = (g.ActiveId ~= id) and (g.NavActivateId == id) and ((bit32.band(g.NavActivateFlags, 1 --[[ImGuiActivateFlags.PreferInput]]) ~= 0) or (g.NavInputSource == 2 --[[ImGuiInputSource.Keyboard]]))
local input_requested_by_reactivate = (g.InputTextReactivateId == id) -- for io.ConfigInputTextEnterKeepActive
local input_requested_by_user = (user_clicked) or (g.ActiveId == 0 and bit32.band(flags, 134217728 --[[ImGuiInputTextFlags.TempInput]]) ~= 0 and g.InputTextDeactivatedState.ID ~= id)
local scrollbar_id = (is_multiline and state ~= nil) and ImGui.GetWindowScrollbarID(draw_window, 2 --[[ImGuiAxis.Y]]) or 0
local user_scroll_finish = is_multiline and state ~= nil and g.ActiveId == 0 and g.ActiveIdPreviousFrame == scrollbar_id
local user_scroll_active = is_multiline and state ~= nil and g.ActiveId == scrollbar_id
local clear_active_id = false
local select_all = false
local scroll_y = is_multiline and draw_window.Scroll.y or 3.4028234663853e+38 --[[FLT_MAX]]
local init_reload_from_user_buf = (state ~= nil and state.WantReloadUserBuf)
local init_changed_specs_multiline = (state ~= nil and (state.Stb.single_line ~= (not is_multiline))) -- state ~= nil means its our state.
local init_changed_specs_readonly = (state ~= nil and (bit32.band(bit32.bxor(state.Flags, flags), 512 --[[ImGuiInputTextFlags.ReadOnly]]) ~= 0)) -- state ~= nil means its our state.
local init_make_active = (input_requested_by_user or input_requested_by_nav or input_requested_by_reactivate or user_scroll_finish)
if init_reload_from_user_buf then
local new_len = ImStd.ImStrlen(buf)
assert(new_len + 1 <= buf_size, "Is your input buffer properly zero-terminated?")
state.WantReloadUserBuf = false
--- @cast state ImGuiInputTextState
InputTextReconcileUndoState(state, state.TextA.Data, state.TextLen, buf, new_len)
state.TextA:resize(buf_size + 1) -- we use +1 to make sure that .Data is always pointing to at least an empty string.
state.TextLen = new_len
ImStd.memmove(state.TextA.Data, 1, buf, 1, state.TextLen + 1)
state.Stb.select_start = state.ReloadSelectionStart
state.Stb.cursor = state.ReloadSelectionEnd
state.Stb.select_end = state.ReloadSelectionEnd -- will be clamped to bounds below
elseif (init_make_active and g.ActiveId ~= id) or init_changed_specs_multiline or init_changed_specs_readonly then
-- Access state even if we don't own it yet.
state = g.InputTextState
state:CursorAnimReset()
-- Backup state of deactivating item so they'll have a chance to do a write to output buffer on the same frame they report IsItemDeactivatedAfterEdit (#4714)
if (state.ID ~= id and state.ID == g.ActiveId and (init_make_active and g.ActiveId ~= id)) then
ImGui.InputTextDeactivateHook(state.ID) -- <-- this is essentially an earlier call to what SetActiveID() would do below.
end
-- Take a copy of the initial buffer value.
-- From the moment we focused we are normally ignoring the content of 'buf' (unless we are in read-only mode)
local buf_len = ImStd.ImStrlen(buf)
assert(((buf_len + 1 <= buf_size) or (buf_len == 0 and buf_size == 0)), "Is your input buffer properly zero-terminated?")
if not user_scroll_finish then
state.TextToRevertTo:resize(buf_len + 1) -- UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string.
ImStd.memmove(state.TextToRevertTo.Data, 1, buf, 1, buf_len + 1)
end
-- Preserve cursor position and undo/redo stack if we come back to same widget
-- FIXME: Since we reworked this on 2022/06, may want to differentiate recycle_cursor vs recycle_undostate?
local recycle_state = (state.ID == id and not init_changed_specs_multiline)
if (recycle_state and not init_changed_specs_readonly and (state.TextLen ~= buf_len or (state.TextA.Data == nil or ImStd.strncmp(state.TextA.Data, buf, buf_len) ~= 0))) then
recycle_state = false
end
-- Start edition
state.ID = id
state.TextLen = buf_len
state.EditedBefore = false
if not is_readonly then
state.TextA:resize(buf_size + 1) -- we use +1 to make sure that .Data is always pointing to at least an empty string.
ImStd.memmove(state.TextA.Data, 1, buf, 1, state.TextLen + 1)
end
-- Find initial scroll position for right alignment
ImVec2_Copy(state.Scroll, ImVec2(0.0, 0.0))
if bit32.band(flags, 131072 --[[ImGuiInputTextFlags.ElideLeft]]) ~= 0 then
state.Scroll.x = state.Scroll.x + ImMax(0.0, ImGui.CalcTextSize(table.concat(buf)).x - frame_size.x + style.FramePadding.x * 2.0) -- FIXME: no table.concat here
end
-- Recycle existing cursor/selection/undo stack but clamp position
-- Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler.
if not recycle_state then
stbte.initialize_state(state.Stb, not is_multiline)
end
if not is_multiline then
if bit32.band(flags, 4096 --[[ImGuiInputTextFlags.AutoSelectAll]]) ~= 0 then
select_all = true
end
if input_requested_by_nav and (not recycle_state or (bit32.band(g.NavActivateFlags, 4 --[[ImGuiActivateFlags.TryToPreserveState]]) == 0)) then
select_all = true
end
if user_clicked and io.KeyCtrl then
select_all = true
end
end
if bit32.band(flags, 2048 --[[ImGuiInputTextFlags.AlwaysOverwrite]]) ~= 0 then
state.Stb.insert_mode = true -- stb field name is indeed incorrect (see #2863)
end
end
local is_osx = io.ConfigMacOSXBehaviors
if init_make_active and g.ActiveId ~= id then
assert(state ~= nil and state.ID == id)
ImGui.SetActiveID(id, window)
ImGui.SetFocusID(id, window)
ImGui.FocusWindow(window)
if input_requested_by_nav then
ImGui.SetNavCursorVisibleAfterMove()
end
end
if g.ActiveId == id then
-- Declare some inputs, the other are registered and polled via Shortcut() routing system.
-- FIXME: The reason we don't use Shortcut() is we would need a routing flag to specify multiple mods, or to all mods combination into individual shortcuts.
local always_owned_keys = { 513 --[[ImGuiKey.LeftArrow]], 514 --[[ImGuiKey.RightArrow]], 522 --[[ImGuiKey.Delete]], 523 --[[ImGuiKey.Backspace]], 519 --[[ImGuiKey.Home]], 520 --[[ImGuiKey.End]] }
for _, key in ipairs(always_owned_keys) do
ImGui.SetKeyOwner(key, id)
end
if user_clicked then
ImGui.SetKeyOwner(656 --[[ImGuiKey.MouseLeft]], id)
end
g.ActiveIdUsingNavDirMask = bit32.bor(g.ActiveIdUsingNavDirMask, bit32.lshift(1, 0 --[[ImGuiDir.Left]]), bit32.lshift(1, 1 --[[ImGuiDir.Right]]))
if is_multiline or (bit32.band(flags, 524288 --[[ImGuiInputTextFlags.CallbackHistory]]) ~= 0) then
g.ActiveIdUsingNavDirMask = bit32.bor(g.ActiveIdUsingNavDirMask, bit32.lshift(1, 2 --[[ImGuiDir.Up]]), bit32.lshift(1, 3 --[[ImGuiDir.Down]]))
ImGui.SetKeyOwner(515 --[[ImGuiKey.UpArrow]], id)
ImGui.SetKeyOwner(516 --[[ImGuiKey.DownArrow]], id)
end
if is_multiline then
ImGui.SetKeyOwner(517 --[[ImGuiKey.PageUp]], id)
ImGui.SetKeyOwner(518 --[[ImGuiKey.PageDown]], id)
end
-- FIXME: May be a problem to always steal Alt on OSX, would ideally still allow an uninterrupted Alt down-up to toggle menu
if is_osx then
ImGui.SetKeyOwner(16384 --[[ImGuiMod_Alt]], id)
end
-- Expose scroll in a manner that is agnostic to us using a child window
if is_multiline and state ~= nil then
state.Scroll.y = draw_window.Scroll.y
end
-- Read-only mode always ever read from source buffer. Refresh TextLen when active.
if is_readonly and state ~= nil then
state.TextLen = ImStd.ImStrlen(buf)
end
if state ~= nil then
state:CursorClamp()
end
-- if is_readonly and state ~= nil then
-- state.TextA:clear() -- Uncomment to facilitate debugging, but we otherwise prefer to keep/amortize th allocation.
-- end
end
if state ~= nil then
state.TextSrc = is_readonly and buf or state.TextA.Data
end
-- We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function)
if g.ActiveId == id and state == nil then
ImGui.ClearActiveID()
end
-- Release focus when we click outside
if g.ActiveId == id and io.MouseClicked[0] and not init_make_active then
clear_active_id = true
end
-- Lock the decision of whether we are going to take the path displaying the cursor or selection
local render_cursor = (g.ActiveId == id) or (state and user_scroll_active) --[[@as bool]]
local render_selection = state and (state:HasSelection() or select_all) and (RENDER_SELECTION_WHEN_INACTIVE or render_cursor)
local value_changed = false
local validated = false
-- Select the buffer to render.
local buf_display_from_state = (render_cursor or render_selection or g.ActiveId == id) and not is_readonly and state ~= nil
local is_displaying_hint = (hint ~= nil and (buf_display_from_state and state.TextA.Data or buf)[1] == 0)
-- Password pushes a temporary font with only a fallback glyph
if is_password and not is_displaying_hint then
ImGui.PushPasswordFont()
end
if state ~= nil and state.ID == id then
state.Flags = flags
state.EditedThisFrame = false; state.ValidatedThisFrame = false
-- Word-wrapping: attempt to keep cursor in view while resizing frame/parent (FIXME-WORDWRAP: would be better to preserve same relative offset)
if is_wordwrap and state.WrapWidth ~= wrap_width then
state.CursorCenterY = true
state.WrapWidth = wrap_width
render_cursor = true
end
end
-- Process mouse inputs and character inputs
if g.ActiveId == id then
--- @cast state ImGuiInputTextState
assert(state ~= nil)
state.BufCapacity = buf_size
state.WrapWidth = wrap_width
-- Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.
-- Down the line we should have a cleaner library-wide concept of Selected vs Active.
g.ActiveIdAllowOverlap = not io.MouseDown[0]
-- Edit in progress
local mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + state.Scroll.x
local mouse_y = (is_multiline and (io.MousePos.y - draw_window.DC.CursorPos.y) or (g.FontSize * 0.5))
if select_all then
state:SelectAll()
state.SelectedAllMouseLock = true
elseif hovered and io.MouseClickedCount[0] >= 2 and not io.KeyShift then
stbte.click(state, state.Stb, mouse_x, mouse_y)
local multiclick_count = (io.MouseClickedCount[0] - 2)
if multiclick_count % 2 == 0 then
-- Double-click: Select word
-- We always use the "Mac" word advance for double-click select vs Ctrl+Right which use the platform dependent variant:
-- FIXME: There are likely many ways to improve this behavior, but there's no "right" behavior (depends on use-case, software, OS)
local is_bol = (state.Stb.cursor == 1) or ImStb.TEXTEDIT_GETCHAR(state, state.Stb.cursor - 1) == 10
if stbte.HAS_SELECTION(state.Stb) or not is_bol then
state:OnKeyPressed(2097164 --[[STB_TEXTEDIT_K_WORDLEFT]])
end
-- state:OnKeyPressed(bit32.bor(STB_TEXTEDIT_K_WORDRIGHT, STB_TEXTEDIT_K_SHIFT))
if not stbte.HAS_SELECTION(state.Stb) then
stbte.prep_selection_at_cursor(state.Stb)
end
state.Stb.cursor = ImStb.TEXTEDIT_MOVEWORDRIGHT_MAC(state, state.Stb.cursor)
state.Stb.select_end = state.Stb.cursor
stbte.clamp(state, state.Stb)
else
-- Triple-click: Select line
local is_eol = ImStb.TEXTEDIT_GETCHAR(state, state.Stb.cursor) == 10
state.WrapWidth = 0.0 -- Temporarily disable wrapping so we use real line start.
state:OnKeyPressed(2097156 --[[STB_TEXTEDIT_K_LINESTART]])
state:OnKeyPressed(bit32.bor(2097157 --[[STB_TEXTEDIT_K_LINEEND]], 4194304 --[[STB_TEXTEDIT_K_SHIFT]]))
state:OnKeyPressed(bit32.bor(2097153 --[[STB_TEXTEDIT_K_RIGHT]], 4194304 --[[STB_TEXTEDIT_K_SHIFT]]))
state.WrapWidth = wrap_width
if not is_eol and is_multiline then
state.Stb.select_start, state.Stb.select_end = state.Stb.select_end, state.Stb.select_start
state.Stb.cursor = state.Stb.select_end
end
state.CursorFollow = false
end
state:CursorAnimReset()
elseif io.MouseClicked[0] and not state.SelectedAllMouseLock then
if hovered then
if io.KeyShift then
stbte.drag(state, state.Stb, mouse_x, mouse_y)
else
stbte.click(state, state.Stb, mouse_x, mouse_y)
end
state:CursorAnimReset()
end
elseif io.MouseDown[0] and not state.SelectedAllMouseLock and (io.MouseDelta.x ~= 0.0 or io.MouseDelta.y ~= 0.0) then
stbte.drag(state, state.Stb, mouse_x, mouse_y)
state:CursorAnimReset()
state.CursorFollow = true
end
if state.SelectedAllMouseLock and not io.MouseDown[0] then
state.SelectedAllMouseLock = false
end
-- We expect backends to emit a Tab key but some also emit a Tab character which we ignore (#2467, #1336)
-- (For Tab and Enter: Win32/SFML/Allegro are sending both keys and chars, GLFW and SDL are only sending keys. For Space they all send all threes)
if bit32.band(flags, 32 --[[ImGuiInputTextFlags.AllowTabInput]]) ~= 0 and not is_readonly then
if ImGui.Shortcut(512 --[[ImGuiKey.Tab]], 1 --[[ImGuiInputFlags.Repeat]], id) then
local c = 9 -- Insert TAB
local ret2
c, ret2 = InputTextFilterCharacter(g, state, c, callback, callback_user_data)
if ret2 then
state:OnCharPressed(c)
end
end
-- FIXME: Implement Shift+Tab
-- if ImGui.Shortcut(bit32.bor(ImGuiKey.Tab, ImGuiMod_Shift), ImGuiInputFlags.Repeat, id) then
-- end
end
-- Process regular text input (before we check for Return because using some IME will effectively send a Return?)
-- We ignore Ctrl inputs, but need to allow Alt+Ctrl as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters.
local ignore_char_inputs = io.KeyCtrl and not io.KeyAlt
if io.InputQueueCharacters.Size > 0 then
if not ignore_char_inputs and not is_readonly and not input_requested_by_nav then
for n = 1, io.InputQueueCharacters.Size do
repeat
-- Insert character if they pass filtering
local c = io.InputQueueCharacters[n]
if c == 9 then -- Skip Tab, see above.
break
end
local ret2
c, ret2 = InputTextFilterCharacter(g, state, c, callback, callback_user_data)
if ret2 then
state:OnCharPressed(c)
end
until true
end
end
-- Consume characters
io.InputQueueCharacters:resize(0)
end
end
-- Process other shortcuts/key-presses
local revert_edit = false
if g.ActiveId == id and not g.ActiveIdIsJustActivated and not clear_active_id then
assert(state ~= nil)
--- @cast state ImGuiInputTextState
local row_count_per_page = ImMax(ImTrunc((inner_size.y - style.FramePadding.y) / g.FontSize), 1)
state.Stb.row_count_per_page = row_count_per_page
local k_mask = (io.KeyShift and 4194304 --[[STB_TEXTEDIT_K_SHIFT]] or 0)
local is_wordmove_key_down = is_osx and io.KeyAlt or io.KeyCtrl -- OS X style: Text editing cursor movement using Alt instead of Ctrl
local is_startend_key_down = is_osx and io.KeyCtrl and not io.KeySuper and not io.KeyAlt -- OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End
-- Using Shortcut() with ImGuiInputFlags.RouteFocused (default policy) to allow routing operations for other code (e.g. calling window trying to use Ctrl+A and Ctrl+B: former would be handled by InputText)
-- Otherwise we could simply assume that we own the keys as we are active.
local f_repeat = 1 --[[ImGuiInputFlags.Repeat]]
local is_cut = (ImGui.Shortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 569 --[[ImGuiKey.X]]), f_repeat, id) or ImGui.Shortcut(bit32.bor(8192 --[[ImGuiMod_Shift]], 522 --[[ImGuiKey.Delete]]), f_repeat, id)) and not is_readonly and not is_password and (not is_multiline or state:HasSelection())
local is_copy = (ImGui.Shortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 548 --[[ImGuiKey.C]]), 0, id) or ImGui.Shortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 521 --[[ImGuiKey.Insert]]), 0, id)) and not is_password and (not is_multiline or state:HasSelection())
local is_paste = (ImGui.Shortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 567 --[[ImGuiKey.V]]), f_repeat, id) or ImGui.Shortcut(bit32.bor(8192 --[[ImGuiMod_Shift]], 521 --[[ImGuiKey.Insert]]), f_repeat, id)) and not is_readonly
local is_undo = (ImGui.Shortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 571 --[[ImGuiKey.Z]]), f_repeat, id)) and not is_readonly and is_undoable
local is_redo = (ImGui.Shortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 570 --[[ImGuiKey.Y]]), f_repeat, id) or ImGui.Shortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 8192 --[[ImGuiMod_Shift]], 571 --[[ImGuiKey.Z]]), f_repeat, id)) and not is_readonly and is_undoable
local is_select_all = ImGui.Shortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 546 --[[ImGuiKey.A]]), 0, id)
-- We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful.
local nav_gamepad_active = (bit32.band(io.ConfigFlags, 2 --[[ImGuiConfigFlags.NavEnableGamepad]]) ~= 0) and (bit32.band(io.BackendFlags, 1 --[[ImGuiBackendFlags.HasGamepad]]) ~= 0)
local is_enter = ImGui.Shortcut(525 --[[ImGuiKey.Enter]], f_repeat, id) or ImGui.Shortcut(627 --[[ImGuiKey.KeypadEnter]], f_repeat, id)
local is_ctrl_enter = ImGui.Shortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 525 --[[ImGuiKey.Enter]]), f_repeat, id) or ImGui.Shortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 627 --[[ImGuiKey.KeypadEnter]]), f_repeat, id)
local is_shift_enter = ImGui.Shortcut(bit32.bor(8192 --[[ImGuiMod_Shift]], 525 --[[ImGuiKey.Enter]]), f_repeat, id) or ImGui.Shortcut(bit32.bor(8192 --[[ImGuiMod_Shift]], 627 --[[ImGuiKey.KeypadEnter]]), f_repeat, id)
local is_gamepad_validate = nav_gamepad_active and ImGui.IsKeyPressed(ImGuiKey.NavGamepadActivate, false)
local is_cancel = ImGui.Shortcut(526 --[[ImGuiKey.Escape]], f_repeat, id) or (nav_gamepad_active and ImGui.Shortcut(ImGuiKey.NavGamepadCancel, f_repeat, id))
-- FIXME: Should use more Shortcut() and reduce IsKeyPressed()+SetKeyOwner(), but requires modifiers combination to be taken account of.
-- FIXME-OSX: Missing support for Alt(option)+Right/Left = go to end of line, or next line if already in end of line.
if ImGui.IsKeyPressed(513 --[[ImGuiKey.LeftArrow]]) then
state:OnKeyPressed(bit32.bor(is_startend_key_down and 2097156 --[[STB_TEXTEDIT_K_LINESTART]] or (is_wordmove_key_down and 2097164 --[[STB_TEXTEDIT_K_WORDLEFT]] or 2097152 --[[STB_TEXTEDIT_K_LEFT]]), k_mask))
elseif ImGui.IsKeyPressed(514 --[[ImGuiKey.RightArrow]]) then
state:OnKeyPressed(bit32.bor(is_startend_key_down and 2097157 --[[STB_TEXTEDIT_K_LINEEND]] or (is_wordmove_key_down and 2097165 --[[STB_TEXTEDIT_K_WORDRIGHT]] or 2097153 --[[STB_TEXTEDIT_K_RIGHT]]), k_mask))
elseif ImGui.IsKeyPressed(515 --[[ImGuiKey.UpArrow]]) and is_multiline then
if io.KeyCtrl then
ImGui.SetScrollY(draw_window, ImMax(draw_window.Scroll.y - g.FontSize, 0.0))
else
state:OnKeyPressed(bit32.bor(is_startend_key_down and 2097158 --[[STB_TEXTEDIT_K_TEXTSTART]] or 2097154 --[[STB_TEXTEDIT_K_UP]], k_mask))
end
elseif ImGui.IsKeyPressed(516 --[[ImGuiKey.DownArrow]]) and is_multiline then
if io.KeyCtrl then
ImGui.SetScrollY(draw_window, ImMin(draw_window.Scroll.y + g.FontSize, ImGui.GetScrollMaxY()))
else
state:OnKeyPressed(bit32.bor(is_startend_key_down and 2097159 --[[STB_TEXTEDIT_K_TEXTEND]] or 2097155 --[[STB_TEXTEDIT_K_DOWN]], k_mask))
end
elseif ImGui.IsKeyPressed(517 --[[ImGuiKey.PageUp]]) and is_multiline then
state:OnKeyPressed(bit32.bor(2097166 --[[STB_TEXTEDIT_K_PGUP]], k_mask))
scroll_y = scroll_y - row_count_per_page * g.FontSize
elseif ImGui.IsKeyPressed(518 --[[ImGuiKey.PageDown]]) and is_multiline then
state:OnKeyPressed(bit32.bor(2097167 --[[STB_TEXTEDIT_K_PGDOWN]], k_mask))
scroll_y = scroll_y + row_count_per_page * g.FontSize
elseif ImGui.IsKeyPressed(519 --[[ImGuiKey.Home]]) then
state:OnKeyPressed(io.KeyCtrl and bit32.bor(2097158 --[[STB_TEXTEDIT_K_TEXTSTART]], k_mask) or bit32.bor(2097156 --[[STB_TEXTEDIT_K_LINESTART]], k_mask))
elseif ImGui.IsKeyPressed(520 --[[ImGuiKey.End]]) then
state:OnKeyPressed(io.KeyCtrl and bit32.bor(2097159 --[[STB_TEXTEDIT_K_TEXTEND]], k_mask) or bit32.bor(2097157 --[[STB_TEXTEDIT_K_LINEEND]], k_mask))
elseif ImGui.IsKeyPressed(522 --[[ImGuiKey.Delete]]) and not is_readonly and not is_cut then
if not state:HasSelection() then
-- OSX doesn't seem to have Super+Delete to delete until end-of-line, so we don't emulate that (as opposed to Super+Backspace)
if is_wordmove_key_down then
state:OnKeyPressed(bit32.bor(2097165 --[[STB_TEXTEDIT_K_WORDRIGHT]], 4194304 --[[STB_TEXTEDIT_K_SHIFT]]))
end
end
state:OnKeyPressed(bit32.bor(2097160 --[[STB_TEXTEDIT_K_DELETE]], k_mask))
elseif ImGui.IsKeyPressed(523 --[[ImGuiKey.Backspace]]) and not is_readonly then
if not state:HasSelection() then
if is_wordmove_key_down then
state:OnKeyPressed(bit32.bor(2097164 --[[STB_TEXTEDIT_K_WORDLEFT]], 4194304 --[[STB_TEXTEDIT_K_SHIFT]]))
elseif is_osx and io.KeyCtrl and not io.KeyAlt and not io.KeySuper then
state:OnKeyPressed(bit32.bor(2097156 --[[STB_TEXTEDIT_K_LINESTART]], 4194304 --[[STB_TEXTEDIT_K_SHIFT]]))
end
end
state:OnKeyPressed(bit32.bor(2097161 --[[STB_TEXTEDIT_K_BACKSPACE]], k_mask))
elseif is_enter or is_ctrl_enter or is_shift_enter or is_gamepad_validate then
-- Determine if we turn Enter into a \n character
local ctrl_enter_for_new_line = bit32.band(flags, 256 --[[ImGuiInputTextFlags.CtrlEnterForNewLine]]) ~= 0
local is_new_line = is_multiline and not is_gamepad_validate and (is_shift_enter or (is_enter and not ctrl_enter_for_new_line) or (is_ctrl_enter and ctrl_enter_for_new_line))
if not is_new_line then
validated = true
clear_active_id = true
if io.ConfigInputTextEnterKeepActive and not is_multiline and not is_ctrl_enter and not is_shift_enter then
-- Queue reactivation, so that e.g. IsItemDeactivatedAfterEdit() will work. (#9001)
state:SelectAll() -- No need to scroll
g.InputTextReactivateId = id -- Mark for reactivation on next frame
end
elseif not is_readonly then
-- Insert new line
local c = 10
local ret2
c, ret2 = InputTextFilterCharacter(g, state, c, callback, callback_user_data)
if ret2 then
state:OnCharPressed(c)
end
end
elseif is_cancel then
if bit32.band(flags, 128 --[[ImGuiInputTextFlags.EscapeClearsAll]]) ~= 0 then
if state.TextA.Data[1] ~= 0 then
revert_edit = true
else
render_cursor = false
render_selection = false
clear_active_id = true
end
else
clear_active_id = true
revert_edit = true
render_cursor = false
render_selection = false
end
elseif is_undo or is_redo then
state:OnKeyPressed(is_undo and 2097162 --[[STB_TEXTEDIT_K_UNDO]] or 2097163 --[[STB_TEXTEDIT_K_REDO]])
state:ClearSelection()
elseif is_select_all then
state:SelectAll()
state.CursorFollow = true
elseif is_cut or is_copy then
-- Cut, Copy
if g.PlatformIO.Platform_SetClipboardTextFn ~= nil then
-- SetClipboardText() only takes null terminated strings + state.TextSrc may point to read-only user buffer, so we need to make a copy.
local ib = state:HasSelection() and ImMin(state.Stb.select_start, state.Stb.select_end) or 1
local ie = state:HasSelection() and ImMax(state.Stb.select_start, state.Stb.select_end) or state.TextLen + 1
g.TempBuffer:reserve(ie - ib + 1)
ImStd.memmove(g.TempBuffer.Data, 1, state.TextSrc, ib, ie - ib)
g.TempBuffer.Data[ie - ib + 1] = 0
ImGui.SetClipboardText(ImGui._ByteArrayToString(g.TempBuffer.Data, 1, ie - ib + 1))
end
if is_cut then
if not state:HasSelection() then
state:SelectAll()
end
state.CursorFollow = true
stbte.cut(state, state.Stb)
end
elseif is_paste then
local clipboard = ImGui.GetClipboardText()
if clipboard then
-- Filter pasted buffer
local clipboard_len = #clipboard
local clipboard_end = clipboard_len + 1
local clipboard_filtered = ImVector()
clipboard_filtered:reserve(clipboard_len + 1)
local s
local i = 1
while i <= clipboard_len do
repeat
s = string.byte(clipboard, i)
local c, ret2
local in_len
in_len, c = ImStd.ImTextCharFromUtf8(clipboard, i, clipboard_end)
i = i + in_len
c, ret2 = InputTextFilterCharacter(g, state, c, callback, callback_user_data, true)
if not ret2 then
break -- continue
end
local c_utf8 = {0, 0, 0, 0, 0}
ImStd.ImTextCharToUtf8(c_utf8, c)
local out_len = ImStd.ImStrlen(c_utf8)
clipboard_filtered:resize(clipboard_filtered.Size + out_len)
ImStd.memmove(clipboard_filtered.Data, clipboard_filtered.Size - out_len + 1, c_utf8, 1, out_len)
until true
end
if clipboard_filtered.Size > 0 then -- If everything was filtered, ignore the pasting operation
clipboard_filtered:push_back(0)
stbte.paste(state, state.Stb, clipboard_filtered.Data, clipboard_filtered.Size - 1)
state.CursorFollow = true
end
end
end
-- Update render selection flag after events have been handled, so selection highlight can be displayed during the same frame.
render_selection = render_selection or (state:HasSelection() and (RENDER_SELECTION_WHEN_INACTIVE or render_cursor))
end
-- Process revert and user callbacks
local apply_new_text = nil
local apply_new_text_length = 0
if g.ActiveId == id then
assert(state ~= nil)
--- @cast state ImGuiInputTextState
if revert_edit and not is_readonly then
if bit32.band(flags, 128 --[[ImGuiInputTextFlags.EscapeClearsAll]]) ~= 0 then
-- Clear input
assert(state.TextA.Data[1] ~= 0)
apply_new_text = {0}
apply_new_text_length = 0
value_changed = true
local empty_string = {0}
ImStb.stb_textedit_replace(state, state.Stb, empty_string, 0)
elseif ImStd.strcmp(state.TextA.Data, state.TextToRevertTo.Data) ~= 0 then
apply_new_text = state.TextToRevertTo.Data
apply_new_text_length = state.TextToRevertTo.Size - 1
-- Restore initial value. Only return true if restoring to the initial value changes the current buffer contents.
-- Push records into the undo stack so we can Ctrl+Z the revert operation itself
value_changed = true
ImStb.stb_textedit_replace(state, state.Stb, state.TextToRevertTo.Data, state.TextToRevertTo.Size - 1)
end
end
-- User callback
if bit32.band(flags, bit32.bor(262144 --[[ImGuiInputTextFlags.CallbackCompletion]], 524288 --[[ImGuiInputTextFlags.CallbackHistory]], 8388608 --[[ImGuiInputTextFlags.CallbackEdit]], 1048576 --[[ImGuiInputTextFlags.CallbackAlways]])) ~= 0 then
assert(callback ~= nil)
-- The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment.
local event_flag = 0
local event_key = 0 --[[ImGuiKey.None]]
if bit32.band(flags, 262144 --[[ImGuiInputTextFlags.CallbackCompletion]]) ~= 0 and ImGui.Shortcut(512 --[[ImGuiKey.Tab]], 0, id) then
event_flag = 262144 --[[ImGuiInputTextFlags.CallbackCompletion]]
event_key = 512 --[[ImGuiKey.Tab]]
elseif bit32.band(flags, 524288 --[[ImGuiInputTextFlags.CallbackHistory]]) ~= 0 and ImGui.IsKeyPressed(515 --[[ImGuiKey.UpArrow]]) then
event_flag = 524288 --[[ImGuiInputTextFlags.CallbackHistory]]
event_key = 515 --[[ImGuiKey.UpArrow]]
elseif bit32.band(flags, 524288 --[[ImGuiInputTextFlags.CallbackHistory]]) ~= 0 and ImGui.IsKeyPressed(516 --[[ImGuiKey.DownArrow]]) then
event_flag = 524288 --[[ImGuiInputTextFlags.CallbackHistory]]
event_key = 516 --[[ImGuiKey.DownArrow]]
elseif bit32.band(flags, 8388608 --[[ImGuiInputTextFlags.CallbackEdit]]) ~= 0 and state.EditedThisFrame then
event_flag = 8388608 --[[ImGuiInputTextFlags.CallbackEdit]]
elseif bit32.band(flags, 1048576 --[[ImGuiInputTextFlags.CallbackAlways]]) ~= 0 then
event_flag = 1048576 --[[ImGuiInputTextFlags.CallbackAlways]]
end
if event_flag ~= 0 then
local callback_data = ImGuiInputTextCallbackData()
callback_data.Ctx = g
callback_data.ID = id
callback_data.Flags = flags
callback_data.EventFlag = event_flag
callback_data.EventActivated = (g.ActiveId == state.ID and g.ActiveIdIsJustActivated)
callback_data.UserData = callback_user_data
-- FIXME-OPT: Undo stack reconcile needs a backup of the data until we rework API, see #7925
local callback_buf = is_readonly and buf or state.TextA.Data
assert(callback_buf == state.TextSrc)
state.CallbackTextBackup:resize(state.TextLen + 1)
ImStd.memmove(state.CallbackTextBackup.Data, 1, callback_buf, 1, state.TextLen + 1)
callback_data.EventKey = event_key
callback_data.Buf = callback_buf
callback_data.BufTextLen = state.TextLen
callback_data.BufSize = state.BufCapacity
callback_data.BufDirty = false
callback_data.CursorPos = state.Stb.cursor - 1 -- [Luau] callback positions are 0-based, stb ones 1-based
callback_data.SelectionStart = state.Stb.select_start - 1
callback_data.SelectionEnd = state.Stb.select_end - 1
-- Call user code
callback(callback_data)
-- Read back what user may have modified
callback_buf = is_readonly and buf or state.TextA.Data -- Pointer may have been invalidated by a resize callback
assert(callback_data.Buf == callback_buf) -- Invalid to modify those fields
assert(callback_data.BufSize == state.BufCapacity)
assert(callback_data.Flags == flags)
if callback_data.BufDirty or callback_data.CursorPos ~= state.Stb.cursor - 1 then
state.CursorFollow = true
end
state.Stb.cursor = ImClamp(callback_data.CursorPos, 0, callback_data.BufTextLen) + 1
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) + 1
if callback_data.BufDirty then
-- Callback may update buffer and thus set buf_dirty even in read-only mode.
assert(callback_data.BufTextLen == ImStd.ImStrlen(callback_data.Buf)) -- You need to maintain BufTextLen if you change the text!
InputTextReconcileUndoState(state, state.CallbackTextBackup.Data, state.CallbackTextBackup.Size - 1, callback_data.Buf, callback_data.BufTextLen)
state.TextLen = callback_data.BufTextLen -- Assume correct length and valid UTF-8 from user, saves us an extra strlen()
state.EditedBefore = true; state.EditedThisFrame = true
state:CursorAnimReset()
end
end
end
-- Write back result string if modified.
-- FIXME-OPT: Could mark dirty state from the stb_textedit callbacks
if not is_readonly then
if bit32.band(g.LastItemData.ItemFlags, 128 --[[ImGuiItemFlags.LiveEditOnInputText]]) ~= 0 then
-- Apply when modified
if (ImStd.strcmp(state.TextSrc, buf) ~= 0) then
apply_new_text = state.TextSrc
apply_new_text_length = state.TextLen
value_changed = true
end
else
-- Apply on validation/deactivation, otherwise cancel out previous apply attempts (e.g. revert)
value_changed = ((validated or clear_active_id or revert_edit) and ImStd.strcmp(state.TextSrc, buf) ~= 0)
apply_new_text = value_changed and state.TextSrc or NULL
apply_new_text_length = value_changed and state.TextLen or 0
end
end
end
-- Handle reapplying final data on deactivation (see InputTextDeactivateHook() for details)
-- This is used when e.g. losing focus or tabbing out into another InputText() which may already be using the temp buffer.
if g.InputTextDeactivatedState.ID == id then
-- The state only exists after an Edit. IsItemDeactivatedAfterEdit() is not valid in every code path (see "widgets_inputtext_status_noliveedit" test).
if ((g.ActiveId ~= id and ImGui.IsItemDeactivated()) or (g.ActiveId == id and bit32.band(flags, 134217728 --[[ImGuiInputTextFlags.TempInput]]) ~= 0)) then
if (not is_readonly and ImStd.strcmp(g.InputTextDeactivatedState.TextA.Data, buf) ~= 0) then
apply_new_text = g.InputTextDeactivatedState.TextA.Data
apply_new_text_length = g.InputTextDeactivatedState.TextA.Size - 1
value_changed = true
-- IMGUI_DEBUG_LOG("InputText(): apply Deactivated data for 0x%08X: \"%.*s\".\n", id, apply_new_text_length, apply_new_text);
end
end
g.InputTextDeactivatedState.ID = 0
end
-- Write back result to user buffer. This can currently only happen when (g.ActiveId == id) or when just deactivated.
-- - As soon as the InputText() is active, our stored in-widget value gets priority over any underlying modification of the user buffer.
-- - Make sure we always reapply the live buffer back to the input/user buffer before clearing ActiveId, even thought strictly speaking
-- it was not modified on this frame. This allows the user to use InputText() without maintaining any user-side storage.
-- (PS: if you use this property together with ImGuiInputTextFlags.CallbackResize, you are at the risk of recreating a temporary
-- allocated/string object every frame. Which in the grand scheme of scheme is nothing, but isn't dear imgui vibe).
if apply_new_text ~= nil then
assert(apply_new_text_length >= 0)
if is_resizable then
local callback_data = ImGuiInputTextCallbackData()
callback_data.Ctx = g
callback_data.ID = id
callback_data.Flags = flags
callback_data.EventFlag = 4194304 --[[ImGuiInputTextFlags.CallbackResize]]
callback_data.EventActivated = (g.ActiveId == state.ID and g.ActiveIdIsJustActivated)
callback_data.Buf = buf
callback_data.BufTextLen = apply_new_text_length
callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1)
callback_data.UserData = callback_user_data
callback(callback_data)
buf = callback_data.Buf
buf_size = callback_data.BufSize
apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1)
assert(apply_new_text_length <= buf_size)
end
-- IMGUI_DEBUG_PRINT("InputText(\"%s\"): apply_new_text length %d\n", label, apply_new_text_length);
-- If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size.
ImStd.ImStrncpy(buf, 1, apply_new_text, 1, ImMin(apply_new_text_length + 1, buf_size))
end
-- Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value)
-- Otherwise request text input ahead for next frame.
if g.ActiveId == id and clear_active_id then
state.ID = 0
ImGui.ClearActiveID()
state.ID = id
end
-- Render frame
if not is_multiline then
ImGui.RenderNavCursor(frame_bb, id)
ImGui.RenderFrame(frame_bb.Min, frame_bb.Max, ImGui.GetColorU32(7 --[[ImGuiCol.FrameBg]]), true, style.FrameRounding)
end
local draw_pos = ImVec2()
ImVec2_Copy(draw_pos, is_multiline and draw_window.DC.CursorPos or (frame_bb.Min + style.FramePadding))
local clip_rect = ImRect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + inner_size.x, frame_bb.Min.y + inner_size.y) -- Not using frame_bb.Max because we have adjusted size
if is_multiline then
clip_rect:ClipWith(draw_window.ClipRect)
end
-- Set upper limit of single-line InputTextEx() at 2 million characters strings. The current pathological worst case is a long line
-- without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether.
-- Note that we only use this limit on single-line InputText(), so a pathologically large line on a InputTextMultiline() would still crash.
local buf_display_max_length = 2 * 1024 * 1024
local buf_display = buf_display_from_state and state.TextA.Data or buf
local buf_display_end = nil -- We have specialized paths below for setting the length
-- Display hint when contents is empty
-- At this point we need to handle the possibility that a callback could have modified the underlying buffer (#8368)
local new_is_displaying_hint = (hint ~= nil and (buf_display_from_state and state.TextA.Data or buf)[1] == 0)
if new_is_displaying_hint ~= is_displaying_hint then
if is_password and not is_displaying_hint then
ImGui.PopPasswordFont()
end
is_displaying_hint = new_is_displaying_hint
if is_password and not is_displaying_hint then
ImGui.PushPasswordFont()
end
end
if is_displaying_hint then
--- @cast hint char[]
buf_display = hint
buf_display_end = ImStd.ImStrlen(hint) + 1
else
if render_cursor or render_selection or g.ActiveId == id then
buf_display_end = state.TextLen + 1
elseif is_multiline and not is_wordwrap then
buf_display_end = nil -- Inactive multi-line: end of buffer will be output by InputTextLineIndexBuild() special strchr() path.
else
buf_display_end = ImStd.ImStrlen(buf_display) + 1
end
end
-- Calculate visibility
local line_visible_n0, line_visible_n1 = 0, 1 -- 0-based like upstream
if is_multiline then
line_visible_n0, line_visible_n1 = ImGui.CalcClipRectVisibleItemsY(clip_rect, draw_pos, g.FontSize)
end
-- Build line index for easy data access (makes code below simpler and faster)
local line_index = g.InputTextLineIndex
line_index.Offsets:resize(0)
local line_count = 1
if is_multiline then
-- If scrolling is expected to change build full index.
-- FIXME-OPT: Could append to index when new value of line_visible_n1 becomes bigger, see second call to CalcClipRectVisibleItemsY() below.
local will_scroll_y = state and ((state.CursorFollow and render_cursor) or (state.CursorCenterY and (render_cursor or render_selection)))
local out_buf_end
line_count, out_buf_end = InputTextLineIndexBuild(flags, line_index, buf_display, buf_display_end, wrap_width, will_scroll_y and 2147483647 --[[INT_MAX]] or (line_visible_n1 + 1), buf_display_end == nil)
if buf_display_end == nil then
buf_display_end = out_buf_end
end
end
--- @cast buf_display_end int
line_index.EndOffset = buf_display_end - 1
line_visible_n1 = ImMin(line_visible_n1, line_count)
-- Store text height (we don't need width)
local text_size_y = line_count * g.FontSize
-- ImGui.GetForegroundDrawList():AddRect(draw_pos + ImVec2(0, line_visible_n0 * g.FontSize), draw_pos + ImVec2(frame_size.x, line_visible_n1 * g.FontSize), IM_COL32(255, 0, 0, 255))
-- Calculate blinking cursor position
--- @cast buf_display_end int
local cursor_offset = (render_cursor and state) and InputTextLineIndexGetPosOffset(g, state, line_index, buf_display, buf_display_end, state.Stb.cursor) or ImVec2(0.0, 0.0)
local draw_scroll = ImVec2()
-- Render text. We currently only render selection when the widget is active or while scrolling.
local text_col = ImGui.GetColorU32(is_displaying_hint and 1 --[[ImGuiCol.TextDisabled]] or 0 --[[ImGuiCol.Text]])
if render_cursor or render_selection then
-- Render text (with cursor and selection)
-- This is going to be messy. We need to:
-- - Display the text (this alone can be more easily clipped)
-- - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation)
-- - Measure text height (for scrollbar)
-- We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort)
-- FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
assert(state ~= nil)
state.LineCount = line_count
-- Scroll
local new_scroll_y = scroll_y
if render_cursor and state.CursorFollow then
-- Horizontal scroll in chunks of quarter width
if bit32.band(flags, 32768 --[[ImGuiInputTextFlags.NoHorizontalScroll]]) == 0 then
local scroll_increment_x = inner_size.x * 0.25
local visible_width = inner_size.x - style.FramePadding.x
if cursor_offset.x < state.Scroll.x then
state.Scroll.x = IM_TRUNC(ImMax(0.0, cursor_offset.x - scroll_increment_x))
elseif cursor_offset.x - visible_width >= state.Scroll.x then
state.Scroll.x = IM_TRUNC(cursor_offset.x - visible_width + scroll_increment_x)
end
else
state.Scroll.x = 0.0
end
-- Vertical scroll
if is_multiline then
-- Test if cursor is vertically visible
if cursor_offset.y - g.FontSize < scroll_y then
new_scroll_y = ImMax(0.0, cursor_offset.y - g.FontSize)
elseif cursor_offset.y - (inner_size.y - style.FramePadding.y * 2.0) >= scroll_y then
new_scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0
end
end
state.CursorFollow = false
end
if state.CursorCenterY then
if is_multiline then
new_scroll_y = cursor_offset.y - g.FontSize - (inner_size.y * 0.5 - style.FramePadding.y)
end
state.CursorCenterY = false
render_cursor = false
end
if new_scroll_y ~= scroll_y then
local scroll_max_y = ImMax((text_size_y + style.FramePadding.y * 2.0) - inner_size.y, 0.0)
scroll_y = ImClamp(new_scroll_y, 0.0, scroll_max_y)
draw_pos.y = draw_pos.y + (draw_window.Scroll.y - scroll_y) -- Manipulate cursor pos immediately avoid a frame of lag
draw_window.Scroll.y = scroll_y
line_visible_n0, line_visible_n1 = ImGui.CalcClipRectVisibleItemsY(clip_rect, draw_pos, g.FontSize)
line_visible_n1 = ImMin(line_visible_n1, line_count)
end
-- Draw selection
draw_scroll.x = state.Scroll.x
if render_selection then
local bg_color = ImGui.GetColorU32(54 --[[ImGuiCol.TextSelectedBg]], render_cursor and 1.0 or 0.6) -- FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests.
local bg_offy_up = is_multiline and 0.0 or -1.0 -- FIXME-DPI: those offsets should be part of the style? they don't play so well with multi-line selection.
local bg_offy_dn = is_multiline and 0.0 or 2.0
local bg_eol_width = IM_TRUNC(g.FontBaked:GetCharAdvance(32) * 0.50) -- So we can see selected empty lines
local text_selected_begin = ImMin(state.Stb.select_start, state.Stb.select_end)
local text_selected_end = ImMax(state.Stb.select_start, state.Stb.select_end)
for line_n = line_visible_n0, line_visible_n1 - 1 do
repeat
local p = line_index:get_line_begin(1, line_n)
local p_eol = line_index:get_line_end(1, line_n)
local p_eol_is_wrap = (p_eol < buf_display_end and buf_display[p_eol] ~= 10)
if p_eol_is_wrap then
p_eol = p_eol + 1
end
local line_selected_begin = (text_selected_begin > p) and text_selected_begin or p
local line_selected_end = (text_selected_end < p_eol) and text_selected_end or p_eol
local rect_width = 0.0
if line_selected_begin < line_selected_end then
rect_width = rect_width + ImGui.CalcTextSizeEx(buf_display, line_selected_begin, line_selected_end).x
end
if text_selected_begin <= p_eol and text_selected_end > p_eol and not p_eol_is_wrap then
rect_width = rect_width + bg_eol_width -- So we can see selected empty lines
end
if rect_width == 0.0 then
break
end
local rect = ImRect()
rect.Min.x = draw_pos.x - draw_scroll.x + ImGui.CalcTextSizeEx(buf_display, p, line_selected_begin).x
rect.Min.y = draw_pos.y - draw_scroll.y + line_n * g.FontSize
rect.Max.x = rect.Min.x + rect_width
rect.Max.y = rect.Min.y + bg_offy_dn + g.FontSize
rect.Min.y = rect.Min.y + bg_offy_up
rect:ClipWith(clip_rect)
draw_window.DrawList:AddRectFilled(rect.Min, rect.Max, bg_color)
until true
end
end
end
-- Find render position for right alignment (single-line only)
if g.ActiveId ~= id and bit32.band(flags, 131072 --[[ImGuiInputTextFlags.ElideLeft]]) ~= 0 and not render_cursor and not render_selection then
draw_pos.x = ImMin(draw_pos.x, frame_bb.Max.x - ImGui.CalcTextSize(buf_display, nil).x - style.FramePadding.x)
end
-- draw_scroll.x = state.Scroll.x -- Preserve scroll when inactive?
-- Render text
if (is_multiline or (buf_display_end - 1) < buf_display_max_length) and bit32.band(text_col, 4278190080 --[[IM_COL32_A_MASK]]) ~= 0 and line_visible_n0 < line_visible_n1 then
g.Font:RenderText(draw_window.DrawList, g.FontSize,
draw_pos - draw_scroll + ImVec2(0.0, line_visible_n0 * g.FontSize),
text_col, clip_rect:AsVec4(),
buf_display,
line_index:get_line_begin(1, line_visible_n0),
line_index:get_line_end(1, line_visible_n1 - 1),
wrap_width, bit32.bor(2 --[[ImDrawTextFlags.WrapKeepBlanks]], 1 --[[ImDrawTextFlags.CpuFineClip]]))
end
-- Render blinking cursor
if render_cursor then
state.CursorAnim = state.CursorAnim + io.DeltaTime
local cursor_is_visible = (not g.IO.ConfigInputTextCursorBlink) or (state.CursorAnim <= 0.0) or (ImFmod(state.CursorAnim, 1.20) <= 0.80)
local cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll)
local cursor_screen_rect = ImRect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 1.0, cursor_screen_pos.x + 1.0, cursor_screen_pos.y - 1.0)
if cursor_is_visible and cursor_screen_rect:Overlaps(clip_rect) then
draw_window.DrawList:AddLineV(cursor_screen_rect.Min.x, cursor_screen_rect.Min.y, cursor_screen_rect.Max.y, ImGui.GetColorU32(34 --[[ImGuiCol.InputTextCursor]]), style.InputTextCursorSize, 131072 --[[ImDrawFlags.StrokeInside]])
end
-- Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)
-- This is required for some backends (SDL3) to start emitting character/text inputs.
-- As per #6341, make sure we don't set that on the deactivating frame.
if not is_readonly and g.ActiveId == id then
local ime_data = g.PlatformImeData -- (this is a public struct, passed to io.Platform_SetImeDataFn() handler)
ime_data.WantVisible = true
ime_data.WantTextInput = true
ImVec2_Copy(ime_data.InputPos, ImVec2(cursor_screen_pos.x - 1.0, cursor_screen_pos.y - g.FontSize))
ime_data.InputLineHeight = g.FontSize
ime_data.ViewportId = window.Viewport.ID
end
end
if is_password and not is_displaying_hint then
ImGui.PopPasswordFont()
end
if is_multiline then
-- For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags.Inputable (see #4761, #7870)...
ImGui.Dummy(ImVec2(0.0, text_size_y + style.FramePadding.y))
g.NextItemData.ItemFlagsSet = bit32.bor(g.NextItemData.ItemFlagsSet, 1048576 --[[ImGuiItemFlags.Inputable]], 1 --[[ImGuiItemFlags.NoTabStop]])
ImGui.EndChild()
item_data_backup.StatusFlags = bit32.bor(item_data_backup.StatusFlags, bit32.band(g.LastItemData.StatusFlags, 128 --[[ImGuiItemStatusFlags.HoveredWindow]]))
-- ...and then we need to undo the group overriding last item data, which gets a bit messy as EndGroup() tries to forward scrollbar being active...
-- FIXME: This quite messy/tricky, should attempt to get rid of the child window.
ImGui.EndGroup()
if g.LastItemData.ID == 0 or g.LastItemData.ID ~= ImGui.GetWindowScrollbarID(draw_window, 2 --[[ImGuiAxis.Y]]) then
g.LastItemData.ID = id
g.LastItemData.ItemFlags = item_data_backup.ItemFlags
g.LastItemData.StatusFlags = item_data_backup.StatusFlags
end
end
if state and is_readonly then
state.TextSrc = nil
end
if label_size.x > 0.0 then
ImGui.RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label, 1, label_end, false)
end
if state and validated then
state.ValidatedThisFrame = true
end
if value_changed then
ImGui.MarkItemEdited(id)
end
if bit32.band(flags, 64 --[[ImGuiInputTextFlags.EnterReturnsTrue]]) ~= 0 then
return validated
else
return value_changed
end
end
--- @param label string
--- @param hint char[]
--- @param buf char[]
--- @param buf_size size_t
--- @param flags? ImGuiInputTextFlags
--- @param callback? ImGuiInputTextCallback
--- @param user_data? any
function ImGui.InputTextWithHint(label, hint, buf, buf_size, flags, callback, user_data)
if flags == nil then flags = 0 end
assert(bit32.band(flags, 67108864 --[[ImGuiInputTextFlags.Multiline]]) == 0) -- call InputTextMultiline() or InputTextEx() manually if you need multi-line + hint.
return ImGui.InputTextEx(label, hint, buf, buf_size, ImVec2(0, 0), flags, callback, user_data)
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)
ImGui.DebugLocateItemOnHover(state.ID)
local wrap_dir = ""
if bit32.band(state.Flags, 16777216 --[[ImGuiInputTextFlags.WordWrap]]) ~= 0 then
if state.LastMoveDirectionLR == 0 --[[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(1 --[[ImGuiChildFlags.Borders]], 8 --[[ImGuiChildFlags.ResizeY]])) then -- Visualize undo state
ImGui.PushStyleVar(14 --[[ImGuiStyleVar.ItemSpacing]], ImVec2(0, 0))
for n = 1, 99 --[[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.
-------------------------------------------------------------------------------
-- - ColorEdit3()
-- - ColorEdit4()
-- - ColorPicker3()
-- - RenderColorRectWithAlphaCheckerboard() [Internal]
-- - ColorPicker4()
-- - ColorButton()
-- - ColorTooltip() [Internal]
-- - ColorEditOptionsPopup() [Internal]
-- - ColorPickerOptionsPopup() [Internal]
-------------------------------------------------------------------------------
--- @param col float[]
--- @param H float
--- @return float
local function ColorEditRestoreH(col, H)
local g = GImGui
assert(g.ColorEditCurrentID ~= 0)
if g.ColorEditSavedID ~= g.ColorEditCurrentID or g.ColorEditSavedColor ~= ImGui.ColorConvertFloat4ToU32(ImVec4(col[1], col[2], col[3], 0)) then
return H
end
H = g.ColorEditSavedHue
return H
end
-- ColorEdit supports RGB and HSV inputs. In case of RGB input resulting color may have undefined hue and/or saturation.
-- Since widget displays both RGB and HSV values we must preserve hue and saturation to prevent these values resetting.
--- @param col float[]
--- @param H float
--- @param S float
--- @param V float
--- @return float, float, float
local function ColorEditRestoreHS(col, H, S, V)
local g = GImGui
assert(g.ColorEditCurrentID ~= 0)
if g.ColorEditSavedID ~= g.ColorEditCurrentID or g.ColorEditSavedColor ~= ImGui.ColorConvertFloat4ToU32(ImVec4(col[1], col[2], col[3], 0)) then
return H, S, V
end
-- When S == 0, H is undefined.
-- When H == 1 it wraps around to 0.
if S == 0.0 or (H == 0.0 and g.ColorEditSavedHue == 1) then
H = g.ColorEditSavedHue
end
-- When V == 0, S is undefined.
if V == 0.0 then
S = g.ColorEditSavedSat
end
return H, S, V
end
do --[[ColorEdit4]]
local ids = { "##X", "##Y", "##Z", "##W" }
local fmt_table_int = {
{ "%3d", "%3d", "%3d", "%3d" }, -- Short display
{ "R:%3d", "G:%3d", "B:%3d", "A:%3d" }, -- Long display for RGBA
{ "H:%3d", "S:%3d", "V:%3d", "A:%3d" } -- Long display for HSVA
}
local fmt_table_float = {
{ "%0.3f", "%0.3f", "%0.3f", "%0.3f" }, -- Short display
{ "R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f" }, -- Long display for RGBA
{ "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" } -- Long display for HSVA
}
-- Edit colors components (each component in 0.0f..1.0f range).
-- See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags.NoAlpha flag is set.
-- With typical options: Left-click on color square to open color picker. Right-click to open option menu. Ctrl+Click over input fields to edit them and TAB to go to next item.
--- @param label string
--- @param col float[]
--- @param flags ImGuiColorEditFlags
@native
function ImGui.ColorEdit4(label, col, flags)
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
local g = GImGui
local style = g.Style
local square_sz = ImGui.GetFrameHeight()
local label_display_end = ImGui.FindRenderedTextEnd(label)
local w_full = ImGui.CalcItemWidth()
g.NextItemData:ClearFlags()
-- [Luau] Fast path for a row that is entirely clipped (e.g. long lists such as Style Editor > Colors): nothing
-- inside would draw, be hovered or receive input, so only reproduce the layout (same group size, baseline and
-- last item rect as the full path). Not taken while anything could depend on the inner items: an active item
-- or popup, keyboard navigation, logging, or the ID debug hook.
if label_display_end == 1 and bit32.band(flags, bit32.bor(32 --[[ImGuiColorEditFlags.NoInputs]], 16 --[[ImGuiColorEditFlags.NoSmallPreview]])) == 0
and g.ActiveId == 0 and g.OpenPopupStack.Size == 0 and not g.NavCursorVisible and not g.NavAnyRequest
and not g.LogEnabled and g.DebugHookIdInfoId == 0 then
local w_button = square_sz + style.ItemInnerSpacing.x
local w = ImMax(w_full - w_button, 1.0) + w_button
local p = window.DC.CursorPos
local cr = window.ClipRect
if p.y + square_sz < cr.Min.y or p.y > cr.Max.y or p.x + w < cr.Min.x or p.x > cr.Max.x then
ImGui.BeginGroup()
local bb = ImRect(p.x, p.y, p.x + w, p.y + square_sz)
ImGui.ItemSize(ImVec2(w, square_sz), style.FramePadding.y)
ImGui.ItemAdd(bb, 0)
ImGui.EndGroup()
return false
end
end
ImGui.BeginGroup()
ImGui.PushID(label)
local set_current_color_edit_id = (g.ColorEditCurrentID == 0)
if set_current_color_edit_id then
g.ColorEditCurrentID = window.IDStack:back()
end
-- If we're not showing any slider there's no point in doing any HSV conversions
local flags_untouched = flags
if bit32.band(flags, 32 --[[ImGuiColorEditFlags.NoInputs]]) ~= 0 then
flags = bit32.bor(bit32.band(flags, bit32.bnot(7340032 --[[ImGuiColorEditFlags.DisplayMask_]])), 1048576 --[[ImGuiColorEditFlags.DisplayRGB]], 8 --[[ImGuiColorEditFlags.NoOptions]])
end
-- Context menu: display and modify options (before defaults are applied)
if bit32.band(flags, 8 --[[ImGuiColorEditFlags.NoOptions]]) == 0 then
ImGui.ColorEditOptionsPopup(col, flags)
end
-- Read stored options
if bit32.band(flags, 7340032 --[[ImGuiColorEditFlags.DisplayMask_]]) == 0 then
flags = bit32.bor(flags, bit32.band(g.IO.ConfigColorEditFlags, 7340032 --[[ImGuiColorEditFlags.DisplayMask_]]))
end
if bit32.band(flags, 25165824 --[[ImGuiColorEditFlags.DataTypeMask_]]) == 0 then
flags = bit32.bor(flags, bit32.band(g.IO.ConfigColorEditFlags, 25165824 --[[ImGuiColorEditFlags.DataTypeMask_]]))
end
if bit32.band(flags, 100663296 --[[ImGuiColorEditFlags.PickerMask_]]) == 0 then
flags = bit32.bor(flags, bit32.band(g.IO.ConfigColorEditFlags, 100663296 --[[ImGuiColorEditFlags.PickerMask_]]))
end
if bit32.band(flags, 805306368 --[[ImGuiColorEditFlags.InputMask_]]) == 0 then
flags = bit32.bor(flags, bit32.band(g.IO.ConfigColorEditFlags, 805306368 --[[ImGuiColorEditFlags.InputMask_]]))
end
flags = bit32.bor(flags, bit32.band(g.IO.ConfigColorEditFlags, bit32.bnot(bit32.bor(7340032 --[[ImGuiColorEditFlags.DisplayMask_]], 25165824 --[[ImGuiColorEditFlags.DataTypeMask_]], 100663296 --[[ImGuiColorEditFlags.PickerMask_]], 805306368 --[[ImGuiColorEditFlags.InputMask_]]))))
assert(ImIsPowerOfTwo(bit32.band(flags, 7340032 --[[ImGuiColorEditFlags.DisplayMask_]]))) -- Check that only 1 is selected
assert(ImIsPowerOfTwo(bit32.band(flags, 805306368 --[[ImGuiColorEditFlags.InputMask_]]))) -- Check that only 1 is selected
local alpha = bit32.band(flags, 2 --[[ImGuiColorEditFlags.NoAlpha]]) == 0
local hdr = bit32.band(flags, 524288 --[[ImGuiColorEditFlags.HDR]]) ~= 0
local components = alpha and 4 or 3
local w_button = (bit32.band(flags, 16 --[[ImGuiColorEditFlags.NoSmallPreview]]) ~= 0) and 0.0 or (square_sz + style.ItemInnerSpacing.x)
local w_inputs = ImMax(w_full - w_button, 1.0)
w_full = w_inputs + w_button
-- Convert to the formats we need
local f = { col[1], col[2], col[3], alpha and col[4] or 1.0 }
if bit32.band(flags, 536870912 --[[ImGuiColorEditFlags.InputHSV]]) ~= 0 and bit32.band(flags, 1048576 --[[ImGuiColorEditFlags.DisplayRGB]]) ~= 0 then
f[1], f[2], f[3] = ImGui.ColorConvertHSVtoRGB(f[1], f[2], f[3])
elseif bit32.band(flags, 268435456 --[[ImGuiColorEditFlags.InputRGB]]) ~= 0 and bit32.band(flags, 2097152 --[[ImGuiColorEditFlags.DisplayHSV]]) ~= 0 then
-- Hue is lost when converting from grayscale rgb (saturation=0). Restore it.
f[1], f[2], f[3] = ImGui.ColorConvertRGBtoHSV(f[1], f[2], f[3])
f[1], f[2], f[3] = ColorEditRestoreHS(col, f[1], f[2], f[3])
end
local i = { IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]), IM_F32_TO_INT8_UNBOUND(f[4]) }
local value_changed = false
local value_changed_as_float = false
local pos = ImVec2()
ImVec2_Copy(pos, window.DC.CursorPos)
local inputs_offset_x = (style.ColorButtonPosition == 0 --[[ImGuiDir.Left]]) and w_button or 0.0
window.DC.CursorPos.x = pos.x + inputs_offset_x
if bit32.band(flags, bit32.bor(1048576 --[[ImGuiColorEditFlags.DisplayRGB]], 2097152 --[[ImGuiColorEditFlags.DisplayHSV]])) ~= 0 and bit32.band(flags, 32 --[[ImGuiColorEditFlags.NoInputs]]) == 0 then
-- RGB/HSV 0..255 Sliders
local w_items = w_inputs - style.ItemInnerSpacing.x * (components - 1)
local w_per_component = IM_TRUNC(w_items / components)
local draw_color_marker = bit32.band(flags, bit32.bor(2097152 --[[ImGuiColorEditFlags.DisplayHSV]], 2048 --[[ImGuiColorEditFlags.NoColorMarkers]])) == 0
local hide_prefix = draw_color_marker or (w_per_component <= ImGui.CalcTextSize((bit32.band(flags, 16777216 --[[ImGuiColorEditFlags.Float]]) ~= 0) and "M:0.000" or "M:000").x)
local fmt_idx
if hide_prefix then
fmt_idx = 1
elseif bit32.band(flags, 2097152 --[[ImGuiColorEditFlags.DisplayHSV]]) ~= 0 then
fmt_idx = 3
else
fmt_idx = 2
end
local drag_flags = draw_color_marker and 4096 --[[ImGuiSliderFlags.ColorMarkers]] or 0 --[[ImGuiSliderFlags.None]]
local prev_split = 0.0
for n = 1, components do
if n > 1 then
ImGui.SameLine(0, style.ItemInnerSpacing.x)
end
local next_split = IM_TRUNC(w_items * n / components)
ImGui.SetNextItemWidth(ImMax(next_split - prev_split, 1.0))
prev_split = next_split
if draw_color_marker then
ImGui.SetNextItemColorMarker(GDefaultRgbaColorMarkers[n])
end
-- FIXME: When ImGuiColorEditFlags_HDR flag is passed HS values snap in weird ways when SV values go below 0.
if bit32.band(flags, 16777216 --[[ImGuiColorEditFlags.Float]]) ~= 0 then
local changed
f[n], changed = ImGui.DragFloat(ids[n], f[n], 1.0 / 255.0, 0.0, hdr and 0.0 or 1.0, fmt_table_float[fmt_idx][n], drag_flags)
value_changed = value_changed or changed
value_changed_as_float = value_changed_as_float or value_changed
else
local changed
i[n], changed = ImGui.DragInt(ids[n], i[n], 1.0, 0, hdr and 0 or 255, fmt_table_int[fmt_idx][n], drag_flags)
value_changed = value_changed or changed
end
if bit32.band(flags, 8 --[[ImGuiColorEditFlags.NoOptions]]) == 0 then
ImGui.OpenPopupOnItemClick("context", 8 --[[ImGuiPopupFlags.MouseButtonRight]])
end
end
elseif bit32.band(flags, 4194304 --[[ImGuiColorEditFlags.DisplayHex]]) ~= 0 and bit32.band(flags, 32 --[[ImGuiColorEditFlags.NoInputs]]) == 0 then
-- RGB Hexadecimal Input
-- TODO: resolve sprintf, sscanf requirement. currently this section creates a lot of temp tables and strings...
local buf, buf_size = nil, 64
if alpha then
local str = ImFormatString("#%02X%02X%02X%02X", ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255), ImClamp(i[3], 0, 255), ImClamp(i[4], 0, 255))
buf = { string.byte(str, 1, #str) }
buf[#buf + 1] = 0
else
local str = ImFormatString("#%02X%02X%02X", ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255), ImClamp(i[3], 0, 255))
buf = { string.byte(str, 1, #str) }
buf[#buf + 1] = 0
end
ImGui.SetNextItemWidth(w_inputs)
if ImGui.InputText("##Text", buf, buf_size, 8 --[[ImGuiInputTextFlags.CharsUppercase]]) then
value_changed = true
local p = 1
while buf[p] == 35 or ImCharIsBlankA(buf[p]) do
p = p + 1
end
i[1], i[2], i[3] = 0, 0, 0
i[4] = 0xFF -- alpha default to 255 is not parsed by scanf (e.g. inputting #FFFFFF omitting alpha)
local r
if alpha then
r = ImStd.sscanf(buf, p, "%02X%02X%02X%02X", i) -- Treat at unsigned (%X is unsigned)
else
r = ImStd.sscanf(buf, p, "%02X%02X%02X", i)
end
-- IM_UNUSED(r) -- Fixes C6031: Return value ignored: 'sscanf'.
end
if bit32.band(flags, 8 --[[ImGuiColorEditFlags.NoOptions]]) == 0 then
ImGui.OpenPopupOnItemClick("context", 8 --[[ImGuiPopupFlags.MouseButtonRight]])
end
end
local picker_active_window = nil
if bit32.band(flags, 16 --[[ImGuiColorEditFlags.NoSmallPreview]]) == 0 then
local button_offset_x = (bit32.band(flags, 32 --[[ImGuiColorEditFlags.NoInputs]]) ~= 0 or style.ColorButtonPosition == 0 --[[ImGuiDir.Left]]) and 0.0 or (w_inputs + style.ItemInnerSpacing.x)
ImVec2_Copy(window.DC.CursorPos, ImVec2(pos.x + button_offset_x, pos.y))
local col_v4 = ImVec4(col[1], col[2], col[3], alpha and col[4] or 1.0)
if ImGui.ColorButton("##ColorButton", col_v4, flags) then
if bit32.band(flags, 4 --[[ImGuiColorEditFlags.NoPicker]]) == 0 then
-- Store current color and open a picker
ImVec4_Copy(g.ColorPickerRef, col_v4)
ImGui.OpenPopup("picker")
ImGui.SetNextWindowPos(g.LastItemData.Rect:GetBL() + ImVec2(0.0, style.ItemSpacing.y))
end
end
if bit32.band(flags, 8 --[[ImGuiColorEditFlags.NoOptions]]) == 0 then
ImGui.OpenPopupOnItemClick("context", 8 --[[ImGuiPopupFlags.MouseButtonRight]])
end
if ImGui.BeginPopup("picker") then
if g.CurrentWindow.BeginCount == 1 then
picker_active_window = g.CurrentWindow
if label ~= "" and label_display_end > 1 then
ImGui.TextEx(label, label_display_end)
ImGui.Spacing()
end
local picker_flags_to_forward = bit32.bor(25165824 --[[ImGuiColorEditFlags.DataTypeMask_]], 100663296 --[[ImGuiColorEditFlags.PickerMask_]], 805306368 --[[ImGuiColorEditFlags.InputMask_]], 524288 --[[ImGuiColorEditFlags.HDR]], 2 --[[ImGuiColorEditFlags.NoAlpha]], 262144 --[[ImGuiColorEditFlags.AlphaBar]])
local picker_flags = bit32.bor(bit32.band(flags_untouched, picker_flags_to_forward), 7340032 --[[ImGuiColorEditFlags.DisplayMask_]], 128 --[[ImGuiColorEditFlags.NoLabel]], 16384 --[[ImGuiColorEditFlags.AlphaPreviewHalf]])
ImGui.SetNextItemWidth(square_sz * 12.0) -- Use 256 + bar sizes?
value_changed = value_changed or ImGui.ColorPicker4("##picker", col, picker_flags, g.ColorPickerRef)
end
ImGui.EndPopup()
end
end
if label ~= "" and label_display_end > 1 and bit32.band(flags, 128 --[[ImGuiColorEditFlags.NoLabel]]) == 0 then
-- Position not necessarily next to last submitted button (e.g. if style.ColorButtonPosition == ImGuiDir_Left),
-- but we need to use SameLine() to setup baseline correctly. Might want to refactor SameLine() to simplify this.
ImGui.SameLine(0.0, style.ItemInnerSpacing.x)
window.DC.CursorPos.x = pos.x + ((bit32.band(flags, 32 --[[ImGuiColorEditFlags.NoInputs]]) ~= 0) and w_button or (w_full + style.ItemInnerSpacing.x))
ImGui.TextEx(label, label_display_end)
end
-- Convert back
if value_changed and picker_active_window == nil then
if not value_changed_as_float then
for n = 1, 4 do
f[n] = i[n] / 255.0
end
end
if bit32.band(flags, 2097152 --[[ImGuiColorEditFlags.DisplayHSV]]) ~= 0 and bit32.band(flags, 268435456 --[[ImGuiColorEditFlags.InputRGB]]) ~= 0 then
g.ColorEditSavedHue = f[1]
g.ColorEditSavedSat = f[2]
f[1], f[2], f[3] = ImGui.ColorConvertHSVtoRGB(f[1], f[2], f[3])
g.ColorEditSavedID = g.ColorEditCurrentID
g.ColorEditSavedColor = ImGui.ColorConvertFloat4ToU32(ImVec4(f[1], f[2], f[3], 0))
end
if bit32.band(flags, 1048576 --[[ImGuiColorEditFlags.DisplayRGB]]) ~= 0 and bit32.band(flags, 536870912 --[[ImGuiColorEditFlags.InputHSV]]) ~= 0 then
f[1], f[2], f[3] = ImGui.ColorConvertRGBtoHSV(f[1], f[2], f[3])
end
col[1] = f[1]
col[2] = f[2]
col[3] = f[3]
if alpha then
col[4] = f[4]
end
end
if set_current_color_edit_id then
g.ColorEditCurrentID = 0
end
ImGui.PopID()
ImGui.EndGroup()
-- Drag and Drop Target
-- NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test.
if bit32.band(g.LastItemData.StatusFlags, 1 --[[ImGuiItemStatusFlags.HoveredRect]]) ~= 0 and bit32.band(g.LastItemData.ItemFlags, 2048 --[[ImGuiItemFlags.ReadOnly]]) == 0 and bit32.band(flags, 512 --[[ImGuiColorEditFlags.NoDragDrop]]) == 0 and ImGui.BeginDragDropTarget() then
local accepted_drag_drop = false
local payload = ImGui.AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)
if payload then
local d = payload.Data
col[1], col[2], col[3] = d[1], d[2], d[3] -- Preserve alpha if any
value_changed = true; accepted_drag_drop = true
end
payload = ImGui.AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)
if payload then
local d = payload.Data
for i = 1, components do col[i] = d[i] end
value_changed = true; accepted_drag_drop = true
end
-- Drag-drop payloads are always RGB
if accepted_drag_drop and bit32.band(flags, 536870912 --[[ImGuiColorEditFlags.InputHSV]]) ~= 0 then
col[1], col[2], col[3] = ImGui.ColorConvertRGBtoHSV(col[1], col[2], col[3])
end
ImGui.EndDragDropTarget()
end
-- When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4().
if picker_active_window and g.ActiveId ~= 0 and g.ActiveIdWindow == picker_active_window then
g.LastItemData.ID = g.ActiveId
end
if value_changed and g.LastItemData.ID ~= 0 then -- In case of ID collision, the second EndGroup() won't catch g.ActiveId
ImGui.MarkItemEdited(g.LastItemData.ID)
end
return value_changed
end
end
--- @param label string
--- @param col [float, float, float]
--- @param flags? ImGuiColorEditFlags
function ImGui.ColorEdit3(label, col, flags)
if flags == nil then flags = 0 end
if flags == nil then flags = 0 end
return ImGui.ColorEdit4(label, col, bit32.bor(flags, 2 --[[ImGuiColorEditFlags.NoAlpha]]))
end
-- `col` mutated in place, returns value_changed
function ImGui.ColorPicker3(label, col, flags)
if flags == nil then flags = 0 end
local col4 = { col[1], col[2], col[3], 1.0 }
if not ImGui.ColorPicker4(label, col4, bit32.bor(flags or 0, 2 --[[ImGuiColorEditFlags.NoAlpha]])) then
return false
end
col[1] = col4[1]; col[2] = col4[2]; col[3] = col4[3]
return true
end
-- This allowed passing 0 to set default, whereas NewFrame() will assert if one of missing. Abide to old logic.
function ImGui.SetColorEditOptions(flags)
local g = GImGui
local def = 311427072 --[[ImGuiColorEditFlags.DefaultOptions_]]
for _, mask in ipairs({ 7340032 --[[ImGuiColorEditFlags.DisplayMask_]], 25165824 --[[ImGuiColorEditFlags.DataTypeMask_]], 100663296 --[[ImGuiColorEditFlags.PickerMask_]], 805306368 --[[ImGuiColorEditFlags.InputMask_]] }) do
if bit32.band(flags, mask) == 0 then
flags = bit32.bor(flags, bit32.band(def, mask))
end
end
g.IO.ConfigColorEditFlags = flags
end
-- Helper for ColorPicker4()
--- @param draw_list ImDrawList
--- @param pos ImVec2
--- @param half_sz ImVec2
--- @param bar_w float
--- @param alpha float
local function RenderArrowsForVerticalBar(draw_list, pos, half_sz, bar_w, alpha)
local alpha8 = IM_F32_TO_INT8_SAT(alpha)
ImGui.RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x + 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), 1 --[[ImGuiDir.Right]], IM_COL32(0, 0, 0, alpha8))
ImGui.RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x, pos.y), half_sz, 1 --[[ImGuiDir.Right]], IM_COL32(255, 255, 255, alpha8))
ImGui.RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x - 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), 0 --[[ImGuiDir.Left]], IM_COL32(0, 0, 0, alpha8))
ImGui.RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x, pos.y), half_sz, 0 --[[ImGuiDir.Left]], IM_COL32(255, 255, 255, alpha8))
end
do --[[ColorPicker4]]
local backup_initial_col = {0, 0, 0, 0}
-- Note: ColorPicker4() only accesses 3 floats if ImGuiColorEditFlags.NoAlpha flag is set.
-- FIXME: we adjust the big color square height based on item width, which may cause a flickering feedback loop (if automatic height makes a vertical scrollbar appears, affecting automatic width..)
-- FIXME: this is trying to be aware of style.Alpha but not fully correct. Also, the color wheel will have overlapping glitches with (style.Alpha < 1.0)
--- @param label string
--- @param col float[]
--- @param flags ImGuiColorEditFlags
--- @param ref_col? float[]
function ImGui.ColorPicker4(label, col, flags, ref_col)
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
local draw_list = window.DrawList
local g = GImGui
local style = g.Style
local io = g.IO
local width = ImGui.CalcItemWidth()
local is_readonly = bit32.band(bit32.bor(g.NextItemData.ItemFlagsSet, g.CurrentItemFlags), 2048 --[[ImGuiItemFlags.ReadOnly]]) ~= 0
g.NextItemData:ClearFlags()
ImGui.PushID(label)
local set_current_color_edit_id = (g.ColorEditCurrentID == 0)
if set_current_color_edit_id then
g.ColorEditCurrentID = window.IDStack:back()
end
ImGui.BeginGroup()
if bit32.band(flags, 256 --[[ImGuiColorEditFlags.NoSidePreview]]) == 0 then
flags = bit32.bor(flags, 16 --[[ImGuiColorEditFlags.NoSmallPreview]])
end
-- Context menu: display and store options.
if bit32.band(flags, 8 --[[ImGuiColorEditFlags.NoOptions]]) == 0 then
ImGui.ColorPickerOptionsPopup(col, flags)
end
-- Read stored options
if bit32.band(flags, 100663296 --[[ImGuiColorEditFlags.PickerMask_]]) == 0 then
local picker_flags = bit32.band(g.IO.ConfigColorEditFlags, 100663296 --[[ImGuiColorEditFlags.PickerMask_]])
if picker_flags ~= 0 then
flags = bit32.bor(flags, picker_flags)
else
flags = bit32.bor(flags, bit32.band(311427072 --[[ImGuiColorEditFlags.DefaultOptions_]], 100663296 --[[ImGuiColorEditFlags.PickerMask_]]))
end
end
if bit32.band(flags, 805306368 --[[ImGuiColorEditFlags.InputMask_]]) == 0 then
local input_flags = bit32.band(g.IO.ConfigColorEditFlags, 805306368 --[[ImGuiColorEditFlags.InputMask_]])
if input_flags ~= 0 then
flags = bit32.bor(flags, input_flags)
else
flags = bit32.bor(flags, bit32.band(311427072 --[[ImGuiColorEditFlags.DefaultOptions_]], 805306368 --[[ImGuiColorEditFlags.InputMask_]]))
end
end
assert(ImIsPowerOfTwo(bit32.band(flags, 100663296 --[[ImGuiColorEditFlags.PickerMask_]]))) -- Check that only 1 is selected
assert(ImIsPowerOfTwo(bit32.band(flags, 805306368 --[[ImGuiColorEditFlags.InputMask_]]))) -- Check that only 1 is selected
if bit32.band(flags, 8 --[[ImGuiColorEditFlags.NoOptions]]) == 0 then
flags = bit32.bor(flags, bit32.band(g.IO.ConfigColorEditFlags, 262144 --[[ImGuiColorEditFlags.AlphaBar]]))
end
-- Setup
local components = (bit32.band(flags, 2 --[[ImGuiColorEditFlags.NoAlpha]]) ~= 0) and 3 or 4
local alpha_bar = (bit32.band(flags, 262144 --[[ImGuiColorEditFlags.AlphaBar]]) ~= 0) and (bit32.band(flags, 2 --[[ImGuiColorEditFlags.NoAlpha]]) == 0)
local picker_pos = ImVec2()
ImVec2_Copy(picker_pos, window.DC.CursorPos)
local square_sz = ImGui.GetFrameHeight()
local bars_width = square_sz -- Arbitrary smallish width of Hue/Alpha picking bars
local sv_picker_size = ImMax(bars_width * 1, width - (alpha_bar and 2 or 1) * (bars_width + style.ItemInnerSpacing.x)) -- Saturation/Value picking box
local bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x
local bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x
local bars_triangles_half_sz = IM_TRUNC(bars_width * 0.20)
backup_initial_col[1], backup_initial_col[2], backup_initial_col[3], backup_initial_col[4] = col[1], col[2], col[3], col[4]
local wheel_thickness = sv_picker_size * 0.08
local wheel_r_outer = sv_picker_size * 0.50
local wheel_r_inner = wheel_r_outer - wheel_thickness
local wheel_center = ImVec2(picker_pos.x + (sv_picker_size + bars_width) * 0.5, picker_pos.y + sv_picker_size * 0.5)
-- Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic.
local triangle_rotate = bit32.band(flags, 134217728 --[[ImGuiColorEditFlags.PickerNoRotate]]) == 0
local triangle_r = wheel_r_inner - math.floor(sv_picker_size * 0.027)
local triangle_pa, triangle_pb, triangle_pc
if triangle_rotate then triangle_pa = ImVec2( triangle_r, 0.0) else triangle_pa = ImVec2( triangle_r * 0.866, triangle_r * 0.5) end -- Hue point.
if triangle_rotate then triangle_pb = ImVec2(triangle_r * -0.5, triangle_r * -0.866) else triangle_pb = ImVec2( 0.0, -triangle_r) end -- Black point.
if triangle_rotate then triangle_pc = ImVec2(triangle_r * -0.5, triangle_r * 0.866) else triangle_pc = ImVec2(triangle_r * -0.866, triangle_r * 0.5) end -- White point.
local H = col[1]; local S = col[2]; local V = col[3]
local R = col[1]; local G = col[2]; local B = col[3]
if bit32.band(flags, 268435456 --[[ImGuiColorEditFlags.InputRGB]]) ~= 0 then
-- Hue is lost when converting from grayscale rgb (saturation=0). Restore it.
H, S, V = ImGui.ColorConvertRGBtoHSV(R, G, B)
H, S, V = ColorEditRestoreHS(col, H, S, V)
elseif bit32.band(flags, 536870912 --[[ImGuiColorEditFlags.InputHSV]]) ~= 0 then
R, G, B = ImGui.ColorConvertHSVtoRGB(H, S, V)
end
local value_changed = false; local value_changed_h = false; local value_changed_sv = false
ImGui.PushItemFlag(2 --[[ImGuiItemFlags.NoNav]], true)
if bit32.band(flags, 67108864 --[[ImGuiColorEditFlags.PickerHueWheel]]) ~= 0 then
-- Hue wheel + SV triangle logic
ImGui.InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size))
if ImGui.IsItemActive() and not is_readonly then
local initial_off = g.IO.MouseClickedPos[0] - wheel_center
local current_off = g.IO.MousePos - wheel_center
local initial_dist2 = ImLengthSqr(initial_off)
if initial_dist2 >= (wheel_r_inner - 1) * (wheel_r_inner - 1) and initial_dist2 <= (wheel_r_outer + 1) * (wheel_r_outer + 1) then
-- Interactive with Hue wheel
H = ImAtan2(current_off.y, current_off.x) / 3.141592653589793 --[[IM_PI]] * 0.5
if H < 0.0 then
H = H + 1.0
end
value_changed = true
value_changed_h = true
end
local cos_hue_angle = triangle_rotate and ImCos(-H * 2.0 * 3.141592653589793 --[[IM_PI]]) or 1.0
local sin_hue_angle = triangle_rotate and ImSin(-H * 2.0 * 3.141592653589793 --[[IM_PI]]) or 0.0
if ImStd.ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle)) then
-- Interacting with SV triangle
local current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle)
if not ImStd.ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated) then
current_off_unrotated = ImStd.ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated)
end
local uu, vv, ww
uu, vv, ww = ImStd.ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated)
V = ImClamp(1.0 - vv, 0.0001, 1.0)
S = ImClamp(uu / V, 0.0001, 1.0)
value_changed = true
value_changed_sv = true
end
end
if bit32.band(flags, 8 --[[ImGuiColorEditFlags.NoOptions]]) == 0 then
ImGui.OpenPopupOnItemClick("context", 8 --[[ImGuiPopupFlags.MouseButtonRight]])
end
elseif bit32.band(flags, 33554432 --[[ImGuiColorEditFlags.PickerHueBar]]) ~= 0 then
-- SV rectangle logic
ImGui.InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size))
if ImGui.IsItemActive() and not is_readonly then
S = ImSaturate((io.MousePos.x - picker_pos.x) / ImMax(sv_picker_size - 1, 0.0001))
V = 1.0 - ImSaturate((io.MousePos.y - picker_pos.y) / ImMax(sv_picker_size - 1, 0.0001))
H = ColorEditRestoreH(col, H) -- Greatly reduces hue jitter and reset to 0 when hue == 255 and color is rapidly modified using SV square.
value_changed = true
value_changed_sv = true
end
if bit32.band(flags, 8 --[[ImGuiColorEditFlags.NoOptions]]) == 0 then
ImGui.OpenPopupOnItemClick("context", 8 --[[ImGuiPopupFlags.MouseButtonRight]])
end
-- Hue bar logic
ImGui.SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y))
ImGui.InvisibleButton("hue", ImVec2(bars_width, sv_picker_size))
if ImGui.IsItemActive() and not is_readonly then
H = ImSaturate((io.MousePos.y - picker_pos.y) / ImMax(sv_picker_size - 1, 0.0001))
value_changed = true
value_changed_h = true
end
end
-- Alpha bar logic
if alpha_bar then
ImGui.SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y))
ImGui.InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size))
if ImGui.IsItemActive() then
col[4] = 1.0 - ImSaturate((io.MousePos.y - picker_pos.y) / ImMax(sv_picker_size - 1, 0.0001))
value_changed = true
end
end
ImGui.PopItemFlag() -- ImGuiItemFlags.NoNav
if bit32.band(flags, 256 --[[ImGuiColorEditFlags.NoSidePreview]]) == 0 then
ImGui.SameLine(0, style.ItemInnerSpacing.x)
ImGui.BeginGroup()
end
if bit32.band(flags, 128 --[[ImGuiColorEditFlags.NoLabel]]) == 0 then
local label_display_end = ImGui.FindRenderedTextEnd(label)
if label ~= "" and label_display_end > 1 then
if bit32.band(flags, 256 --[[ImGuiColorEditFlags.NoSidePreview]]) ~= 0 then
ImGui.SameLine(0, style.ItemInnerSpacing.x)
end
ImGui.TextEx(label, label_display_end)
end
end
if bit32.band(flags, 256 --[[ImGuiColorEditFlags.NoSidePreview]]) == 0 then
ImGui.PushItemFlag(4 --[[ImGuiItemFlags.NoNavDefaultFocus]], true)
local col_v4 = ImVec4(col[1], col[2], col[3], (bit32.band(flags, 2 --[[ImGuiColorEditFlags.NoAlpha]]) ~= 0) and 1.0 or col[4])
if bit32.band(flags, 128 --[[ImGuiColorEditFlags.NoLabel]]) ~= 0 then
ImGui.Text("Current")
end
local sub_flags_to_forward = bit32.bor(805306368 --[[ImGuiColorEditFlags.InputMask_]], 524288 --[[ImGuiColorEditFlags.HDR]], 28674 --[[ImGuiColorEditFlags.AlphaMask_]], 64 --[[ImGuiColorEditFlags.NoTooltip]])
ImGui.ColorButton("##current", col_v4, bit32.band(flags, sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2))
if ref_col ~= nil then
ImGui.Text("Original")
local ref_col_v4 = ImVec4(ref_col[1], ref_col[2], ref_col[3], (bit32.band(flags, 2 --[[ImGuiColorEditFlags.NoAlpha]]) ~= 0) and 1.0 or ref_col[4])
if ImGui.ColorButton("##original", ref_col_v4, bit32.band(flags, sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2)) then
for i = 1, components do
col[i] = ref_col[i]
end
value_changed = true
end
end
ImGui.PopItemFlag()
ImGui.EndGroup()
end
-- Convert back color to RGB
if value_changed_h or value_changed_sv then
if bit32.band(flags, 268435456 --[[ImGuiColorEditFlags.InputRGB]]) ~= 0 then
col[1], col[2], col[3] = ImGui.ColorConvertHSVtoRGB(H, S, V) -- Lua 1-based indexing
g.ColorEditSavedHue = H
g.ColorEditSavedSat = S
g.ColorEditSavedID = g.ColorEditCurrentID
g.ColorEditSavedColor = ImGui.ColorConvertFloat4ToU32(ImVec4(col[1], col[2], col[3], 0))
elseif bit32.band(flags, 536870912 --[[ImGuiColorEditFlags.InputHSV]]) ~= 0 then
col[1] = H
col[2] = S
col[3] = V
end
end
-- R,G,B and H,S,V slider color editor
local value_changed_fix_hue_wrap = false
if bit32.band(flags, 32 --[[ImGuiColorEditFlags.NoInputs]]) == 0 then
ImGui.PushItemWidth((alpha_bar and bar1_pos_x or bar0_pos_x) + bars_width - picker_pos.x)
local sub_flags_to_forward = bit32.bor(25165824 --[[ImGuiColorEditFlags.DataTypeMask_]], 805306368 --[[ImGuiColorEditFlags.InputMask_]], 524288 --[[ImGuiColorEditFlags.HDR]], 28674 --[[ImGuiColorEditFlags.AlphaMask_]], 8 --[[ImGuiColorEditFlags.NoOptions]], 64 --[[ImGuiColorEditFlags.NoTooltip]], 16 --[[ImGuiColorEditFlags.NoSmallPreview]])
local sub_flags = bit32.bor(bit32.band(flags, sub_flags_to_forward), 4 --[[ImGuiColorEditFlags.NoPicker]])
if bit32.band(flags, 1048576 --[[ImGuiColorEditFlags.DisplayRGB]]) ~= 0 or bit32.band(flags, 7340032 --[[ImGuiColorEditFlags.DisplayMask_]]) == 0 then
if ImGui.ColorEdit4("##rgb", col, bit32.bor(sub_flags, 1048576 --[[ImGuiColorEditFlags.DisplayRGB]])) then
-- FIXME: Hackily differentiating using the DragInt (ActiveId != 0 && !ActiveIdAllowOverlap) vs. using the InputText or DropTarget.
-- For the later we don't want to run the hue-wrap canceling code. If you are well versed in HSV picker please provide your input! (See #2050)
value_changed_fix_hue_wrap = (g.ActiveId ~= 0 and not g.ActiveIdAllowOverlap)
value_changed = true
end
end
if bit32.band(flags, 2097152 --[[ImGuiColorEditFlags.DisplayHSV]]) ~= 0 or bit32.band(flags, 7340032 --[[ImGuiColorEditFlags.DisplayMask_]]) == 0 then
if ImGui.ColorEdit4("##hsv", col, bit32.bor(sub_flags, 2097152 --[[ImGuiColorEditFlags.DisplayHSV]])) then
value_changed = true
end
end
if bit32.band(flags, 4194304 --[[ImGuiColorEditFlags.DisplayHex]]) ~= 0 or bit32.band(flags, 7340032 --[[ImGuiColorEditFlags.DisplayMask_]]) == 0 then
if ImGui.ColorEdit4("##hex", col, bit32.bor(sub_flags, 4194304 --[[ImGuiColorEditFlags.DisplayHex]])) then
value_changed = true
end
end
ImGui.PopItemWidth()
end
-- Try to cancel hue wrap (after ColorEdit4 call), if any
if value_changed_fix_hue_wrap and bit32.band(flags, 268435456 --[[ImGuiColorEditFlags.InputRGB]]) ~= 0 then
local new_H, new_S, new_V = ImGui.ColorConvertRGBtoHSV(col[1], col[2], col[3]) -- Lua 1-based indexing
if new_H <= 0 and H > 0 then
if new_V <= 0 and V ~= new_V then
col[1], col[2], col[3] = ImGui.ColorConvertHSVtoRGB(H, S, (new_V <= 0) and (V * 0.5) or new_V)
elseif new_S <= 0 then
col[1], col[2], col[3] = ImGui.ColorConvertHSVtoRGB(H, (new_S <= 0) and (S * 0.5) or new_S, new_V)
end
end
end
if value_changed then
if bit32.band(flags, 268435456 --[[ImGuiColorEditFlags.InputRGB]]) ~= 0 then
R = col[1]
G = col[2]
B = col[3]
H, S, V = ImGui.ColorConvertRGBtoHSV(R, G, B)
H, S, V = ColorEditRestoreHS(col, H, S, V) -- Fix local Hue as display below will use it immediately.
elseif bit32.band(flags, 536870912 --[[ImGuiColorEditFlags.InputHSV]]) ~= 0 then
H = col[1]
S = col[2]
V = col[3]
R, G, B = ImGui.ColorConvertHSVtoRGB(H, S, V)
end
end
local style_alpha8 = IM_F32_TO_INT8_SAT(style.Alpha)
local col_black = IM_COL32(0, 0, 0, style_alpha8)
local col_white = IM_COL32(255, 255, 255, style_alpha8)
local col_midgrey = IM_COL32(128, 128, 128, style_alpha8)
local col_hues = { IM_COL32(255, 0, 0, style_alpha8), IM_COL32(255, 255, 0, style_alpha8), IM_COL32(0, 255, 0, style_alpha8), IM_COL32(0, 255, 255, style_alpha8), IM_COL32(0, 0, 255, style_alpha8), IM_COL32(255, 0, 255, style_alpha8), IM_COL32(255, 0, 0, style_alpha8) }
local hue_color_f = ImVec4(1, 1, 1, style.Alpha)
hue_color_f.x, hue_color_f.y, hue_color_f.z = ImGui.ColorConvertHSVtoRGB(H, 1, 1)
local hue_color32 = ImGui.ColorConvertFloat4ToU32(hue_color_f)
local user_col32_striped_of_alpha = ImGui.ColorConvertFloat4ToU32(ImVec4(R, G, B, style.Alpha)) -- Important: this is still including the main rendering/style alpha!!
local sv_cursor_pos = ImVec2()
if bit32.band(flags, 67108864 --[[ImGuiColorEditFlags.PickerHueWheel]]) ~= 0 then
-- Render Hue Wheel
local aeps = 0.5 / wheel_r_outer -- Half a pixel arc length in radians (2pi cancels out).
local segment_per_arc = ImMax(4, math.floor(wheel_r_outer / 12))
for n = 1, 6 do
local a0 = (n - 1) / 6.0 * 2.0 * 3.141592653589793 --[[IM_PI]] - aeps
local a1 = n / 6.0 * 2.0 * 3.141592653589793 --[[IM_PI]] + aeps
local vert_start_idx = draw_list.VtxBuffer.Size + 1
draw_list:PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer) * 0.5, a0, a1, segment_per_arc)
draw_list:PathStroke(col_white, wheel_thickness)
local vert_end_idx = draw_list.VtxBuffer.Size + 1
-- Paint colors over existing vertices
local gradient_p0 = ImVec2(wheel_center.x + ImCos(a0) * wheel_r_inner, wheel_center.y + ImSin(a0) * wheel_r_inner)
local gradient_p1 = ImVec2(wheel_center.x + ImCos(a1) * wheel_r_inner, wheel_center.y + ImSin(a1) * wheel_r_inner)
ImGui.ShadeVertsLinearColorGradientKeepAlpha(draw_list, vert_start_idx, vert_end_idx, gradient_p0, gradient_p1, col_hues[n], col_hues[n + 1])
end
-- Render Cursor + preview on Hue Wheel
local cos_hue_angle = ImCos(H * 2.0 * 3.141592653589793 --[[IM_PI]])
local sin_hue_angle = ImSin(H * 2.0 * 3.141592653589793 --[[IM_PI]])
local hue_cursor_pos = ImVec2(wheel_center.x + cos_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5, wheel_center.y + sin_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5)
local hue_cursor_rad = value_changed_h and (wheel_thickness * 0.65) or (wheel_thickness * 0.55)
local hue_cursor_segments = draw_list:_CalcCircleAutoSegmentCount(hue_cursor_rad) -- Lock segment count so the +1 one matches others.
draw_list:AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments)
draw_list:AddCircle(hue_cursor_pos, hue_cursor_rad + 1, col_midgrey, hue_cursor_segments)
draw_list:AddCircle(hue_cursor_pos, hue_cursor_rad, col_white, hue_cursor_segments)
if triangle_rotate == false then
cos_hue_angle = 1.0
sin_hue_angle = 0.0
end
-- Render SV triangle (rotated according to hue)
local tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle)
local trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle)
local trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle)
local uv_white = ImGui.GetFontTexUvWhitePixel()
draw_list:PrimReserve(3, 3)
draw_list:PrimVtx(tra, uv_white, hue_color32)
draw_list:PrimVtx(trb, uv_white, col_black)
draw_list:PrimVtx(trc, uv_white, col_white)
draw_list:AddTriangle(tra, trb, trc, col_midgrey, 1.5)
sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V))
elseif bit32.band(flags, 33554432 --[[ImGuiColorEditFlags.PickerHueBar]]) ~= 0 then
-- Render SV Square
draw_list:AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), col_white, hue_color32, hue_color32, col_white)
draw_list:AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), 0, 0, col_black, col_black)
ImGui.RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), 0.0)
sv_cursor_pos.x = ImClamp(IM_ROUND(picker_pos.x + ImSaturate(S) * sv_picker_size), picker_pos.x + 2, picker_pos.x + sv_picker_size - 2) -- Sneakily prevent the circle to stick out too much
sv_cursor_pos.y = ImClamp(IM_ROUND(picker_pos.y + ImSaturate(1 - V) * sv_picker_size), picker_pos.y + 2, picker_pos.y + sv_picker_size - 2)
-- Render Hue Bar
for i = 1, 6 do
draw_list:AddRectFilledMultiColor(ImVec2(bar0_pos_x, picker_pos.y + (i - 1) * (sv_picker_size / 6)), ImVec2(bar0_pos_x + bars_width, picker_pos.y + i * (sv_picker_size / 6)), col_hues[i], col_hues[i], col_hues[i + 1], col_hues[i + 1])
end
local bar0_line_y = IM_ROUND(picker_pos.y + H * sv_picker_size)
ImGui.RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0)
RenderArrowsForVerticalBar(draw_list, ImVec2(bar0_pos_x - 1, bar0_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0, style.Alpha)
end
-- Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range)
local sv_cursor_rad = value_changed_sv and (wheel_thickness * 0.55) or (wheel_thickness * 0.40)
local sv_cursor_segments = draw_list:_CalcCircleAutoSegmentCount(sv_cursor_rad) -- Lock segment count so the +1 one matches others.
draw_list:AddCircleFilled(sv_cursor_pos, sv_cursor_rad, user_col32_striped_of_alpha, sv_cursor_segments)
draw_list:AddCircle(sv_cursor_pos, sv_cursor_rad + 1, col_midgrey, sv_cursor_segments)
draw_list:AddCircle(sv_cursor_pos, sv_cursor_rad, col_white, sv_cursor_segments)
-- Render alpha bar
if alpha_bar then
local alpha = ImSaturate(col[4])
local bar1_bb = ImRect(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size)
ImGui.RenderColorRectWithAlphaCheckerboard(draw_list, bar1_bb.Min, bar1_bb.Max, IM_COL32(0, 0, 0, 0), style.Alpha, bar1_bb:GetWidth() / 2.0, ImVec2(0.0, 0.0))
draw_list:AddRectFilledMultiColor(bar1_bb.Min, bar1_bb.Max, user_col32_striped_of_alpha, user_col32_striped_of_alpha, bit32.band(user_col32_striped_of_alpha, bit32.bnot(4278190080 --[[IM_COL32_A_MASK]])), bit32.band(user_col32_striped_of_alpha, bit32.bnot(4278190080 --[[IM_COL32_A_MASK]])))
local bar1_line_y = IM_ROUND(picker_pos.y + (1.0 - alpha) * sv_picker_size)
ImGui.RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0)
RenderArrowsForVerticalBar(draw_list, ImVec2(bar1_pos_x - 1, bar1_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0, style.Alpha)
end
ImGui.EndGroup()
if value_changed then
for i = 1, components do
if backup_initial_col[i] ~= col[i] then
break
end
if i == components then
value_changed = false
end
end
end
if value_changed and g.LastItemData.ID ~= 0 then -- In case of ID collision, the second EndGroup() won't catch g.ActiveId
ImGui.MarkItemEdited(g.LastItemData.ID)
end
if set_current_color_edit_id then
g.ColorEditCurrentID = 0
end
ImGui.PopID()
return value_changed
end
end
-- A little color square. Return true when clicked.
-- FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip.
-- 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip.
-- Note that 'col' may be encoded in HSV if ImGuiColorEditFlags.InputHSV is set.
--- @param desc_id string
--- @param col ImVec4
--- @param flags? ImGuiColorEditFlags
--- @param size_arg? ImVec2
@native
function ImGui.ColorButton(desc_id, col, flags, size_arg)
if flags == nil then flags = 0 end
if size_arg == nil then size_arg = ImVec2(0, 0) end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
local g = GImGui
local id = window:GetID(desc_id)
local default_size = ImGui.GetFrameHeight()
local size = ImVec2(size_arg.x == 0.0 and default_size or size_arg.x, size_arg.y == 0.0 and default_size or size_arg.y)
local bb = ImRect(window.DC.CursorPos, window.DC.CursorPos + size)
ImGui.ItemSize(bb, (size.y >= default_size) and g.Style.FramePadding.y or 0.0)
if not ImGui.ItemAdd(bb, id) then
return false
end
local pressed, hovered, _ = ImGui.ButtonBehavior(bb, id)
if bit32.band(flags, bit32.bor(2 --[[ImGuiColorEditFlags.NoAlpha]], 4096 --[[ImGuiColorEditFlags.AlphaOpaque]])) ~= 0 then
flags = bit32.band(flags, bit32.bnot(bit32.bor(8192 --[[ImGuiColorEditFlags.AlphaNoBg]], 16384 --[[ImGuiColorEditFlags.AlphaPreviewHalf]])))
end
local col_rgb = ImVec4(col.x, col.y, col.z, col.w)
if bit32.band(flags, 536870912 --[[ImGuiColorEditFlags.InputHSV]]) ~= 0 then
col_rgb.x, col_rgb.y, col_rgb.z = ImGui.ColorConvertHSVtoRGB(col_rgb.x, col_rgb.y, col_rgb.z)
end
local col_rgb_without_alpha = ImVec4(col_rgb.x, col_rgb.y, col_rgb.z, 1.0)
local grid_step = ImMin(size.x, size.y) / 2.99
local rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5)
local is_mixed = bit32.band(g.LastItemData.ItemFlags, 4096 --[[ImGuiItemFlags.MixedValue]]) ~= 0
local backup_alpha = g.Style.Alpha
if g.DisabledStackSize > 0 then
g.Style.Alpha = g.DisabledAlphaBackup -- Cancel out effect of BeginDisabled() for color swatches.
end
if is_mixed then
window.DrawList:AddRectFilled(bb.Min, bb.Max, ImGui.GetColorU32(7 --[[ImGuiCol.FrameBg]]), rounding)
ImGui.PushStyleColor(0 --[[ImGuiCol.Text]], g.Style.Colors[1 --[[ImGuiCol.TextDisabled]]]) -- ImGuiCol_TextMixedValue
if g.MixedValueLabel ~= nil then
ImGui.RenderTextClipped(ImVec2(bb.Min.x, bb.Min.y + g.Style.FramePadding.y), bb.Max, g.MixedValueLabel, nil, nil, ImVec2(0.5, 0.0))
end
ImGui.PopStyleColor()
elseif bit32.band(flags, 16384 --[[ImGuiColorEditFlags.AlphaPreviewHalf]]) ~= 0 and col_rgb.w < 1.0 then
local mid_x = IM_ROUND((bb.Min.x + bb.Max.x) * 0.5)
if bit32.band(flags, 8192 --[[ImGuiColorEditFlags.AlphaNoBg]]) == 0 then
ImGui.RenderColorRectWithAlphaCheckerboard(window.DrawList, ImVec2(bb.Min.x + grid_step, bb.Min.y), bb.Max, ImGui.ColorConvertFloat4ToU32(col_rgb), g.Style.Alpha, grid_step, ImVec2(-grid_step, 0.0), rounding, 160 --[[ImDrawFlags.RoundRight]])
else
window.DrawList:AddRectFilled(ImVec2(bb.Min.x + grid_step, bb.Min.y), bb.Max, ImGui.GetColorU32(col_rgb), rounding, 160 --[[ImDrawFlags.RoundRight]])
end
window.DrawList:AddRectFilled(bb.Min, ImVec2(mid_x, bb.Max.y), ImGui.GetColorU32(col_rgb_without_alpha), rounding, 80 --[[ImDrawFlags.RoundLeft]])
else
-- Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha
local col_source = (bit32.band(flags, 4096 --[[ImGuiColorEditFlags.AlphaOpaque]]) ~= 0) and col_rgb_without_alpha or col_rgb
if col_source.w < 1.0 and bit32.band(flags, 8192 --[[ImGuiColorEditFlags.AlphaNoBg]]) == 0 then
ImGui.RenderColorRectWithAlphaCheckerboard(window.DrawList, bb.Min, bb.Max, ImGui.ColorConvertFloat4ToU32(col_source), g.Style.Alpha, grid_step, ImVec2(0.0, 0.0), rounding)
else
window.DrawList:AddRectFilled(bb.Min, bb.Max, ImGui.GetColorU32(col_source), rounding)
end
end
if g.DisabledStackSize > 0 then
g.Style.Alpha = backup_alpha
end
ImGui.RenderNavCursor(bb, id)
if bit32.band(flags, 1024 --[[ImGuiColorEditFlags.NoBorder]]) == 0 then
if g.Style.FrameBorderSize > 0.0 then
ImGui.RenderFrameBorder(bb.Min, bb.Max, rounding)
else
window.DrawList:AddRect(bb.Min, bb.Max, ImGui.GetColorU32(7 --[[ImGuiCol.FrameBg]]), rounding, 1.0 * ImTrunc(g.Style._MainScale)) -- Color buttons are often in need of some sort of border -- FIXME-DPI
end
end
-- Drag and Drop Source
-- NB: The ActiveId test is merely an optional micro-optimization, BeginDragDropSource() does the same test.
-- [Port] payload.Data is a 1-based float array copy { r, g, b [, a] } (C++ memcpy of float[3]/float[4])
if g.ActiveId == id and bit32.band(flags, 512 --[[ImGuiColorEditFlags.NoDragDrop]]) == 0 and ImGui.BeginDragDropSource() then
if bit32.band(flags, 2 --[[ImGuiColorEditFlags.NoAlpha]]) ~= 0 then
ImGui.SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, { col_rgb.x, col_rgb.y, col_rgb.z }, 3, 2 --[[ImGuiCond.Once]])
else
ImGui.SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, { col_rgb.x, col_rgb.y, col_rgb.z, col_rgb.w }, 4, 2 --[[ImGuiCond.Once]])
end
ImGui.PushItemFlag(4096 --[[ImGuiItemFlags.MixedValue]], false)
ImGui.ColorButton(desc_id, col, flags)
ImGui.PopItemFlag()
ImGui.SameLine()
ImGui.TextEx("Color")
ImGui.EndDragDropSource()
end
-- Tooltip
if bit32.band(flags, 64 --[[ImGuiColorEditFlags.NoTooltip]]) == 0 and hovered and ImGui.IsItemHovered(4096 --[[ImGuiHoveredFlags.ForTooltip]]) then
ImGui.ColorTooltip(desc_id, col, bit32.band(flags, bit32.bor(805306368 --[[ImGuiColorEditFlags.InputMask_]], 28674 --[[ImGuiColorEditFlags.AlphaMask_]])))
end
return pressed
end
-- Note: only access 3 floats if ImGuiColorEditFlags.NoAlpha flag is set.
--- @param text? string
--- @param col ImVec4
--- @param flags ImGuiColorEditFlags
function ImGui.ColorTooltip(text, col, flags)
local g = GImGui
if not ImGui.BeginTooltipEx(2 --[[ImGuiTooltipFlags.OverridePrevious]], 0 --[[ImGuiWindowFlags.None]]) then
return
end
local text_end = text and ImGui.FindRenderedTextEnd(text, nil) or 1
if text_end > 1 then
--- @cast text string
ImGui.TextEx(text, text_end)
ImGui.Separator()
end
local sz = ImVec2(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2)
local cf = ImVec4(col.x, col.y, col.z, (bit32.band(flags, 2 --[[ImGuiColorEditFlags.NoAlpha]]) ~= 0) and 1.0 or col.w)
local cr = IM_F32_TO_INT8_SAT(col.x)
local cg = IM_F32_TO_INT8_SAT(col.y)
local cb = IM_F32_TO_INT8_SAT(col.z)
local ca = (bit32.band(flags, 2 --[[ImGuiColorEditFlags.NoAlpha]]) ~= 0) and 255 or IM_F32_TO_INT8_SAT(col.w)
local flags_to_forward = bit32.bor(805306368 --[[ImGuiColorEditFlags.InputMask_]], 28674 --[[ImGuiColorEditFlags.AlphaMask_]])
ImGui.ColorButton("##preview", cf, bit32.bor(bit32.band(flags, flags_to_forward), 64 --[[ImGuiColorEditFlags.NoTooltip]]), sz)
ImGui.SameLine()
if bit32.band(flags, 268435456 --[[ImGuiColorEditFlags.InputRGB]]) ~= 0 or bit32.band(flags, 805306368 --[[ImGuiColorEditFlags.InputMask_]]) == 0 then
if bit32.band(flags, 2 --[[ImGuiColorEditFlags.NoAlpha]]) ~= 0 then
ImGui.Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col.x, col.y, col.z)
else
ImGui.Text("#%02X%02X%02X%02X\nR:%d, G:%d, B:%d, A:%d\n(%.3f, %.3f, %.3f, %.3f)", cr, cg, cb, ca, cr, cg, cb, ca, col.x, col.y, col.z, col.w)
end
elseif bit32.band(flags, 536870912 --[[ImGuiColorEditFlags.InputHSV]]) ~= 0 then
if bit32.band(flags, 2 --[[ImGuiColorEditFlags.NoAlpha]]) ~= 0 then
ImGui.Text("H: %.3f, S: %.3f, V: %.3f", col.x, col.y, col.z)
else
ImGui.Text("H: %.3f, S: %.3f, V: %.3f, A: %.3f", col.x, col.y, col.z, col.w)
end
end
ImGui.EndTooltip()
end
--- @param col float[]
--- @param flags ImGuiColorEditFlags
function ImGui.ColorEditOptionsPopup(col, flags)
local allow_opt_inputs = bit32.band(flags, 7340032 --[[ImGuiColorEditFlags.DisplayMask_]]) == 0
local allow_opt_datatype = bit32.band(flags, 25165824 --[[ImGuiColorEditFlags.DataTypeMask_]]) == 0
if (not allow_opt_inputs and not allow_opt_datatype) or not ImGui.BeginPopup("context") then
return
end
local g = GImGui
ImGui.PushItemFlag(65536 --[[ImGuiItemFlags.NoMarkEdited]], true)
local opts = g.IO.ConfigColorEditFlags
if allow_opt_inputs then
if ImGui.RadioButtonEx("RGB", bit32.band(opts, 1048576 --[[ImGuiColorEditFlags.DisplayRGB]]) ~= 0) then
opts = bit32.bor(bit32.band(opts, bit32.bnot(7340032 --[[ImGuiColorEditFlags.DisplayMask_]])), 1048576 --[[ImGuiColorEditFlags.DisplayRGB]])
end
if ImGui.RadioButtonEx("HSV", bit32.band(opts, 2097152 --[[ImGuiColorEditFlags.DisplayHSV]]) ~= 0) then
opts = bit32.bor(bit32.band(opts, bit32.bnot(7340032 --[[ImGuiColorEditFlags.DisplayMask_]])), 2097152 --[[ImGuiColorEditFlags.DisplayHSV]])
end
if ImGui.RadioButtonEx("Hex", bit32.band(opts, 4194304 --[[ImGuiColorEditFlags.DisplayHex]]) ~= 0) then
opts = bit32.bor(bit32.band(opts, bit32.bnot(7340032 --[[ImGuiColorEditFlags.DisplayMask_]])), 4194304 --[[ImGuiColorEditFlags.DisplayHex]])
end
end
if allow_opt_datatype then
if allow_opt_inputs then ImGui.Separator() end
if ImGui.RadioButtonEx("0..255", bit32.band(opts, 8388608 --[[ImGuiColorEditFlags.Uint8]]) ~= 0) then
opts = bit32.bor(bit32.band(opts, bit32.bnot(25165824 --[[ImGuiColorEditFlags.DataTypeMask_]])), 8388608 --[[ImGuiColorEditFlags.Uint8]])
end
if ImGui.RadioButtonEx("0.00..1.00", bit32.band(opts, 16777216 --[[ImGuiColorEditFlags.Float]]) ~= 0) then
opts = bit32.bor(bit32.band(opts, bit32.bnot(25165824 --[[ImGuiColorEditFlags.DataTypeMask_]])), 16777216 --[[ImGuiColorEditFlags.Float]])
end
end
if allow_opt_inputs or allow_opt_datatype then
ImGui.Separator()
end
if ImGui.Button("Copy as..", ImVec2(-1, 0)) then
ImGui.OpenPopup("Copy")
end
if ImGui.BeginPopup("Copy") then
local cr = IM_F32_TO_INT8_SAT(col[1])
local cg = IM_F32_TO_INT8_SAT(col[2])
local cb = IM_F32_TO_INT8_SAT(col[3])
local ca = (bit32.band(flags, 2 --[[ImGuiColorEditFlags.NoAlpha]]) ~= 0) and 255 or IM_F32_TO_INT8_SAT(col[4])
local buf1 = ImFormatString("(%.3ff, %.3ff, %.3ff, %.3ff)", col[1], col[2], col[3], (bit32.band(flags, 2 --[[ImGuiColorEditFlags.NoAlpha]]) ~= 0) and 1.0 or col[4])
if ImGui.Selectable(buf1) then
ImGui.SetClipboardText(buf1)
end
local buf2 = ImFormatString("(%d,%d,%d,%d)", cr, cg, cb, ca)
if ImGui.Selectable(buf2) then
ImGui.SetClipboardText(buf2)
end
local buf3 = ImFormatString("#%02X%02X%02X", cr, cg, cb)
if ImGui.Selectable(buf3) then
ImGui.SetClipboardText(buf3)
end
if bit32.band(flags, 2 --[[ImGuiColorEditFlags.NoAlpha]]) == 0 then
local buf4 = ImFormatString("#%02X%02X%02X%02X", cr, cg, cb, ca)
if ImGui.Selectable(buf4) then
ImGui.SetClipboardText(buf4)
end
end
ImGui.EndPopup()
end
g.IO.ConfigColorEditFlags = opts
ImGui.PopItemFlag()
ImGui.EndPopup()
end
--- @param ref_col float[]
--- @param flags ImGuiColorEditFlags
function ImGui.ColorPickerOptionsPopup(ref_col, flags)
local allow_opt_picker = bit32.band(flags, 100663296 --[[ImGuiColorEditFlags.PickerMask_]]) == 0
local allow_opt_alpha_bar = (bit32.band(flags, 2 --[[ImGuiColorEditFlags.NoAlpha]]) == 0) and (bit32.band(flags, 262144 --[[ImGuiColorEditFlags.AlphaBar]]) == 0)
if (not allow_opt_picker and not allow_opt_alpha_bar) or not ImGui.BeginPopup("context") then
return
end
local g = GImGui
ImGui.PushItemFlag(65536 --[[ImGuiItemFlags.NoMarkEdited]], true)
if allow_opt_picker then
local picker_size = ImVec2(g.FontSize * 8, ImMax(g.FontSize * 8 - (ImGui.GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0)) -- FIXME: Picker size copied from main picker function
ImGui.PushItemWidth(picker_size.x)
for picker_type = 0, 1 do
-- Draw small/thumbnail version of each picker type (over an invisible button for selection)
if picker_type > 0 then
ImGui.Separator()
end
ImGui.PushID(picker_type)
local picker_flags = bit32.bor(32 --[[ImGuiColorEditFlags.NoInputs]], 8 --[[ImGuiColorEditFlags.NoOptions]], 128 --[[ImGuiColorEditFlags.NoLabel]], 256 --[[ImGuiColorEditFlags.NoSidePreview]], bit32.band(flags, 2 --[[ImGuiColorEditFlags.NoAlpha]]))
if picker_type == 0 then
picker_flags = bit32.bor(picker_flags, 33554432 --[[ImGuiColorEditFlags.PickerHueBar]])
end
if picker_type == 1 then
picker_flags = bit32.bor(picker_flags, 67108864 --[[ImGuiColorEditFlags.PickerHueWheel]])
end
local backup_pos = ImGui.GetCursorScreenPos()
if ImGui.Selectable("##selectable", false, 0, picker_size) then -- By default, Selectable() is closing popup
g.IO.ConfigColorEditFlags = bit32.bor(bit32.band(g.IO.ConfigColorEditFlags, bit32.bnot(100663296 --[[ImGuiColorEditFlags.PickerMask_]])), bit32.band(picker_flags, 100663296 --[[ImGuiColorEditFlags.PickerMask_]]))
end
ImGui.SetCursorScreenPos(backup_pos)
local previewing_ref_col = ImVec4()
for i = 1, (bit32.band(picker_flags, 2 --[[ImGuiColorEditFlags.NoAlpha]]) ~= 0 and 3 or 4) do
previewing_ref_col[i] = ref_col[i]
end
ImGui.ColorPicker4("##previewing_picker", previewing_ref_col, picker_flags)
ImGui.PopID()
end
ImGui.PopItemWidth()
end
if allow_opt_alpha_bar then
if allow_opt_picker then
ImGui.Separator()
end
_, g.IO.ConfigColorEditFlags = ImGui.CheckboxFlags("Alpha Bar", g.IO.ConfigColorEditFlags, 262144 --[[ImGuiColorEditFlags.AlphaBar]])
end
ImGui.PopItemFlag()
ImGui.EndPopup()
end
-------------------------------------------------------------------------------
-- [SECTION] Widgets: TreeNode, CollapsingHeader, etc.
-------------------------------------------------------------------------------
-- - TreeNode()
-- - TreeNodeV()
-- - TreeNodeEx()
-- - TreeNodeExV()
-- - TreeNodeStoreStackData() [Internal]
-- - TreeNodeBehavior() [Internal]
-- - TreePush()
-- - TreePop()
-- - GetTreeNodeToLabelSpacing()
-- - SetNextItemOpen()
-- - CollapsingHeader()
-------------------------------------------------------------------------------
-- va_list variants: identical in Lua (varargs)
ImGui.TextColoredV = function(...) return ImGui.TextColored(...) end
ImGui.TextDisabledV = function(...) return ImGui.TextDisabled(...) end
ImGui.TextWrappedV = function(...) return ImGui.TextWrapped(...) end
ImGui.TextAlignedV = function(...) return ImGui.TextAligned(...) end
ImGui.LabelTextV = function(...) return ImGui.LabelText(...) end
ImGui.BulletTextV = function(...) return ImGui.BulletText(...) end
--- @param data_type ImGuiDataType
--- @param v number
function ImGui.SetNextItemRefVal(data_type, v)
local g = GImGui
g.NextItemData.HasFlags = bit32.bor(g.NextItemData.HasFlags, 8 --[[ImGuiNextItemDataFlags.HasRefVal]])
g.NextItemData.RefVal = v
end
-- Overloads: TreeNode(label) / TreeNode(str_id, fmt, ...)
--- @param label string
@native
function ImGui.TreeNode(label, fmt, ...)
if fmt ~= nil then
return ImGui.TreeNodeEx(label, 0, fmt, ...)
end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
local id = window:GetID(label)
return ImGui.TreeNodeBehavior(id, 0 --[[ImGuiTreeNodeFlags.None]], label, nil)
end
function ImGui.TreeNodeV(str_id, fmt, ...)
return ImGui.TreeNodeExV(str_id, 0, fmt, ...)
end
-- Overloads: TreeNodeEx(label, flags) / TreeNodeEx(str_id, flags, fmt, ...)
--- @param label string
--- @param flags? ImGuiTreeNodeFlags
function ImGui.TreeNodeEx(label, flags, fmt, ...)
if flags == nil then flags = 0 end
if fmt ~= nil then
return ImGui.TreeNodeExV(label, flags, fmt, ...)
end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
local id = window:GetID(label)
return ImGui.TreeNodeBehavior(id, flags, label, nil)
end
--- @param str_id string|ImGuiID
function ImGui.TreeNodeExV(str_id, flags, fmt, ...)
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
local id = window:GetID(str_id)
local label = (select("#", ...) > 0) and string.format(fmt, ...) or fmt
return ImGui.TreeNodeBehavior(id, flags, label, #label + 1)
end
-- The reason those two functions are not yet in public API is because I would like to design a more feature-full and generic API for this.
-- They are otherwise function (cc: #3823, #9251, #7553, #6754, #5423, #2958, #2079, #1947, #1131, #722)
--- @param storage_id ImGuiID
function ImGui.TreeNodeGetOpen(storage_id)
local g = GImGui
local storage = g.CurrentWindow.DC.StateStorage
return (storage[storage_id]) and true or false
end
--- @param storage_id ImGuiID
--- @param is_open bool
function ImGui.TreeNodeSetOpen(storage_id, is_open)
local g = GImGui
local storage = g.CurrentWindow.DC.StateStorage
storage[storage_id] = is_open
end
--- @param storage_id ImGuiID
--- @param flags ImGuiTreeNodeFlags
function ImGui.TreeNodeUpdateNextOpen(storage_id, flags)
-- Leaf node always open a new tree/id scope. If you never use it, add ImGuiTreeNodeFlags.NoTreePushOnOpen.
if bit32.band(flags, 256 --[[ImGuiTreeNodeFlags.Leaf]]) ~= 0 then
return true
end
-- We only write to the tree storage if the user clicks, or explicitly use the SetNextItemOpen function
local g = GImGui
local window = g.CurrentWindow
local storage = window.DC.StateStorage
local is_open
if bit32.band(g.NextItemData.HasFlags, 2 --[[ImGuiNextItemDataFlags.HasOpen]]) ~= 0 then
if bit32.band(g.NextItemData.OpenCond, 1 --[[ImGuiCond.Always]]) ~= 0 then
is_open = g.NextItemData.OpenVal
ImGui.TreeNodeSetOpen(storage_id, is_open)
else
-- We treat ImGuiCond.Once and ImGuiCond.FirstUseEver the same because tree node state are not saved persistently.
local stored_value = storage[storage_id]
if stored_value == nil then
is_open = g.NextItemData.OpenVal
ImGui.TreeNodeSetOpen(storage_id, is_open)
else
is_open = stored_value and true or false
end
end
else
is_open = (storage[storage_id] == nil) and (bit32.band(flags, 32 --[[ImGuiTreeNodeFlags.DefaultOpen]]) ~= 0) or storage[storage_id]
end
-- When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior).
-- NB- If we are above max depth we still allow manually opened nodes to be logged.
if g.LogEnabled and bit32.band(flags, 16 --[[ImGuiTreeNodeFlags.NoAutoOpenOnLog]]) == 0 and (window.DC.TreeDepth - g.LogDepthRef) < g.LogDepthToExpand then
is_open = true
end
return is_open
end
-- Store ImGuiTreeNodeStackData for just submitted node.
-- Currently only supports 32 level deep. Easy to increase to e.g. 64.
--- @param flags ImGuiTreeNodeFlags
--- @param x1 float
local function TreeNodeStoreStackData(flags, x1)
local g = GImGui
local window = g.CurrentWindow
g.TreeNodeStack:resize(g.TreeNodeStack.Size + 1)
local tree_node_data = g.TreeNodeStack.Data[g.TreeNodeStack.Size]
tree_node_data.ID = g.LastItemData.ID
tree_node_data.TreeFlags = flags
tree_node_data.ItemFlags = g.LastItemData.ItemFlags
ImRect_Copy(tree_node_data.NavRect, g.LastItemData.NavRect)
-- Initially I tried to latch value for GetColorU32(ImGuiCol.TreeLines) but it's not a good trade-off for very large trees.
local draw_lines = bit32.band(flags, bit32.bor(524288 --[[ImGuiTreeNodeFlags.DrawLinesFull]], 1048576 --[[ImGuiTreeNodeFlags.DrawLinesToNodes]])) ~= 0
tree_node_data.DrawLinesX1 = draw_lines and (x1 + g.FontSize * 0.5 + g.Style.FramePadding.x) or 3.4028234663853e+38 --[[FLT_MAX]]
tree_node_data.DrawLinesTableColumn = (draw_lines and g.CurrentTable) and g.CurrentTable.CurrentColumn or -1
tree_node_data.DrawLinesToNodesY2 = -3.4028234663853e+38 --[[FLT_MAX]]
window.DC.TreeHasStackDataDepthMask = bit32.bor(window.DC.TreeHasStackDataDepthMask, bit32.lshift(1, window.DC.TreeDepth))
if bit32.band(flags, 1048576 --[[ImGuiTreeNodeFlags.DrawLinesToNodes]]) ~= 0 then
window.DC.TreeRecordsClippedNodesY2Mask = bit32.bor(window.DC.TreeRecordsClippedNodesY2Mask, bit32.lshift(1, window.DC.TreeDepth))
end
end
--- @param id ImGuiID
--- @param flags ImGuiTreeNodeFlags
--- @param label string
--- @param label_end? int
@native
function ImGui.TreeNodeBehavior(id, flags, label, label_end)
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
local g = GImGui
local style = g.Style
-- When not framed, we vertically increase height up to typical framed widget height
local display_frame = bit32.band(flags, 2 --[[ImGuiTreeNodeFlags.Framed]]) ~= 0
local use_frame_padding = display_frame or (bit32.band(flags, 1024 --[[ImGuiTreeNodeFlags.FramePadding]]) ~= 0)
-- [Luau] scalar padding, no temporary ImVec2
local padding_x, padding_y
if use_frame_padding then
padding_x, padding_y = style.FramePadding.x, style.FramePadding.y
else
padding_x, padding_y = style.FramePadding.x, math.min(window.DC.CurrLineTextBaseOffset, style.FramePadding.y)
end
if label_end == nil then
label_end = ImGui.FindRenderedTextEnd(label)
end
local label_size = ImGui.CalcTextSize(label, label_end, false)
local text_offset_x = g.FontSize + (display_frame and (padding_x * 3) or (padding_x * 2)) -- Collapsing arrow width + Spacing
local text_offset_y = use_frame_padding and ImMax(style.FramePadding.y, window.DC.CurrLineTextBaseOffset) or window.DC.CurrLineTextBaseOffset -- Latch before ItemSize changes it
local text_width = g.FontSize + label_size.x + padding_x * 2 -- Include collapsing arrow
local frame_height = label_size.y + padding_y * 2
local span_all_columns = bit32.band(flags, 16384 --[[ImGuiTreeNodeFlags.SpanAllColumns]]) ~= 0 and (g.CurrentTable ~= nil)
local span_all_columns_label = bit32.band(flags, 32768 --[[ImGuiTreeNodeFlags.LabelSpanAllColumns]]) ~= 0 and (g.CurrentTable ~= nil)
local frame_bb = ImRect()
frame_bb.Min.x = span_all_columns and window.ParentWorkRect.Min.x or (bit32.band(flags, 4096 --[[ImGuiTreeNodeFlags.SpanFullWidth]]) ~= 0 and window.WorkRect.Min.x or window.DC.CursorPos.x)
frame_bb.Min.y = window.DC.CursorPos.y + (text_offset_y - padding_y)
frame_bb.Max.x = span_all_columns and window.ParentWorkRect.Max.x or (bit32.band(flags, 8192 --[[ImGuiTreeNodeFlags.SpanLabelWidth]]) ~= 0 and window.DC.CursorPos.x + text_width + padding_x or window.WorkRect.Max.x)
frame_bb.Max.y = window.DC.CursorPos.y + (text_offset_y - padding_y) + frame_height
if display_frame then
local outer_extend = IM_TRUNC(window.WindowPadding.x * 0.5) -- Framed header expand a little outside of current limits
frame_bb.Min.x = frame_bb.Min.x - outer_extend
frame_bb.Max.x = frame_bb.Max.x + outer_extend
end
local text_pos = ImVec2(window.DC.CursorPos.x + text_offset_x, window.DC.CursorPos.y + text_offset_y)
local item_size = LayoutHelpers_size; item_size.x = text_width; item_size.y = frame_height -- [Luau] scratch, ItemSize() only reads it
ImGui.ItemSize(item_size, padding_y)
-- For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing
local interact_bb = ImRect()
ImRect_Copy(interact_bb, frame_bb)
if bit32.band(flags, bit32.bor(2 --[[ImGuiTreeNodeFlags.Framed]], 2048 --[[ImGuiTreeNodeFlags.SpanAvailWidth]], 4096 --[[ImGuiTreeNodeFlags.SpanFullWidth]], 8192 --[[ImGuiTreeNodeFlags.SpanLabelWidth]], 16384 --[[ImGuiTreeNodeFlags.SpanAllColumns]])) == 0 then
interact_bb.Max.x = frame_bb.Min.x + text_width + (label_size.x > 0.0 and (style.ItemSpacing.x * 2.0) or 0.0)
end
-- Compute open and multi-select states before ItemAdd() as it clear NextItem data.
local storage_id = (bit32.band(g.NextItemData.HasFlags, 16 --[[ImGuiNextItemDataFlags.HasStorageID]]) ~= 0) and g.NextItemData.StorageId or id
local is_open = ImGui.TreeNodeUpdateNextOpen(storage_id, flags)
local is_visible
if span_all_columns or span_all_columns_label then
-- Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable..
local backup_clip_rect_min_x = window.ClipRect.Min.x
local backup_clip_rect_max_x = window.ClipRect.Max.x
window.ClipRect.Min.x = window.ParentWorkRect.Min.x
window.ClipRect.Max.x = window.ParentWorkRect.Max.x
is_visible = ImGui.ItemAdd(interact_bb, id)
window.ClipRect.Min.x = backup_clip_rect_min_x
window.ClipRect.Max.x = backup_clip_rect_max_x
else
is_visible = ImGui.ItemAdd(interact_bb, id)
end
g.LastItemData.StatusFlags = bit32.bor(g.LastItemData.StatusFlags, 2 --[[ImGuiItemStatusFlags.HasDisplayRect]])
ImRect_Copy(g.LastItemData.DisplayRect, frame_bb)
-- If a NavLeft request is happening and ImGuiTreeNodeFlags.NavLeftJumpsToParent enabled:
-- Store data for the current depth to allow returning to this node from any child item.
-- For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop().
-- It will become tempting to enable ImGuiTreeNodeFlags.NavLeftJumpsToParent by default or move it to ImGuiStyle.
local store_tree_node_stack_data = false
if bit32.band(flags, 1835008 --[[ImGuiTreeNodeFlags.DrawLinesMask_]]) == 0 then
flags = bit32.bor(flags, g.Style.TreeLinesFlags)
end
local draw_tree_lines = (bit32.band(flags, bit32.bor(524288 --[[ImGuiTreeNodeFlags.DrawLinesFull]], 1048576 --[[ImGuiTreeNodeFlags.DrawLinesToNodes]])) ~= 0) and (frame_bb.Min.y < window.ClipRect.Max.y) and (g.Style.TreeLinesSize > 0.0)
if bit32.band(flags, 8 --[[ImGuiTreeNodeFlags.NoTreePushOnOpen]]) == 0 then
store_tree_node_stack_data = draw_tree_lines
if bit32.band(flags, 131072 --[[ImGuiTreeNodeFlags.NavLeftJumpsToParent]]) ~= 0 and not g.NavIdIsAlive then
if g.NavMoveDir == 0 --[[ImGuiDir.Left]] and g.NavWindow == window and ImGui.NavMoveRequestButNoResultYet() then
store_tree_node_stack_data = true
end
end
end
local is_leaf = bit32.band(flags, 256 --[[ImGuiTreeNodeFlags.Leaf]]) ~= 0
if not is_visible then
if bit32.band(flags, 1048576 --[[ImGuiTreeNodeFlags.DrawLinesToNodes]]) ~= 0 and bit32.band(window.DC.TreeRecordsClippedNodesY2Mask, bit32.lshift(1, (window.DC.TreeDepth - 1))) ~= 0 then
local parent_data = g.TreeNodeStack.Data[g.TreeNodeStack.Size]
parent_data.DrawLinesToNodesY2 = ImMax(parent_data.DrawLinesToNodesY2, window.DC.CursorPos.y) -- Don't need to aim to mid Y position as we are clipped anyway.
if frame_bb.Min.y >= window.ClipRect.Max.y then
window.DC.TreeRecordsClippedNodesY2Mask = bit32.band(window.DC.TreeRecordsClippedNodesY2Mask, bit32.bnot(bit32.lshift(1, (window.DC.TreeDepth - 1)))) -- Done
end
end
if is_open and store_tree_node_stack_data then
TreeNodeStoreStackData(flags, text_pos.x - text_offset_x) -- Call before TreePushOverrideID()
end
if is_open and bit32.band(flags, 8 --[[ImGuiTreeNodeFlags.NoTreePushOnOpen]]) == 0 then
ImGui.TreePushOverrideID(id)
end
-- IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0))
return is_open
end
if span_all_columns or span_all_columns_label then
ImGui.TablePushBackgroundChannel()
g.LastItemData.StatusFlags = bit32.bor(g.LastItemData.StatusFlags, 512 --[[ImGuiItemStatusFlags.HasClipRect]])
ImRect_Copy(g.LastItemData.ClipRect, window.ClipRect)
end
local button_flags = 0 --[[ImGuiTreeNodeFlags.None]]
if bit32.band(flags, 4 --[[ImGuiTreeNodeFlags.AllowOverlap]]) ~= 0 or bit32.band(g.LastItemData.ItemFlags, 16384 --[[ImGuiItemFlags.AllowOverlap]]) ~= 0 then
button_flags = bit32.bor(button_flags, 4096 --[[ImGuiButtonFlags.AllowOverlap]])
end
if not is_leaf then
button_flags = bit32.bor(button_flags, 512 --[[ImGuiButtonFlags.PressedOnDragDropHold]])
end
-- We allow clicking on the arrow section with keyboard modifiers held, in order to easily
-- allow browsing a tree while preserving selection with code implementing multi-selection patterns.
-- When clicking on the rest of the tree node we always disallow keyboard modifiers.
local arrow_hit_x1 = (text_pos.x - text_offset_x) - style.TouchExtraPadding.x
local arrow_hit_x2 = (text_pos.x - text_offset_x) + (g.FontSize + padding_x * 2.0) + style.TouchExtraPadding.x
local is_mouse_x_over_arrow = (g.IO.MousePos.x >= arrow_hit_x1 and g.IO.MousePos.x < arrow_hit_x2)
local is_multi_select = bit32.band(g.LastItemData.ItemFlags, 4194304 --[[ImGuiItemFlags.IsMultiSelect]]) ~= 0
if is_multi_select then -- We absolutely need to distinguish open vs select so _OpenOnArrow comes by default
flags = bit32.bor(flags, (bit32.band(flags, 192 --[[ImGuiTreeNodeFlags.OpenOnMask_]]) == 0) and bit32.bor(128 --[[ImGuiTreeNodeFlags.OpenOnArrow]], 64 --[[ImGuiTreeNodeFlags.OpenOnDoubleClick]]) or 128 --[[ImGuiTreeNodeFlags.OpenOnArrow]])
end
-- Open behaviors can be altered with the _OpenOnArrow and _OnOnDoubleClick flags.
-- Some alteration have subtle effects (e.g. toggle on MouseUp vs MouseDown events) due to requirements for multi-selection and drag and drop support.
-- - Single-click on label = Toggle on MouseUp (default, when _OpenOnArrow=0)
-- - Single-click on arrow = Toggle on MouseDown (when _OpenOnArrow=0)
-- - Single-click on arrow = Toggle on MouseDown (when _OpenOnArrow=1)
-- - Double-click on label = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1)
-- - Double-click on arrow = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1 and _OpenOnArrow=0)
-- It is rather standard that arrow click react on Down rather than Up.
-- We set ImGuiButtonFlags.PressedOnClickRelease on OpenOnDoubleClick because we want the item to be active on the initial MouseDown in order for drag and drop to work.
if is_mouse_x_over_arrow then
button_flags = bit32.bor(button_flags, 16 --[[ImGuiButtonFlags.PressedOnClick]])
elseif bit32.band(flags, 64 --[[ImGuiTreeNodeFlags.OpenOnDoubleClick]]) ~= 0 then
button_flags = bit32.bor(button_flags, 32 --[[ImGuiButtonFlags.PressedOnClickRelease]], 256 --[[ImGuiButtonFlags.PressedOnDoubleClick]])
else
button_flags = bit32.bor(button_flags, 32 --[[ImGuiButtonFlags.PressedOnClickRelease]])
end
if bit32.band(flags, 134217728 --[[ImGuiTreeNodeFlags.NoNavFocus]]) ~= 0 then
button_flags = bit32.bor(button_flags, 262144 --[[ImGuiButtonFlags.NoNavFocus]])
end
local selected = bit32.band(flags, 1 --[[ImGuiTreeNodeFlags.Selected]]) ~= 0
local was_selected = selected
-- Multi-selection support (header)
if is_multi_select then
-- Handle multi-select + alter button flags for it
selected, button_flags = ImGui.MultiSelectItemHeader(id, selected, button_flags)
if is_mouse_x_over_arrow then
button_flags = bit32.band(bit32.bor(button_flags, 16 --[[ImGuiButtonFlags.PressedOnClick]]), bit32.bnot(32 --[[ImGuiButtonFlags.PressedOnClickRelease]]))
end
else
if window ~= g.HoveredWindow or not is_mouse_x_over_arrow then
button_flags = bit32.bor(button_flags, 65536 --[[ImGuiButtonFlags.NoKeyModsAllowed]])
end
end
local pressed, hovered, held = ImGui.ButtonBehavior(interact_bb, id, button_flags)
local toggled = false
if not is_leaf then
if pressed and g.DragDropHoldJustPressedId ~= id then
if bit32.band(flags, 192 --[[ImGuiTreeNodeFlags.OpenOnMask_]]) == 0 or (g.NavActivateId == id and not is_multi_select) then
toggled = true -- Single click
end
if bit32.band(flags, 128 --[[ImGuiTreeNodeFlags.OpenOnArrow]]) ~= 0 then
toggled = toggled or (is_mouse_x_over_arrow and not g.NavHighlightItemUnderNav) -- Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job
end
if bit32.band(flags, 64 --[[ImGuiTreeNodeFlags.OpenOnDoubleClick]]) ~= 0 and g.IO.MouseClickedCount[0] == 2 then
toggled = true -- Double click
end
elseif pressed and g.DragDropHoldJustPressedId == id then
assert(bit32.band(button_flags, 512 --[[ImGuiButtonFlags.PressedOnDragDropHold]]) ~= 0)
if not is_open then -- When using Drag and Drop "hold to open" we keep the node highlighted after opening, but never close it again.
toggled = true
else
pressed = false -- Cancel press so it doesn't trigger selection.
end
end
if g.NavId == id and g.NavMoveDir == 0 --[[ImGuiDir.Left]] and is_open then
toggled = true
ImGui.NavClearPreferredPosForAxis(1 --[[ImGuiAxis.X]])
ImGui.NavMoveRequestCancel()
end
if g.NavId == id and g.NavMoveDir == 1 --[[ImGuiDir.Right]] and not is_open then -- If there's something upcoming on the line we may want to give it the priority?
toggled = true
ImGui.NavClearPreferredPosForAxis(1 --[[ImGuiAxis.X]])
ImGui.NavMoveRequestCancel()
end
if toggled then
is_open = not is_open
window.DC.StateStorage[storage_id] = is_open
g.LastItemData.StatusFlags = bit32.bor(g.LastItemData.StatusFlags, 16 --[[ImGuiItemStatusFlags.ToggledOpen]])
end
end
-- Multi-selection support (footer)
if is_multi_select then
local pressed_copy = pressed and not toggled
selected, pressed_copy = ImGui.MultiSelectItemFooter(id, selected, pressed_copy)
if pressed then
ImGui.SetNavID(id, window.DC.NavLayerCurrent, g.CurrentFocusScopeId, interact_bb)
end
end
if selected ~= was_selected then
g.LastItemData.StatusFlags = bit32.bor(g.LastItemData.StatusFlags, 8 --[[ImGuiItemStatusFlags.ToggledSelection]])
end
-- Render
do
local text_col = ImGui.GetColorU32(0 --[[ImGuiCol.Text]])
local nav_render_cursor_flags = 2 --[[ImGuiNavRenderCursorFlags.Compact]]
if is_multi_select then
nav_render_cursor_flags = bit32.bor(nav_render_cursor_flags, 4 --[[ImGuiNavRenderCursorFlags.AlwaysDraw]]) -- Always show the nav rectangle
end
if display_frame then
-- Framed type
local bg_col = ImGui.GetColorU32((held and hovered) and 27 --[[ImGuiCol.HeaderActive]] or (hovered and 26 --[[ImGuiCol.HeaderHovered]] or 25 --[[ImGuiCol.Header]]))
ImGui.RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding)
ImGui.RenderNavCursor(frame_bb, id, nav_render_cursor_flags)
if span_all_columns and not span_all_columns_label then
ImGui.TablePopBackgroundChannel()
end
if bit32.band(flags, 512 --[[ImGuiTreeNodeFlags.Bullet]]) ~= 0 then
ImGui.RenderBullet(window.DrawList, ImVec2(text_pos.x - text_offset_x * 0.60, text_pos.y + g.FontSize * 0.5), text_col)
elseif not is_leaf then
ImGui.RenderArrow(window.DrawList, ImVec2(text_pos.x - text_offset_x + padding_x, text_pos.y), text_col, is_open and ((bit32.band(flags, 536870912 --[[ImGuiTreeNodeFlags.UpsideDownArrow]]) ~= 0) and 2 --[[ImGuiDir.Up]] or 3 --[[ImGuiDir.Down]]) or 1 --[[ImGuiDir.Right]], 1.0)
else -- Leaf without bullet, left-adjusted text
text_pos.x = text_pos.x - (text_offset_x - padding_x)
end
if bit32.band(flags, 268435456 --[[ImGuiTreeNodeFlags.ClipLabelForTrailingButton]]) ~= 0 then
frame_bb.Max.x = frame_bb.Max.x - (g.FontSize + style.FramePadding.x)
end
-- if g.LogEnabled then
-- ImGui.LogSetNextTextDecoration("###", "###")
-- end
else
-- Unframed typed for tree nodes
if hovered or selected then
local bg_col = ImGui.GetColorU32((held and hovered) and 27 --[[ImGuiCol.HeaderActive]] or (hovered and 26 --[[ImGuiCol.HeaderHovered]] or 25 --[[ImGuiCol.Header]]))
ImGui.RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false, 0.0)
end
ImGui.RenderNavCursor(frame_bb, id, nav_render_cursor_flags, 0.0)
if span_all_columns and not span_all_columns_label then
ImGui.TablePopBackgroundChannel()
end
if bit32.band(flags, 512 --[[ImGuiTreeNodeFlags.Bullet]]) ~= 0 then
ImGui.RenderBullet(window.DrawList, ImVec2(text_pos.x - text_offset_x * 0.5, text_pos.y + g.FontSize * 0.5), text_col)
elseif not is_leaf then
ImGui.RenderArrow(window.DrawList, ImVec2(text_pos.x - text_offset_x + padding_x, text_pos.y + g.FontSize * 0.15), text_col, is_open and ((bit32.band(flags, 536870912 --[[ImGuiTreeNodeFlags.UpsideDownArrow]]) ~= 0) and 2 --[[ImGuiDir.Up]] or 3 --[[ImGuiDir.Down]]) or 1 --[[ImGuiDir.Right]], 0.70)
end
-- if g.LogEnabled then
-- ImGui.LogSetNextTextDecoration(">", nil)
-- end
end
if draw_tree_lines then
ImGui.TreeNodeDrawLineToChildNode(ImVec2(text_pos.x - text_offset_x + padding_x, text_pos.y + g.FontSize * 0.5))
end
-- Label
if display_frame then
ImGui.RenderTextClipped(text_pos, frame_bb.Max, label, label_end, label_size)
else
ImGui.RenderText(text_pos, label, 1, label_end, false)
end
if span_all_columns_label then
ImGui.TablePopBackgroundChannel()
end
end
if is_open and store_tree_node_stack_data then
TreeNodeStoreStackData(flags, text_pos.x - text_offset_x) -- Call before TreePushOverrideID()
end
if is_open and bit32.band(flags, 8 --[[ImGuiTreeNodeFlags.NoTreePushOnOpen]]) == 0 then
ImGui.TreePushOverrideID(id) -- Could use TreePush(label) but this avoid computing twice
end
-- IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));
return is_open
end
-- Draw horizontal line from our parent node
-- This is only called for visible child nodes so we are not too fussy anymore about performances
--- @param target_pos ImVec2
function ImGui.TreeNodeDrawLineToChildNode(target_pos)
local g = GImGui
local window = g.CurrentWindow
if window.DC.TreeDepth == 0 or bit32.band(window.DC.TreeHasStackDataDepthMask, bit32.lshift(1, window.DC.TreeDepth - 1)) == 0 then
return
end
-- FIXME: Could this be simplified using new stroke flags?
local parent_data = g.TreeNodeStack.Data[g.TreeNodeStack.Size]
local half_size = g.Style.TreeLinesSize * 0.5
local x1 = ImTrunc(parent_data.DrawLinesX1 - half_size) + half_size -- Draw line centered at X1, but snap to pixels boundary.
local x2 = ImTrunc(target_pos.x - g.Style.ItemInnerSpacing.x)
local y = ImTrunc(target_pos.y - half_size) + half_size
local rounding = (g.Style.TreeLinesRounding > 0.0) and ImMin(x2 - x1, g.Style.TreeLinesRounding) or 0.0
parent_data.DrawLinesToNodesY2 = ImMax(parent_data.DrawLinesToNodesY2, y - rounding)
if x1 >= x2 then
return
end
if rounding > 0.0 and rounding > half_size then
x1 = x1 + rounding
window.DrawList:PathArcToFast(ImVec2(x1, y - rounding), rounding, 6, 3)
if x1 < x2 then
window.DrawList:PathLineTo(ImVec2(x2, y))
end
window.DrawList:PathStroke(ImGui.GetColorU32(55 --[[ImGuiCol.TreeLines]]), g.Style.TreeLinesSize)
else
-- We use AddLine() instead of AddLineH() as we provide coordinates for line center,
-- in order to make the code use same coordinates for both rounded and non-rounded version. (FIXME-OPT)
x1 = x1 + half_size -- Avoid overdraw
window.DrawList:AddLine(ImVec2(x1, y), ImVec2(x2, y), ImGui.GetColorU32(55 --[[ImGuiCol.TreeLines]]), g.Style.TreeLinesSize)
end
end
-- Draw vertical line of the hierarchy
--- @param data ImGuiTreeNodeStackData
function ImGui.TreeNodeDrawLineToTreePop(data)
local g = GImGui
local window = g.CurrentWindow
local y1 = ImMax(data.NavRect.Max.y, window.ClipRect.Min.y)
local y2 = data.DrawLinesToNodesY2
if bit32.band(data.TreeFlags, 524288 --[[ImGuiTreeNodeFlags.DrawLinesFull]]) ~= 0 then
local y2_full = window.DC.CursorPos.y
if g.CurrentTable then
y2_full = ImMax(g.CurrentTable.RowPosY2, y2_full)
end
y2_full = ImTrunc(y2_full - g.Style.ItemSpacing.y - g.FontSize * 0.5)
if y2 + (g.Style.ItemSpacing.y + g.Style.TreeLinesRounding) < y2_full then -- FIXME: threshold to use ToNodes Y2 instead of Full Y2 when close by ItemSpacing.y
y2 = y2_full
end
end
y2 = ImMin(y2, window.ClipRect.Max.y)
if y2 <= y1 then
return
end
local x = ImTrunc(data.DrawLinesX1 - g.Style.TreeLinesSize * 0.5) + g.Style.TreeLinesSize * 0.5 -- Draw line centered at X1, but snap to pixels boundary.
if data.DrawLinesTableColumn ~= -1 then
ImGui.TablePushColumnChannel(data.DrawLinesTableColumn)
end
-- We use AddLine() instead of AddLineV() as we provide coordinates for line center (FIXME-OPT)
window.DrawList:AddLine(ImVec2(x, y1), ImVec2(x, y2), ImGui.GetColorU32(55 --[[ImGuiCol.TreeLines]]), g.Style.TreeLinesSize)
if data.DrawLinesTableColumn ~= -1 then
ImGui.TablePopColumnChannel()
end
end
--- @param str_id string|ImGuiID
function ImGui.TreePush(str_id)
local window = ImGui.GetCurrentWindow()
ImGui.Indent()
window.DC.TreeDepth = window.DC.TreeDepth + 1
ImGui.PushID(str_id)
end
--- @param id ImGuiID
function ImGui.TreePushOverrideID(id)
local g = GImGui
local window = g.CurrentWindow
ImGui.Indent()
window.DC.TreeDepth = window.DC.TreeDepth + 1
ImGui.PushOverrideID(id)
end
function ImGui.TreePop()
local g = GImGui
local window = g.CurrentWindow
ImGui.Unindent()
window.DC.TreeDepth = window.DC.TreeDepth - 1
local tree_depth_mask = bit32.lshift(1, window.DC.TreeDepth)
if bit32.band(window.DC.TreeHasStackDataDepthMask, tree_depth_mask) ~= 0 then
local data = g.TreeNodeStack.Data[g.TreeNodeStack.Size]
assert(data.ID == window.IDStack:back())
-- Handle Left arrow to move to parent tree node (when ImGuiTreeNodeFlags.NavLeftJumpsToParent is enabled)
if bit32.band(data.TreeFlags, 131072 --[[ImGuiTreeNodeFlags.NavLeftJumpsToParent]]) ~= 0 then
if g.NavIdIsAlive and g.NavMoveDir == 0 --[[ImGuiDir.Left]] and g.NavWindow == window and ImGui.NavMoveRequestButNoResultYet() then
ImGui.NavMoveRequestResolveWithPastTreeNode(g.NavMoveResultLocal, data)
end
end
-- Draw hierarchy lines
if data.DrawLinesX1 ~= 3.4028234663853e+38 --[[FLT_MAX]] and window.DC.CursorPos.y >= window.ClipRect.Min.y then
ImGui.TreeNodeDrawLineToTreePop(data)
end
g.TreeNodeStack:pop_back()
window.DC.TreeHasStackDataDepthMask = bit32.band(window.DC.TreeHasStackDataDepthMask, bit32.bnot(tree_depth_mask))
window.DC.TreeRecordsClippedNodesY2Mask = bit32.band(window.DC.TreeRecordsClippedNodesY2Mask, bit32.bnot(tree_depth_mask))
end
assert(window.IDStack.Size > 1) -- There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much.
ImGui.PopID()
end
-- Set next TreeNode/CollapsingHeader open state.
--- @param is_open bool
--- @param cond? ImGuiCond
function ImGui.SetNextItemOpen(is_open, cond)
if cond == nil then cond = 0 end
local g = GImGui
if g.CurrentWindow.SkipItems then
return
end
g.NextItemData.HasFlags = bit32.bor(g.NextItemData.HasFlags, 2 --[[ImGuiNextItemDataFlags.HasOpen]])
g.NextItemData.OpenVal = is_open
g.NextItemData.OpenCond = (cond ~= 0) and cond or 1 --[[ImGuiCond.Always]]
end
-- Horizontal distance preceding label when using TreeNode() or Bullet()
function ImGui.GetTreeNodeToLabelSpacing()
local g = GImGui
return g.FontSize + (g.Style.FramePadding.x * 2.0)
end
-- Set next TreeNode/CollapsingHeader storage id.
--- @param storage_id ImGuiID
function ImGui.SetNextItemStorageID(storage_id)
local g = GImGui
if g.CurrentWindow.SkipItems then
return
end
g.NextItemData.HasFlags = bit32.bor(g.NextItemData.HasFlags, 16 --[[ImGuiNextItemDataFlags.HasStorageID]])
g.NextItemData.StorageId = storage_id
end
-- CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags.NoTreePushOnOpen flag).
-- This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags.CollapsingHeader). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode().
-- Overloads: CollapsingHeader(label, flags?) -> is_open
-- CollapsingHeader(label, p_visible: bool, flags?) -> is_open, p_visible
-- p_visible == nil : regular collapsing header
-- p_visible == true : show a small close button on the corner of the header, clicking the button will set p_visible = false (returned as 2nd value)
-- p_visible == false : do not show the header at all
-- Do not mistake this with the Open state of the header itself, which you can adjust with SetNextItemOpen() or ImGuiTreeNodeFlags.DefaultOpen.
--- @param label string
@native
function ImGui.CollapsingHeader(label, a, b)
local p_visible, flags
if type(a) == "boolean" then p_visible, flags = a, b else flags = a end
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false, p_visible
end
if p_visible == false then
return false, p_visible
end
local id = window:GetID(label)
flags = bit32.bor(flags, 26 --[[ImGuiTreeNodeFlags.CollapsingHeader]])
if p_visible ~= nil then
flags = bit32.bor(flags, 4 --[[ImGuiTreeNodeFlags.AllowOverlap]], 268435456 --[[ImGuiTreeNodeFlags.ClipLabelForTrailingButton]])
end
local is_open = ImGui.TreeNodeBehavior(id, flags, label)
if p_visible ~= nil then
-- Create a small overlapping close button
-- FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc.
-- FIXME: CloseButton can overlap into text, need find a way to clip the text somehow.
local g = GImGui
local last_item_backup = ImGuiLastItemData()
ImGuiLastItemData_Copy(last_item_backup, g.LastItemData)
local button_size = g.FontSize
local button_x = ImMax(g.LastItemData.Rect.Min.x, g.LastItemData.Rect.Max.x - g.Style.FramePadding.x - button_size)
local button_y = g.LastItemData.Rect.Min.y + g.Style.FramePadding.y
local close_button_id = ImGui.GetIDWithSeed("#CLOSE", nil, id)
if ImGui.CloseButton(close_button_id, ImVec2(button_x, button_y)) then
p_visible = false
end
ImGuiLastItemData_Copy(g.LastItemData, last_item_backup)
end
return is_open, p_visible
end
-------------------------------------------------------------------------------
-- [SECTION] Widgets: Selectable
-------------------------------------------------------------------------------
-- - Selectable()
-------------------------------------------------------------------------------
-- Tip: pass a non-visible label (e.g. "##hello") then you can use the space to draw other text or image.
-- But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID or use ##unique_id.
-- With this scheme, ImGuiSelectableFlags.SpanAllColumns and ImGuiSelectableFlags.AllowOverlap are also frequently used flags.
-- FIXME: Selectable() with (size.x == 0.0f) and (SelectableTextAlign.x > 0.0f) followed by SameLine() is currently not supported.
--- @param label string
--- @param selected? bool
--- @param flags? ImGuiSelectableFlags
--- @param size_arg? any
--- @return bool is_pressed
--- @return bool is_selected # Updated `selected`
@native
function ImGui.Selectable(label, selected, flags, size_arg)
if selected == nil then selected = false end
if flags == nil then flags = 0 end
if size_arg == nil then size_arg = LAYOUT_ZERO_SIZE end -- [Luau] shared read-only default (only read below)
local window = ImGui.GetCurrentWindow()
if (window.SkipItems) then
return false, selected
end
local g = GImGui
local style = g.Style
-- Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle.
local id = window:GetID(label)
local label_end = ImGui.FindRenderedTextEnd(label)
local label_size = ImGui.CalcTextSize(label, label_end, false)
local size = ImVec2((size_arg.x ~= 0.0) and size_arg.x or label_size.x, (size_arg.y ~= 0.0) and size_arg.y or label_size.y)
local pos = ImVec2()
ImVec2_Copy(pos, window.DC.CursorPos)
pos.y = pos.y + window.DC.CurrLineTextBaseOffset
ImGui.ItemSize(size, 0.0)
-- Fill horizontal space
-- We don't support (size < 0.0) in Selectable() because the ItemSpacing extension would make explicitly right-aligned sizes not visibly match other widgets.
local span_all_columns = bit32.band(flags, 2 --[[ImGuiSelectableFlags.SpanAllColumns]]) ~= 0
local min_x = span_all_columns and window.ParentWorkRect.Min.x or pos.x
local max_x = span_all_columns and window.ParentWorkRect.Max.x or window.WorkRect.Max.x
if size_arg.x == 0.0 or bit32.band(flags, 16777216 --[[ImGuiSelectableFlags.SpanAvailWidth]]) ~= 0 then
size.x = ImMax(label_size.x, max_x - min_x)
end
-- Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable.
-- FIXME: Not part of layout so not included in clipper calculation, but ItemSize currently doesn't allow offsetting CursorPos.
local bb = ImRect(min_x, pos.y, min_x + size.x, pos.y + size.y)
if bit32.band(flags, 67108864 --[[ImGuiSelectableFlags.NoPadWithHalfSpacing]]) == 0 then
local spacing_x = span_all_columns and 0.0 or style.ItemSpacing.x
local spacing_y = style.ItemSpacing.y
local spacing_L = IM_TRUNC(spacing_x * 0.50)
local spacing_U = IM_TRUNC(spacing_y * 0.50)
bb.Min.x = bb.Min.x - spacing_L
bb.Min.y = bb.Min.y - spacing_U
bb.Max.x = bb.Max.x + (spacing_x - spacing_L)
bb.Max.y = bb.Max.y + (spacing_y - spacing_U)
end
-- if g.IO.KeyCtrl then ImGui.GetForegroundDrawList():AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)) end
local disabled_item = bit32.band(flags, 8 --[[ImGuiSelectableFlags.Disabled]]) ~= 0
local extra_item_flags = disabled_item and 64 --[[ImGuiItemFlags.Disabled]] or 0 --[[ImGuiItemFlags.None]]
local is_visible
if span_all_columns then
-- Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable..
local backup_clip_rect_min_x = window.ClipRect.Min.x
local backup_clip_rect_max_x = window.ClipRect.Max.x
window.ClipRect.Min.x = window.ParentWorkRect.Min.x
window.ClipRect.Max.x = window.ParentWorkRect.Max.x
is_visible = ImGui.ItemAdd(bb, id, nil, extra_item_flags)
window.ClipRect.Min.x = backup_clip_rect_min_x
window.ClipRect.Max.x = backup_clip_rect_max_x
else
is_visible = ImGui.ItemAdd(bb, id, nil, extra_item_flags)
end
local is_multi_select = bit32.band(g.LastItemData.ItemFlags, 4194304 --[[ImGuiItemFlags.IsMultiSelect]]) ~= 0
if not is_visible then
if not is_multi_select or not g.BoxSelectState.UnclipMode or not g.BoxSelectState.UnclipRect:Overlaps(bb) then -- Extra layer of "no logic clip" for box-select support (would be more overhead to add to ItemAdd)
return false, selected
end
end
local disabled_global = bit32.band(g.CurrentItemFlags, 64 --[[ImGuiItemFlags.Disabled]]) ~= 0
if disabled_item and not disabled_global then -- Only testing this as an optimization
ImGui.BeginDisabled()
end
-- FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only,
-- which would be advantageous since most selectable are not selected.
if span_all_columns then
if g.CurrentTable then
ImGui.TablePushBackgroundChannel()
elseif window.DC.CurrentColumns then
ImGui.PushColumnsBackground()
end
g.LastItemData.StatusFlags = bit32.bor(g.LastItemData.StatusFlags, 512 --[[ImGuiItemStatusFlags.HasClipRect]])
ImRect_Copy(g.LastItemData.ClipRect, window.ClipRect)
end
-- We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries
local button_flags = 0
if bit32.band(flags, 1048576 --[[ImGuiSelectableFlags.NoHoldingActiveID]]) ~= 0 then button_flags = bit32.bor(button_flags, 131072 --[[ImGuiButtonFlags.NoHoldingActiveId]]) end
if bit32.band(flags, 134217728 --[[ImGuiSelectableFlags.NoSetKeyOwner]]) ~= 0 then button_flags = bit32.bor(button_flags, 1048576 --[[ImGuiButtonFlags.NoSetKeyOwner]]) end
if bit32.band(flags, 4194304 --[[ImGuiSelectableFlags.SelectOnClick]]) ~= 0 then button_flags = bit32.bor(button_flags, 16 --[[ImGuiButtonFlags.PressedOnClick]]) end
if bit32.band(flags, 8388608 --[[ImGuiSelectableFlags.SelectOnRelease]]) ~= 0 then button_flags = bit32.bor(button_flags, 128 --[[ImGuiButtonFlags.PressedOnRelease]]) end
if bit32.band(flags, 4 --[[ImGuiSelectableFlags.AllowDoubleClick]]) ~= 0 then button_flags = bit32.bor(button_flags, 32 --[[ImGuiButtonFlags.PressedOnClickRelease]], 256 --[[ImGuiButtonFlags.PressedOnDoubleClick]]) end
if bit32.band(flags, 16 --[[ImGuiSelectableFlags.AllowOverlap]]) ~= 0 or bit32.band(g.LastItemData.ItemFlags, 16384 --[[ImGuiItemFlags.AllowOverlap]]) ~= 0 then button_flags = bit32.bor(button_flags, 4096 --[[ImGuiButtonFlags.AllowOverlap]]) end
-- Multi-selection support (header)
local was_selected = selected
if is_multi_select then
-- Handle multi-select + alter button flags for it
selected, button_flags = ImGui.MultiSelectItemHeader(id, selected, button_flags)
end
local pressed, hovered, held = ImGui.ButtonBehavior(bb, id, button_flags)
local auto_selected = false
-- Multi-selection support (footer)
if is_multi_select then
selected, pressed = ImGui.MultiSelectItemFooter(id, selected, pressed)
else
-- Auto-select when moved into
-- - This will be more fully fleshed in the range-select branch
-- - This is not exposed as it won't nicely work with some user side handling of shift/control
-- - We cannot do 'if (g.NavJustMovedToId != id) { selected = false; pressed = was_selected; }' for two reasons
-- - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope())
-- - (2) usage will fail with clipped items
-- The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API.
if bit32.band(flags, 64 --[[ImGuiSelectableFlags.SelectOnNav]]) ~= 0 and g.NavJustMovedToId ~= 0 and g.NavJustMovedToFocusScopeId == g.CurrentFocusScopeId then
if g.NavJustMovedToId == id and bit32.band(g.NavJustMovedToKeyMods, 4096 --[[ImGuiMod_Ctrl]]) == 0 then
selected = true
pressed = true
auto_selected = true
end
end
end
-- Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with keyboard/gamepad
if pressed or (hovered and bit32.band(flags, 33554432 --[[ImGuiSelectableFlags.SetNavIdOnHover]]) ~= 0) then
if not g.NavHighlightItemUnderNav and g.NavWindow == window and g.NavLayer == window.DC.NavLayerCurrent then
ImGui.SetNavID(id, window.DC.NavLayerCurrent, g.CurrentFocusScopeId, ImGui.WindowRectAbsToRel(window, bb)) -- (bb == NavRect)
if g.IO.ConfigNavCursorVisibleAuto then
g.NavCursorVisible = false
end
end
end
if pressed then
ImGui.MarkItemEdited(id)
end
if selected ~= was_selected then
g.LastItemData.StatusFlags = bit32.bor(g.LastItemData.StatusFlags, 8 --[[ImGuiItemStatusFlags.ToggledSelection]])
end
if g.ActiveId == id and g.ActiveIdIsJustActivated then
g.ActiveIdWasSelected = was_selected
g.ActiveIdWasSoleSelected = was_selected and (not is_multi_select or g.CurrentMultiSelect.IsSoleOrUnknownSelectionSize)
end
-- Render
if is_visible then
local highlighted = hovered or (bit32.band(flags, 32 --[[ImGuiSelectableFlags.Highlight]]) ~= 0)
if highlighted or selected then
-- Between 1.91.0 and 1.91.4 we made selected Selectable use an arbitrary lerp between _Header and _HeaderHovered. Removed that now. (#8106)
local col
if held and highlighted then
col = ImGui.GetColorU32(27 --[[ImGuiCol.HeaderActive]])
elseif highlighted then
col = ImGui.GetColorU32(26 --[[ImGuiCol.HeaderHovered]])
else
col = ImGui.GetColorU32(25 --[[ImGuiCol.Header]])
end
ImGui.RenderFrame(bb.Min, bb.Max, col, false, style.SelectableRounding)
end
if g.NavId == id then
local nav_render_cursor_flags = 2 --[[ImGuiNavRenderCursorFlags.Compact]]
if is_multi_select then
nav_render_cursor_flags = bit32.bor(nav_render_cursor_flags, 4 --[[ImGuiNavRenderCursorFlags.AlwaysDraw]]) -- Always show the nav rectangle
end
ImGui.RenderNavCursor(bb, id, nav_render_cursor_flags, style.SelectableRounding)
end
end
if span_all_columns then
if g.CurrentTable then
ImGui.TablePopBackgroundChannel()
elseif window.DC.CurrentColumns then
ImGui.PopColumnsBackground()
end
end
-- Text stays at the submission position. Alignment/clipping extents ignore SpanAllColumns.
if is_visible then
ImGui.RenderTextClipped(pos, ImVec2(ImMin(pos.x + size.x, window.WorkRect.Max.x), pos.y + size.y), label, label_end, label_size, style.SelectableTextAlign, bb)
end
-- Automatically close popups
if pressed and not auto_selected and bit32.band(window.Flags, 67108864 --[[ImGuiWindowFlags.Popup]]) ~= 0 and bit32.band(flags, 1 --[[ImGuiSelectableFlags.NoAutoClosePopups]]) == 0 and bit32.band(g.LastItemData.ItemFlags, 16 --[[ImGuiItemFlags.AutoClosePopups]]) ~= 0 then
ImGui.CloseCurrentPopup()
end
if disabled_item and not disabled_global then
ImGui.EndDisabled()
end
-- Selectable() always returns a pressed state!
-- Users of BeginMultiSelect()/EndMultiSelect() scope: you may call ImGui.IsItemToggledSelection() to retrieve
-- selection toggle, only useful if you need that state updated (e.g. for rendering purpose) before reaching EndMultiSelect().
return pressed, selected
end
-------------------------------------------------------------------------------
-- [SECTION] Widgets: Typing-Select support
-------------------------------------------------------------------------------
do -- [Port] Typing-Select, Box-Select, Multi-Select sections: locals kept private
local MT = ImGui.GetMetatables()
local TypingSelectCharBuf = { 0, 0, 0, 0, 0 } -- [Luau] scratch for ImTextCharToUtf8() (C++: char w_buf[5])
-- [Experimental] Currently not exposed in public API.
-- Consume character inputs and return search request, if any.
-- This would typically only be called on the focused window or location you want to grab inputs for, e.g.
-- if ImGui.IsWindowFocused(...) then
-- local req = ImGui.GetTypingSelectRequest()
-- if req then focus_idx = ImGui.TypingSelectFindMatch(req, #my_items, function(user_data, n) return my_items[n + 1].Name end, my_items, -1) end
-- However the code is written in a way where calling it from multiple locations is safe (e.g. to obtain buffer).
-- [Port] Item indices passed to get_item_name_func(user_data, idx) and returned by TypingSelectFind*() are 0-based like upstream (-1 = none).
--- @param flags? ImGuiTypingSelectFlags
--- @return ImGuiTypingSelectRequest?
function ImGui.GetTypingSelectRequest(flags)
if flags == nil then flags = 0 --[[ImGuiTypingSelectFlags.None]] end
local g = GImGui
local data = g.TypingSelectState
local out_request = data.Request
-- Clear buffer
local TYPING_SELECT_RESET_TIMER = 1.80 -- FIXME: Potentially move to IO config.
local TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK = 4 -- Lock single char matching when repeating same char 4 times
if #data.SearchBuffer ~= 0 then
local clear_buffer = false
clear_buffer = clear_buffer or (g.NavFocusScopeId ~= data.FocusScope)
clear_buffer = clear_buffer or (data.LastRequestTime + TYPING_SELECT_RESET_TIMER < g.Time)
clear_buffer = clear_buffer or g.NavAnyRequest
clear_buffer = clear_buffer or (g.ActiveId ~= 0 and g.NavActivateId == 0) -- Allow temporary SPACE activation to not interfere
clear_buffer = clear_buffer or ImGui.IsKeyPressed(526 --[[ImGuiKey.Escape]]) or ImGui.IsKeyPressed(525 --[[ImGuiKey.Enter]])
clear_buffer = clear_buffer or (ImGui.IsKeyPressed(523 --[[ImGuiKey.Backspace]]) and bit32.band(flags, 1 --[[ImGuiTypingSelectFlags.AllowBackspace]]) == 0)
--if clear_buffer then IMGUI_DEBUG_LOG("GetTypingSelectRequest(): Clear SearchBuffer.\n") end
if clear_buffer then
data:Clear()
end
end
-- Append to buffer
local buffer_max_len = 64 - 1 -- [Port] IM_COUNTOF(data->SearchBuffer) - 1
local buffer_len = #data.SearchBuffer
local select_request = false
local input_queue = g.IO.InputQueueCharacters
for n = 1, input_queue.Size do
repeat
local w = input_queue.Data[n]
local w_buf = TypingSelectCharBuf
local w_len = ImStd.ImTextCharToUtf8(w_buf, w) -- [Port] also gives ImTextCountUtf8BytesFromStr(&w, &w + 1)
if w < 32 or (buffer_len == 0 and ImCharIsBlankW(w)) or (buffer_len + w_len > buffer_max_len) then -- Ignore leading blanks
break -- continue
end
local w_str = string.char(table.unpack(w_buf, 1, w_len))
if data.SingleCharModeLock and w_len == out_request.SingleCharSize and string.sub(data.SearchBuffer, 1, w_len) == w_str then
select_request = true -- Same character: don't need to append to buffer.
break -- continue
end
if data.SingleCharModeLock then
data:Clear() -- Different character: clear
buffer_len = 0
end
data.SearchBuffer = string.sub(data.SearchBuffer, 1, buffer_len) .. w_str -- Append
buffer_len = buffer_len + w_len
select_request = true
until true
end
input_queue:resize(0)
-- Handle backspace
if bit32.band(flags, 1 --[[ImGuiTypingSelectFlags.AllowBackspace]]) ~= 0 and ImGui.IsKeyPressedEx(523 --[[ImGuiKey.Backspace]], 1 --[[ImGuiInputFlags.Repeat]]) then
local p = ImStd.ImTextFindPreviousUtf8Codepoint(data.SearchBuffer, 1, buffer_len + 1) -- 1-based byte position
data.SearchBuffer = string.sub(data.SearchBuffer, 1, p - 1)
buffer_len = p - 1
end
-- Return request if any
if buffer_len == 0 then
return nil
end
if select_request then
data.FocusScope = g.NavFocusScopeId
data.LastRequestFrame = g.FrameCount
data.LastRequestTime = g.Time
end
out_request.Flags = flags
out_request.SearchBufferLen = buffer_len
out_request.SearchBuffer = data.SearchBuffer
out_request.SelectRequest = (data.LastRequestFrame == g.FrameCount)
out_request.SingleCharMode = false
out_request.SingleCharSize = 0
-- Calculate if buffer contains the same character repeated.
-- - This can be used to implement a special search mode on first character.
-- - Performed on UTF-8 codepoint for correctness.
-- - SingleCharMode is always set for first input character, because it usually leads to a "next".
if bit32.band(flags, 2 --[[ImGuiTypingSelectFlags.AllowSingleCharMode]]) ~= 0 then
local buf = out_request.SearchBuffer
local buf_begin = 1
local buf_end = buf_begin + out_request.SearchBufferLen
local c0_len = ImStd.ImTextCountUtf8BytesFromChar(buf, buf_begin, buf_end)
local c0 = string.sub(buf, buf_begin, buf_begin + c0_len - 1)
local p = buf_begin + c0_len
while p < buf_end do
if string.sub(buf, p, p + c0_len - 1) ~= c0 then -- memcmp(buf_begin, p, c0_len) != 0
break
end
p = p + c0_len
end
local single_char_count = (p == buf_end) and math.floor(out_request.SearchBufferLen / c0_len) or 0
out_request.SingleCharMode = (single_char_count > 0 or data.SingleCharModeLock)
out_request.SingleCharSize = c0_len
data.SingleCharModeLock = data.SingleCharModeLock or (single_char_count >= TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK) -- From now on we stop search matching to lock to single char mode.
end
return out_request
end
-- [Port] s1 is the byte range [s1_begin, s1_end) of string s1, s2 is a whole string (out of range bytes read as 0 like a C terminator).
local function ImStrimatchlen(s1, s1_begin, s1_end, s2)
local match_len = 0
local s2_p = 1
while s1_begin < s1_end and ImToUpper(string.byte(s1, s1_begin)) == ImToUpper(string.byte(s2, s2_p) or 0) do
s1_begin = s1_begin + 1
s2_p = s2_p + 1
match_len = match_len + 1
end
return match_len
end
-- Default handler for finding a result for typing-select. You may implement your own.
-- You might want to display a tooltip to visualize the current request SearchBuffer
-- When SingleCharMode is set:
-- - it is better to NOT display a tooltip of other on-screen display indicator.
-- - the index of the currently focused item is required.
-- if your SetNextItemSelectionUserData() values are indices, you can obtain it from ImGuiMultiSelectIO.NavIdItem, otherwise from g.NavLastValidSelectionUserData.
--- @param req ImGuiTypingSelectRequest?
--- @param items_count int
--- @param get_item_name_func fun(user_data: any, idx: int): string # idx is 0-based
--- @param user_data any
--- @param nav_item_idx int # 0-based, -1 if unknown
--- @return int # 0-based, -1 if none
function ImGui.TypingSelectFindMatch(req, items_count, get_item_name_func, user_data, nav_item_idx)
if req == nil or req.SelectRequest == false then -- Support NULL parameter so both calls can be done from same spot.
return -1
end
local idx = -1
if req.SingleCharMode and bit32.band(req.Flags, 2 --[[ImGuiTypingSelectFlags.AllowSingleCharMode]]) ~= 0 then
idx = ImGui.TypingSelectFindNextSingleCharMatch(req, items_count, get_item_name_func, user_data, nav_item_idx)
else
idx = ImGui.TypingSelectFindBestLeadingMatch(req, items_count, get_item_name_func, user_data)
end
if idx ~= -1 then
ImGui.SetNavCursorVisibleAfterMove()
end
return idx
end
-- Special handling when a single character is repeated: perform search on a single letter and goes to next.
function ImGui.TypingSelectFindNextSingleCharMatch(req, items_count, get_item_name_func, user_data, nav_item_idx)
-- FIXME: Assume selection user data is index. Would be extremely practical.
--if nav_item_idx == -1 then
-- nav_item_idx = g.NavLastValidSelectionUserData
--end
local first_match_idx = -1
local return_next_match = false
for idx = 0, items_count - 1 do
repeat
local item_name = get_item_name_func(user_data, idx)
if ImStrimatchlen(req.SearchBuffer, 1, 1 + req.SingleCharSize, item_name) < req.SingleCharSize then
break -- continue
end
if return_next_match then -- Return next matching item after current item.
return idx
end
if first_match_idx == -1 and nav_item_idx == -1 then -- Return first match immediately if we don't have a nav_item_idx value.
return idx
end
if first_match_idx == -1 then -- Record first match for wrapping.
first_match_idx = idx
end
if nav_item_idx == idx then -- Record that we encountering nav_item so we can return next match.
return_next_match = true
end
until true
end
return first_match_idx -- First result
end
function ImGui.TypingSelectFindBestLeadingMatch(req, items_count, get_item_name_func, user_data)
local longest_match_idx = -1
local longest_match_len = 0
for idx = 0, items_count - 1 do
local item_name = get_item_name_func(user_data, idx)
local match_len = ImStrimatchlen(req.SearchBuffer, 1, 1 + req.SearchBufferLen, item_name)
if match_len > longest_match_len then -- [Port] inverted 'if (match_len <= longest_match_len) continue;'
longest_match_idx = idx
longest_match_len = match_len
if match_len == req.SearchBufferLen then
break
end
end
end
return longest_match_idx
end
--- @param data ImGuiTypingSelectState
function ImGui.DebugNodeTypingSelectState(data)
ImGui.Text("SearchBuffer = \"%s\"", data.SearchBuffer)
ImGui.Text("SingleCharMode = %d, Size = %d, Lock = %d", data.Request.SingleCharMode and 1 or 0, data.Request.SingleCharSize, data.SingleCharModeLock and 1 or 0)
ImGui.Text("LastRequest = time: %.2f, frame: %d", data.LastRequestTime, data.LastRequestFrame)
end
-------------------------------------------------------------------------------
-- [SECTION] Widgets: Box-Select support
-- This has been extracted away from Multi-Select logic in the hope that it could eventually be used elsewhere, but hasn't been yet.
-------------------------------------------------------------------------------
-- Extra logic in MultiSelectItemFooter() and ImGuiListClipper:Step()
-------------------------------------------------------------------------------
-- - BoxSelectPreStartDrag() [Internal]
-- - BoxSelectActivateDrag() [Internal]
-- - BoxSelectDeactivateDrag() [Internal]
-- - BoxSelectScrollWithMouseDrag() [Internal]
-- - BeginBoxSelect() [Internal]
-- - EndBoxSelect() [Internal]
-------------------------------------------------------------------------------
-- Call on the initial click.
local function BoxSelectPreStartDrag(id, clicked_item)
local g = GImGui
local bs = g.BoxSelectState
bs.ID = id
bs.IsStarting = true -- Consider starting box-select.
bs.IsStartedFromVoid = (clicked_item == (-1) --[[ImGuiSelectionUserData_Invalid]])
bs.IsStartedSetNavIdOnce = bs.IsStartedFromVoid
bs.KeyMods = g.IO.KeyMods
local off = g.CurrentWindow.DC.CursorStartPos -- [Luau] inline WindowPosAbsToRel(g.CurrentWindow, g.IO.MousePos)
ImVec2_CopyV(bs.StartPosRel, g.IO.MousePos.x - off.x, g.IO.MousePos.y - off.y)
ImVec2_Copy(bs.EndPosRel, bs.StartPosRel)
ImVec2_CopyV(bs.ScrollAccum, 0.0, 0.0)
end
local function BoxSelectActivateDrag(bs, window)
local g = GImGui
IMGUI_DEBUG_LOG_SELECTION("[selection] BeginBoxSelect() 0X%08X: Activate\n", bs.ID)
bs.IsActive = true
bs.Window = window
bs.IsStarting = false
ImGui.SetActiveID(bs.ID, window)
ImGui.SetActiveIdUsingAllKeyboardKeys()
if bs.IsStartedFromVoid and bit32.band(bs.KeyMods, bit32.bor(4096 --[[ImGuiMod_Ctrl]], 8192 --[[ImGuiMod_Shift]])) == 0 then
bs.RequestClear = true
end
end
local function BoxSelectDeactivateDrag(bs)
local g = GImGui
bs.IsActive = false; bs.IsStarting = false
if g.ActiveId == bs.ID then
IMGUI_DEBUG_LOG_SELECTION("[selection] BeginBoxSelect() 0X%08X: Deactivate\n", bs.ID)
ImGui.ClearActiveID()
end
bs.ID = 0
end
local function BoxSelectScrollWithMouseDrag(bs, window, inner_r)
local g = GImGui
assert(bs.Window == window)
for n = 1, 2 do -- each axis -- [Port] 1 = X, 2 = Y
repeat
local k = (n == 1) and "x" or "y"
local mouse_pos = g.IO.MousePos[k]
local dist = (mouse_pos > inner_r.Max[k]) and mouse_pos - inner_r.Max[k] or (mouse_pos < inner_r.Min[k]) and mouse_pos - inner_r.Min[k] or 0.0
local scroll_curr = window.Scroll[k]
if dist == 0.0 or (dist < 0.0 and scroll_curr < 0.0) or (dist > 0.0 and scroll_curr >= window.ScrollMax[k]) then
break -- continue
end
local speed_multiplier = ImLinearRemapClamp(g.FontSize, g.FontSize * 5.0, 1.0, 4.0, ImAbs(dist)) -- x1 to x4 depending on distance
local scroll_step = g.FontSize * 35.0 * speed_multiplier * ImSign(dist) * g.IO.DeltaTime
bs.ScrollAccum[k] = bs.ScrollAccum[k] + scroll_step
-- Accumulate into a stored value so we can handle high-framerate
local scroll_step_i = ImFloor(bs.ScrollAccum[k])
if scroll_step_i == 0.0 then
break -- continue
end
if n == 1 then
ImGui.SetScrollX(window, scroll_curr + scroll_step_i)
else
ImGui.SetScrollY(window, scroll_curr + scroll_step_i)
end
bs.ScrollAccum[k] = bs.ScrollAccum[k] - scroll_step_i
until true
end
end
--- @param scope_rect ImRect
--- @param window ImGuiWindow
--- @param box_select_id ImGuiID
--- @param ms_flags ImGuiMultiSelectFlags
--- @return bool
function ImGui.BeginBoxSelect(scope_rect, window, box_select_id, ms_flags)
local g = GImGui
local bs = g.BoxSelectState
ImGui.KeepAliveID(box_select_id)
if bs.ID ~= box_select_id then
return false
end
-- IsStarting is set by MultiSelectItemFooter() when considering a possible box-select. We validate it here and lock geometry.
bs.UnclipMode = false
bs.RequestClear = false
if bs.IsStarting and ImGui.IsMouseDragPastThreshold(0) then
BoxSelectActivateDrag(bs, window)
elseif (bs.IsStarting or bs.IsActive) and g.IO.MouseDown[0] == false then
BoxSelectDeactivateDrag(bs)
end
if not bs.IsActive then
return false
end
-- Current frame absolute prev/current rectangles are used to toggle selection.
-- They are derived from positions relative to scrolling space, so "previous" rectangle is reprojected for current frame coordinates.
-- [Luau] scalar versions of WindowPosRelToAbs()/ImClamp()/ImMin()/ImMax() (no temporary ImVec2)
local off = window.DC.CursorStartPos
local start_pos_abs_x, start_pos_abs_y = bs.StartPosRel.x + off.x, bs.StartPosRel.y + off.y
local prev_end_pos_abs_x, prev_end_pos_abs_y = bs.EndPosRel.x + off.x, bs.EndPosRel.y + off.y -- Clamped already
local curr_end_pos_abs_x, curr_end_pos_abs_y = g.IO.MousePos.x, g.IO.MousePos.y
if bit32.band(ms_flags, 2048 --[[ImGuiMultiSelectFlags.ScopeWindow]]) ~= 0 then -- Box-select scrolling only happens with ScopeWindow
curr_end_pos_abs_x = ImClamp(curr_end_pos_abs_x, scope_rect.Min.x, scope_rect.Max.x)
curr_end_pos_abs_y = ImClamp(curr_end_pos_abs_y, scope_rect.Min.y, scope_rect.Max.y)
end
ImVec2_CopyV(bs.BoxSelectRectPrev.Min, ImMin(start_pos_abs_x, prev_end_pos_abs_x), ImMin(start_pos_abs_y, prev_end_pos_abs_y))
ImVec2_CopyV(bs.BoxSelectRectPrev.Max, ImMax(start_pos_abs_x, prev_end_pos_abs_x), ImMax(start_pos_abs_y, prev_end_pos_abs_y))
ImVec2_CopyV(bs.BoxSelectRectCurr.Min, ImMin(start_pos_abs_x, curr_end_pos_abs_x), ImMin(start_pos_abs_y, curr_end_pos_abs_y))
ImVec2_CopyV(bs.BoxSelectRectCurr.Max, ImMax(start_pos_abs_x, curr_end_pos_abs_x), ImMax(start_pos_abs_y, curr_end_pos_abs_y))
--IMGUI_DEBUG_LOG("StartPosRel (%.2f,%.2f) EndPosRel (%.2f,%.2f) -> (%.2f,%.2f)\n", bs.StartPosRel.x, bs.StartPosRel.y, bs.EndPosRel.x, bs.EndPosRel.y, WindowPosAbsToRel(window, g.IO.MousePos).x, WindowPosAbsToRel(window, g.IO.MousePos).y)
-- Box-select 2D mode detects change of the rectangle.
-- Storing unclip rects which will be tested by widgets supporting box-select. Always update rectangles when active (even if we don't use them).
-- To facilitate understanding this: enable IMGUI_DEBUG_BOXSELECT and visualize all geometry.
if bit32.band(ms_flags, bit32.bor(64 --[[ImGuiMultiSelectFlags.BoxSelect1d]], 128 --[[ImGuiMultiSelectFlags.BoxSelect2d]])) ~= 0 then
-- For both sides, compute the area differing between Prev and Curr rectangles.
local unclip_x, unclip_y = bs.UnclipRects[1], bs.UnclipRects[2] -- [Port] UnclipRects[0], UnclipRects[1]
ImVec2_CopyV(unclip_x.Min, 3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]]); ImVec2_CopyV(unclip_x.Max, -3.4028234663853e+38 --[[FLT_MAX]], -3.4028234663853e+38 --[[FLT_MAX]])
ImVec2_CopyV(unclip_y.Min, 3.4028234663853e+38 --[[FLT_MAX]], 3.4028234663853e+38 --[[FLT_MAX]]); ImVec2_CopyV(unclip_y.Max, -3.4028234663853e+38 --[[FLT_MAX]], -3.4028234663853e+38 --[[FLT_MAX]])
local curr, prev = bs.BoxSelectRectCurr, bs.BoxSelectRectPrev
for side = 0, 1 do
local a = (side == 0) and curr.Min or curr.Max
local b = (side == 0) and prev.Min or prev.Max
local d_min_x, d_min_y = ImMin(a.x, b.x), ImMin(a.y, b.y)
local d_max_x, d_max_y = ImMax(a.x, b.x), ImMax(a.y, b.y)
if d_min_x ~= d_max_x then
unclip_x:AddX(d_min_x)
unclip_x:AddX(d_max_x)
end
if d_min_y ~= d_max_y then
unclip_y:AddY(d_min_y)
unclip_y:AddY(d_max_y)
end
end
-- box_select_intersection = BoxSelectRectPrev + BoxSelectRectCurr
local box_select_intersection_min_x, box_select_intersection_min_y = ImMin(prev.Min.x, curr.Min.x), ImMin(prev.Min.y, curr.Min.y)
local box_select_intersection_max_x, box_select_intersection_max_y = ImMax(prev.Max.x, curr.Max.x), ImMax(prev.Max.y, curr.Max.y)
if bit32.band(ms_flags, 128 --[[ImGuiMultiSelectFlags.BoxSelect2d]]) ~= 0 then
if prev.Min.x ~= curr.Min.x or prev.Max.x ~= curr.Max.x then
unclip_x:AddY(box_select_intersection_min_y)
unclip_x:AddY(box_select_intersection_max_y)
end
end
if bit32.band(ms_flags, bit32.bor(64 --[[ImGuiMultiSelectFlags.BoxSelect1d]], 128 --[[ImGuiMultiSelectFlags.BoxSelect2d]])) ~= 0 then
if prev.Min.y ~= curr.Min.y or prev.Max.y ~= curr.Max.y then
unclip_y:AddX(box_select_intersection_min_x)
unclip_y:AddX(box_select_intersection_max_x)
end
end
-- Merge both rectangles into one.
-- FIXME-OPT: When UnclipRect.Area() is much larger than the sum of UnclipRects[0]/[1] Areas, widgets should
-- ideally first use UnclipRect as a first coarse cull layer + the individual ones as a second validation.
ImRect_Copy(bs.UnclipRect, unclip_x)
bs.UnclipRect:Add(unclip_y)
if not bs.UnclipRect:IsInverted() and (not window.ClipRect:Contains(bs.UnclipRect.Min) or not window.ClipRect:Contains(bs.UnclipRect.Max)) then -- !! Don't use Contains(ImRect)
bs.UnclipMode = true
end
if bs.UnclipMode and g.CurrentTable ~= nil then
ImGui.TableApplyExternalUnclipRect(g.CurrentTable, bs.UnclipRect) -- No need submitting both
end
end
-- [Port] IMGUI_DEBUG_BOXSELECT visualization not ported (compile-time debug option, disabled upstream).
return true
end
--- @param scope_rect ImRect
--- @param ms_flags ImGuiMultiSelectFlags
function ImGui.EndBoxSelect(scope_rect, ms_flags)
local g = GImGui
local window = g.CurrentWindow
local bs = g.BoxSelectState
assert(bs.IsActive)
bs.UnclipMode = false
-- Render selection rectangle
bs.EndPosRel = ImGui.WindowPosAbsToRel(window, ImClamp(g.IO.MousePos, scope_rect.Min, scope_rect.Max)) -- Clamp stored position according to current scrolling view
local box_select_r = ImRect(bs.BoxSelectRectCurr.Min, bs.BoxSelectRectCurr.Max)
box_select_r:ClipWith(scope_rect)
local draw_window = ImGui.FindFrontMostVisibleChildWindow(window)
draw_window.DrawList:AddRectFilled(box_select_r.Min, box_select_r.Max, ImGui.GetColorU32(29 --[[ImGuiCol.SeparatorHovered]], 0.30)) -- FIXME-MULTISELECT: Styling
draw_window.DrawList:AddRect(box_select_r.Min, box_select_r.Max, ImGui.GetColorU32(59 --[[ImGuiCol.NavCursor]])) -- FIXME-MULTISELECT FIXME-DPI: Styling
-- Scroll
local enable_scroll = bit32.band(ms_flags, 2048 --[[ImGuiMultiSelectFlags.ScopeWindow]]) ~= 0 and bit32.band(ms_flags, 256 --[[ImGuiMultiSelectFlags.BoxSelectNoScroll]]) == 0
if enable_scroll then
local scroll_r = ImRect(scope_rect.Min, scope_rect.Max)
scroll_r:Expand(-g.FontSize)
--GetForegroundDrawList():AddRect(scroll_r.Min, scroll_r.Max, IM_COL32(0, 255, 0, 255))
if not scroll_r:Contains(g.IO.MousePos) then
BoxSelectScrollWithMouseDrag(bs, window, scroll_r)
end
end
end
-------------------------------------------------------------------------------
-- [SECTION] Widgets: Multi-Select support
-------------------------------------------------------------------------------
-- - DebugLogMultiSelectRequests() [Internal]
-- - CalcScopeRect() [Internal]
-- - BeginMultiSelect()
-- - EndMultiSelect()
-- - SetNextItemSelectionUserData()
-- - MultiSelectItemHeader() [Internal]
-- - MultiSelectItemFooter() [Internal]
-- - DebugNodeMultiSelectState() [Internal]
-------------------------------------------------------------------------------
-- [Luau] reused scratch request: ImVector:push_back() copies it into the preallocated element (ImGuiSelectionRequest_Copy)
local MultiSelectScratchRequest = { Type = 0, Selected = false, RangeDirection = 0, RangeFirstItem = -1, RangeLastItem = -1 }
local MultiSelectScratchItemRect = ImRect() -- [Luau] scratch for MultiSelectItemFooter()'s item_rect
local function DebugLogMultiSelectRequests(func, io)
local g = GImGui
for _, req in io.Requests:iter() do
if req.Type == 1 --[[ImGuiSelectionRequestType.SetAll]] then IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetAll %d (= %s)\n", func, req.Selected and 1 or 0, req.Selected and "SelectAll" or "Clear") end
if req.Type == 2 --[[ImGuiSelectionRequestType.SetRange]] then IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetRange %s..%s = %d (dir %d)\n", func, tostring(req.RangeFirstItem), tostring(req.RangeLastItem), req.Selected and 1 or 0, req.RangeDirection) end
end
end
local function CalcScopeRect(ms, window)
if bit32.band(ms.Flags, 4096 --[[ImGuiMultiSelectFlags.ScopeRect]]) ~= 0 then
-- Warning: this depends on CursorMaxPos so it means to be called by EndMultiSelect() only
-- This probably doesn't work inside a table as there are ample ambiguities related to exact time of calling BeginMultiSelect()/EndMultiSelect().
return ImRect(ms.ScopeRectMin.x, ms.ScopeRectMin.y, ImMax(window.DC.CursorMaxPos.x, ms.ScopeRectMin.x), ImMax(window.DC.CursorMaxPos.y, ms.ScopeRectMin.y))
else
--// When a table, pull HostClipRect, which allows us to predict ClipRect before first row/layout is performed. (#7970)
local scope_rect = ImRect(window.InnerClipRect.Min, window.InnerClipRect.Max)
--if g.CurrentTable ~= nil then
-- scope_rect = g.CurrentTable.HostClipRect
--end
-- Add inner table decoration (#7821) // FIXME: Why not baking in InnerClipRect?
scope_rect.Min.x = ImMin(scope_rect.Min.x + window.DecoInnerSizeX1, scope_rect.Max.x)
scope_rect.Min.y = ImMin(scope_rect.Min.y + window.DecoInnerSizeY1, scope_rect.Max.y)
return scope_rect
end
end
-- Return ImGuiMultiSelectIO structure.
-- Lifetime: don't hold on ImGuiMultiSelectIO references over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect().
-- Passing 'selection_size' and 'items_count' parameters is currently optional.
-- - 'selection_size' is useful to disable some shortcut routing: e.g. ImGuiMultiSelectFlags.ClearOnEscape won't claim Escape key when selection_size 0,
-- allowing a first press to clear selection THEN the second press to leave child window and return to parent.
-- - 'items_count' is stored in ImGuiMultiSelectIO which makes it a convenient way to pass the information to your ApplyRequest() handler (but you may pass it differently).
-- - If they are costly for you to compute (e.g. external intrusive selection without maintaining size), you may avoid them and pass -1.
-- - If you can easily tell if your selection is empty or not, you may pass 0/1, or you may enable ImGuiMultiSelectFlags.ClearOnEscape flag dynamically.
--- @param flags ImGuiMultiSelectFlags
--- @param selection_size? int
--- @param items_count? int
--- @return ImGuiMultiSelectIO
function ImGui.BeginMultiSelect(flags, selection_size, items_count)
if selection_size == nil then selection_size = -1 end
if items_count == nil then items_count = -1 end
local g = GImGui
local window = g.CurrentWindow
g.MultiSelectTempDataStacked = g.MultiSelectTempDataStacked + 1
while g.MultiSelectTempDataStacked > g.MultiSelectTempData.Size do -- [Port] resize(n, ImGuiMultiSelectTempData()) would share one instance
g.MultiSelectTempData:push_back(ImGuiMultiSelectTempData())
end
local ms = g.MultiSelectTempData.Data[g.MultiSelectTempDataStacked]
g.CurrentMultiSelect = ms
if bit32.band(flags, bit32.bor(2048 --[[ImGuiMultiSelectFlags.ScopeWindow]], 4096 --[[ImGuiMultiSelectFlags.ScopeRect]])) == 0 then
flags = bit32.bor(flags, 2048 --[[ImGuiMultiSelectFlags.ScopeWindow]])
end
if bit32.band(flags, 1 --[[ImGuiMultiSelectFlags.SingleSelect]]) ~= 0 then
flags = bit32.band(flags, bit32.bnot(bit32.bor(128 --[[ImGuiMultiSelectFlags.BoxSelect2d]], 64 --[[ImGuiMultiSelectFlags.BoxSelect1d]])))
end
if bit32.band(flags, 128 --[[ImGuiMultiSelectFlags.BoxSelect2d]]) ~= 0 then
flags = bit32.band(flags, bit32.bnot(64 --[[ImGuiMultiSelectFlags.BoxSelect1d]]))
end
-- FIXME: Workaround to the fact we override CursorMaxPos, meaning size measurement are lost. (#8250)
-- They should perhaps be stacked properly?
local table_ = g.CurrentTable
if table_ then
if not table_.IsLayoutLocked then
ImGui.TableUpdateLayout(table_)
elseif table_.CurrentColumn ~= -1 then
ImGui.TableEndCell(table_) -- This is currently safe to call multiple time. If that properly is lost we can extract the "save measurement" part of it.
end
end
-- FIXME: BeginFocusScope()
local id = window.IDStack.Data[window.IDStack.Size] -- window.IDStack:back()
ms:Clear()
ms.FocusScopeId = id
ms.Flags = flags
ImVec2_Copy(ms.BackupCursorMaxPos, window.DC.CursorMaxPos)
ImVec2_Copy(ms.ScopeRectMin, window.DC.CursorPos)
if bit32.band(flags, 4096 --[[ImGuiMultiSelectFlags.ScopeRect]]) ~= 0 then
ImVec2_Copy(window.DC.CursorMaxPos, ms.ScopeRectMin) -- CalcScopeRect() for ImGuiMultiSelectFlags.ScopeRect will measure in EndMultiSelect().
end
ImGui.PushFocusScope(ms.FocusScopeId)
ms.IsFocused = ImGui.IsInNavFocusRoute(g.CurrentFocusScopeId)
if bit32.band(flags, 2048 --[[ImGuiMultiSelectFlags.ScopeWindow]]) ~= 0 then -- Mark parent child window as navigable into, with highlight. Assume user will always submit interactive items.
window.DC.NavLayersActiveMask = bit32.bor(window.DC.NavLayersActiveMask, bit32.lshift(1, 0 --[[ImGuiNavLayer.Main]]))
end
-- Use copy of keyboard mods at the time of the request, otherwise we would requires mods to be held for an extra frame.
if g.NavJustMovedToId ~= 0 then
ms.KeyMods = g.NavJustMovedToIsTabbing and 0 or g.NavJustMovedToKeyMods
else
ms.KeyMods = g.IO.KeyMods
end
if bit32.band(flags, 4 --[[ImGuiMultiSelectFlags.NoRangeSelect]]) ~= 0 then
ms.KeyMods = bit32.band(ms.KeyMods, bit32.bnot(8192 --[[ImGuiMod_Shift]]))
end
-- Bind storage
local storage = g.MultiSelectStorage:GetOrAddByKey(id)
storage.ID = id
storage.LastFrameActive = g.FrameCount
storage.LastSelectionSize = selection_size
storage.Window = window
ms.Storage = storage
-- Output to user
ms.IO.Requests:resize(0)
ms.IO.RangeSrcItem = storage.RangeSrcItem
ms.IO.NavIdItem = storage.NavIdItem
ms.IO.NavIdSelected = (storage.NavIdSelected == 1)
ms.IO.ItemsCount = items_count
-- Clear when using Navigation to move within the scope
-- (we compare FocusScopeId so it possible to use multiple selections inside a same window)
local request_clear = false
local request_select_all = false
if g.NavJustMovedToId ~= 0 and g.NavJustMovedToFocusScopeId == ms.FocusScopeId and g.NavJustMovedToHasSelectionData then
if bit32.band(ms.KeyMods, 8192 --[[ImGuiMod_Shift]]) ~= 0 then
ms.IsKeyboardSetRange = true
end
if ms.IsKeyboardSetRange then
assert(storage.RangeSrcItem ~= (-1) --[[ImGuiSelectionUserData_Invalid]]) -- Not ready -> could clear?
end
if bit32.band(ms.KeyMods, bit32.bor(4096 --[[ImGuiMod_Ctrl]], 8192 --[[ImGuiMod_Shift]])) == 0 and bit32.band(flags, bit32.bor(16 --[[ImGuiMultiSelectFlags.NoAutoClear]], 8 --[[ImGuiMultiSelectFlags.NoAutoSelect]])) == 0 then
request_clear = true
end
elseif g.NavJustMovedFromFocusScopeId == ms.FocusScopeId then
-- Also clear on leaving scope (may be optional?)
if bit32.band(ms.KeyMods, bit32.bor(4096 --[[ImGuiMod_Ctrl]], 8192 --[[ImGuiMod_Shift]])) == 0 and bit32.band(flags, bit32.bor(16 --[[ImGuiMultiSelectFlags.NoAutoClear]], 8 --[[ImGuiMultiSelectFlags.NoAutoSelect]])) == 0 then
request_clear = true
end
end
-- Box-select handling: update active state.
local bs = g.BoxSelectState
if bit32.band(flags, bit32.bor(64 --[[ImGuiMultiSelectFlags.BoxSelect1d]], 128 --[[ImGuiMultiSelectFlags.BoxSelect2d]])) ~= 0 then
ms.BoxSelectId = ImGui.GetID("##BoxSelect")
if ImGui.BeginBoxSelect(CalcScopeRect(ms, window), window, ms.BoxSelectId, flags) then
request_clear = request_clear or bs.RequestClear
end
end
if ms.IsFocused then
-- Shortcut: Clear selection (Escape)
-- - Only claim shortcut if selection is not empty, allowing further presses on Escape to e.g. leave current child window.
-- - Box select also handle Escape and needs to pass an id to bypass ActiveIdUsingAllKeyboardKeys lock.
if bit32.band(flags, 512 --[[ImGuiMultiSelectFlags.ClearOnEscape]]) ~= 0 then
if selection_size ~= 0 or bs.IsActive then
if ImGui.Shortcut(526 --[[ImGuiKey.Escape]], 0 --[[ImGuiInputFlags.None]], bs.IsActive and bs.ID or 0) then
request_clear = true
if bs.IsActive then
BoxSelectDeactivateDrag(bs)
end
end
end
end
-- Shortcut: Select all (Ctrl+A)
if bit32.band(flags, 1 --[[ImGuiMultiSelectFlags.SingleSelect]]) == 0 and bit32.band(flags, 2 --[[ImGuiMultiSelectFlags.NoSelectAll]]) == 0 then
if ImGui.Shortcut(bit32.bor(4096 --[[ImGuiMod_Ctrl]], 546 --[[ImGuiKey.A]])) then
request_select_all = true
end
end
end
if request_clear or request_select_all then
ImGui.MultiSelectAddSetAll(ms, request_select_all)
if not request_select_all then
storage.LastSelectionSize = 0
end
end
ms.LoopRequestSetAll = request_select_all and 1 or (request_clear and 0 or -1)
ms.IsSoleOrUnknownSelectionSize = (storage.LastSelectionSize == 1) or (storage.LastSelectionSize == -1)
--ms.PrevSubmittedItem = ImGuiSelectionUserData_Invalid
if bit32.band(g.DebugLogFlags, 64 --[[ImGuiDebugLogFlags.EventSelection]]) ~= 0 then
DebugLogMultiSelectRequests("BeginMultiSelect", ms.IO)
end
return ms.IO
end
-- Return updated ImGuiMultiSelectIO structure.
-- Lifetime: don't hold on ImGuiMultiSelectIO references over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect().
--- @return ImGuiMultiSelectIO
function ImGui.EndMultiSelect()
local g = GImGui
local ms = g.CurrentMultiSelect
local storage = ms.Storage
local window = g.CurrentWindow
IM_ASSERT_USER_ERROR(ms.FocusScopeId == g.CurrentFocusScopeId, "EndMultiSelect() FocusScope mismatch!")
assert(g.CurrentMultiSelect ~= nil and storage.Window == g.CurrentWindow)
assert(g.MultiSelectTempDataStacked > 0 and g.MultiSelectTempData.Data[g.MultiSelectTempDataStacked] == g.CurrentMultiSelect)
local scope_rect = CalcScopeRect(ms, window)
if ms.IsFocused then
-- We currently don't allow user code to modify RangeSrcItem by writing to BeginIO's version, but that would be an easy change here.
if ms.IO.RangeSrcReset or (ms.RangeSrcPassedBy == false and ms.IO.RangeSrcItem ~= (-1) --[[ImGuiSelectionUserData_Invalid]]) then -- Can't read storage.RangeSrcItem here -> we want the state at beginning of the scope (see tests for easy failure)
IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset RangeSrcItem.\n") -- Will set be to NavId.
storage.RangeSrcItem = (-1) --[[ImGuiSelectionUserData_Invalid]]
end
if ms.NavIdPassedBy == false and storage.NavIdItem ~= (-1) --[[ImGuiSelectionUserData_Invalid]] then
IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset NavIdItem.\n")
storage.NavIdItem = (-1) --[[ImGuiSelectionUserData_Invalid]]
storage.NavIdSelected = -1
end
if bit32.band(ms.Flags, bit32.bor(64 --[[ImGuiMultiSelectFlags.BoxSelect1d]], 128 --[[ImGuiMultiSelectFlags.BoxSelect2d]])) ~= 0 and ImGui.GetBoxSelectState(ms.BoxSelectId) then
ImGui.EndBoxSelect(scope_rect, ms.Flags)
end
end
if ms.IsEndIO == false then
ms.IO.Requests:resize(0)
end
-- Clear selection when clicking void?
-- We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection!
-- The InnerRect test is necessary for non-child/decorated windows.
local scope_hovered = window.InnerRect:Contains(g.IO.MousePos) and ImGui.IsWindowHovered(1 --[[ImGuiHoveredFlags.ChildWindows]])
if scope_hovered and bit32.band(ms.Flags, 4096 --[[ImGuiMultiSelectFlags.ScopeRect]]) ~= 0 then
scope_hovered = scope_hovered and scope_rect:Contains(g.IO.MousePos)
end
if scope_hovered and g.HoveredId == 0 and g.ActiveId == 0 then
if bit32.band(ms.Flags, bit32.bor(64 --[[ImGuiMultiSelectFlags.BoxSelect1d]], 128 --[[ImGuiMultiSelectFlags.BoxSelect2d]])) ~= 0 then
if not g.BoxSelectState.IsActive and not g.BoxSelectState.IsStarting and g.IO.MouseClickedCount[0] == 1 then
BoxSelectPreStartDrag(ms.BoxSelectId, (-1) --[[ImGuiSelectionUserData_Invalid]])
ImGui.FocusWindow(window, 2 --[[ImGuiFocusRequestFlags.UnlessBelowModal]])
ImGui.SetHoveredID(ms.BoxSelectId)
if bit32.band(ms.Flags, 4096 --[[ImGuiMultiSelectFlags.ScopeRect]]) ~= 0 then
ImGui.SetNavID(0, 0 --[[ImGuiNavLayer.Main]], ms.FocusScopeId, ImRect(g.IO.MousePos.x, g.IO.MousePos.y, g.IO.MousePos.x, g.IO.MousePos.y)) -- Automatically switch FocusScope for initial click from void to box-select.
end
end
end
if bit32.band(ms.Flags, 1024 --[[ImGuiMultiSelectFlags.ClearOnClickVoid]]) ~= 0 then
if ImGui.IsMouseReleased(0) and ImGui.IsMouseDragPastThreshold(0) == false and g.IO.KeyMods == 0 --[[ImGuiMod_None]] then
ImGui.MultiSelectAddSetAll(ms, false)
end
end
end
-- Courtesy nav wrapping helper flag
if bit32.band(ms.Flags, 65536 --[[ImGuiMultiSelectFlags.NavWrapX]]) ~= 0 then
assert(bit32.band(ms.Flags, 2048 --[[ImGuiMultiSelectFlags.ScopeWindow]]) ~= 0) -- Only supported at window scope
ImGui.NavMoveRequestTryWrapping(ImGui.GetCurrentWindow(), 4 --[[ImGuiNavMoveFlags.WrapX]])
end
-- Unwind
local table_ = g.CurrentTable
if table_ then
if table_.IsInsideRow then
ImGui.TableEndRow(table_)
end
end
ImVec2_CopyV(window.DC.CursorMaxPos, ImMax(ms.BackupCursorMaxPos.x, window.DC.CursorMaxPos.x), ImMax(ms.BackupCursorMaxPos.y, window.DC.CursorMaxPos.y))
ImGui.PopFocusScope()
if bit32.band(g.DebugLogFlags, 64 --[[ImGuiDebugLogFlags.EventSelection]]) ~= 0 then
DebugLogMultiSelectRequests("EndMultiSelect", ms.IO)
end
ms.FocusScopeId = 0
ms.Flags = 0 --[[ImGuiMultiSelectFlags.None]]
g.MultiSelectTempDataStacked = g.MultiSelectTempDataStacked - 1
if g.MultiSelectTempDataStacked > 0 then
g.CurrentMultiSelect = g.MultiSelectTempData.Data[g.MultiSelectTempDataStacked]
else
g.CurrentMultiSelect = nil
end
return ms.IO
end
--- @param selection_user_data ImGuiSelectionUserData
function ImGui.SetNextItemSelectionUserData(selection_user_data)
-- Note that flags will be cleared by ItemAdd(), so it's only useful for Navigation code!
-- This designed so widgets can also cheaply set this before calling ItemAdd(), so we are not tied to MultiSelect api.
local g = GImGui
local next_item_data = g.NextItemData
next_item_data.SelectionUserData = selection_user_data
next_item_data.FocusScopeId = g.CurrentFocusScopeId
local ms = g.CurrentMultiSelect
if ms then
-- Auto updating RangeSrcPassedBy for cases were clipper is not used (done before ItemAdd() clipping)
next_item_data.ItemFlagsSet = bit32.bor(next_item_data.ItemFlagsSet, 2097152 --[[ImGuiItemFlags.HasSelectionUserData]], 4194304 --[[ImGuiItemFlags.IsMultiSelect]])
if ms.IO.RangeSrcItem == selection_user_data then
ms.RangeSrcPassedBy = true
end
--ms.PrevSubmittedItem = ms.CurrSubmittedItem -- Can't rely on previous g.NextItemData.SelectionUserData because NextItemData is not restored on nested multi-select.
--ms.CurrSubmittedItem = selection_user_data
else
next_item_data.ItemFlagsSet = bit32.bor(next_item_data.ItemFlagsSet, 2097152 --[[ImGuiItemFlags.HasSelectionUserData]])
end
end
-- In charge of:
-- - Applying SetAll for submitted items.
-- - Applying SetRange for submitted items and record end points.
-- - Altering button behavior flags to facilitate use with drag and drop.
-- [Port] out-params as return values: `selected, button_flags = MultiSelectItemHeader(id, selected, button_flags)` (button_flags may be nil: C++ p_button_flags == NULL)
--- @param id ImGuiID
--- @param selected bool
--- @param button_flags ImGuiButtonFlags?
--- @return bool, ImGuiButtonFlags?
function ImGui.MultiSelectItemHeader(id, selected, button_flags)
local g = GImGui
local ms = g.CurrentMultiSelect
if ms.IsFocused then
local storage = ms.Storage
local item_data = g.NextItemData.SelectionUserData
assert(g.NextItemData.FocusScopeId == g.CurrentFocusScopeId, "Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope")
-- Apply SetAll (Clear/SelectAll) requests requested by BeginMultiSelect().
-- This is only useful if the user hasn't processed them already, and this only works if the user isn't using the clipper.
-- If you are using a clipper you need to process the SetAll request after calling BeginMultiSelect()
if ms.LoopRequestSetAll ~= -1 then
selected = (ms.LoopRequestSetAll == 1)
end
-- When using Shift+Nav: because it can incur scrolling we cannot afford a frame of lag with the selection highlight (otherwise scrolling would happen before selection)
-- For this to work, we need someone to set 'RangeSrcPassedBy = true' at some point (either clipper either SetNextItemSelectionUserData() function)
if ms.IsKeyboardSetRange then
assert(id ~= 0 and bit32.band(ms.KeyMods, 8192 --[[ImGuiMod_Shift]]) ~= 0)
local is_range_dst = (ms.RangeDstPassedBy == false) and g.NavJustMovedToId == id -- Assume that g.NavJustMovedToId is not clipped.
if is_range_dst then
ms.RangeDstPassedBy = true
end
if is_range_dst and storage.RangeSrcItem == (-1) --[[ImGuiSelectionUserData_Invalid]] then -- If we don't have RangeSrc, assign RangeSrc = RangeDst
storage.RangeSrcItem = item_data
storage.RangeSelected = selected and 1 or 0
end
local is_range_src = storage.RangeSrcItem == item_data
if is_range_src or is_range_dst or ms.RangeSrcPassedBy ~= ms.RangeDstPassedBy then
-- Apply range-select value to visible items
assert(storage.RangeSrcItem ~= (-1) --[[ImGuiSelectionUserData_Invalid]] and storage.RangeSelected ~= -1)
selected = (storage.RangeSelected ~= 0)
elseif bit32.band(ms.KeyMods, 4096 --[[ImGuiMod_Ctrl]]) == 0 and bit32.band(ms.Flags, 16 --[[ImGuiMultiSelectFlags.NoAutoClear]]) == 0 then
-- Clear other items
selected = false
end
end
end
-- Alter button behavior flags
-- To handle drag and drop of multiple items we need to avoid clearing selection on click.
-- Enabling this test makes actions using Ctrl+Shift delay their effect on MouseUp which is annoying, but it allows drag and drop of multiple items.
if button_flags ~= nil then
button_flags = bit32.bor(button_flags, 524288 --[[ImGuiButtonFlags.NoHoveredOnFocus]])
button_flags = bit32.band(button_flags, bit32.bnot(bit32.bor(16 --[[ImGuiButtonFlags.PressedOnClick]], 32 --[[ImGuiButtonFlags.PressedOnClickRelease]])))
if bit32.band(ms.Flags, 16384 --[[ImGuiMultiSelectFlags.SelectOnClickAlways]]) ~= 0 then
button_flags = bit32.bor(button_flags, 16 --[[ImGuiButtonFlags.PressedOnClick]])
elseif bit32.band(ms.Flags, 32768 --[[ImGuiMultiSelectFlags.SelectOnClickRelease]]) ~= 0 then
button_flags = bit32.bor(button_flags, 32 --[[ImGuiButtonFlags.PressedOnClickRelease]])
else -- ImGuiMultiSelectFlags.SelectOnAuto
button_flags = bit32.bor(button_flags, (not selected or (g.ActiveId == id and g.ActiveIdHasBeenPressedBefore)) and 16 --[[ImGuiButtonFlags.PressedOnClick]] or 32 --[[ImGuiButtonFlags.PressedOnClickRelease]])
end
end
return selected, button_flags
end
-- In charge of:
-- - Auto-select on navigation.
-- - Box-select toggle handling.
-- - Right-click handling.
-- - Altering selection based on Ctrl/Shift modifiers, both for keyboard and mouse.
-- - Record current selection state for RangeSrc
-- This is all rather complex, best to run and refer to "widgets_multiselect_xxx" tests in imgui_test_suite.
-- [Port] out-params as return values: `selected, pressed = MultiSelectItemFooter(id, selected, pressed, extra_flags)`
--- @param id ImGuiID
--- @param selected bool
--- @param pressed bool
--- @param extra_flags? ImGuiMultiSelectFlags
--- @return bool, bool
function ImGui.MultiSelectItemFooter(id, selected, pressed, extra_flags)
if extra_flags == nil then extra_flags = 0 end
local g = GImGui
local window = g.CurrentWindow
local ms = g.CurrentMultiSelect
local storage = ms.Storage
if pressed then
ms.IsFocused = true
end
local hovered = false
if bit32.band(g.LastItemData.StatusFlags, 1 --[[ImGuiItemStatusFlags.HoveredRect]]) ~= 0 then
hovered = ImGui.IsItemHovered(32 --[[ImGuiHoveredFlags.AllowWhenBlockedByPopup]])
end
if not ms.IsFocused and not hovered then
return selected, pressed
end
local item_data = g.NextItemData.SelectionUserData
local flags = bit32.bor(ms.Flags, extra_flags)
local is_singleselect = bit32.band(flags, 1 --[[ImGuiMultiSelectFlags.SingleSelect]]) ~= 0
local is_ctrl = bit32.band(ms.KeyMods, 4096 --[[ImGuiMod_Ctrl]]) ~= 0
local is_shift = bit32.band(ms.KeyMods, 8192 --[[ImGuiMod_Shift]]) ~= 0
local apply_to_range_src = false
if g.NavId == id and storage.RangeSrcItem == (-1) --[[ImGuiSelectionUserData_Invalid]] then
apply_to_range_src = true
end
if ms.IsEndIO == false then
ms.IO.Requests:resize(0)
ms.IsEndIO = true
end
-- Auto-select as you navigate a list
if g.NavJustMovedToId == id then
if bit32.band(flags, 8 --[[ImGuiMultiSelectFlags.NoAutoSelect]]) == 0 then
if is_ctrl and is_shift then
pressed = true
elseif not is_ctrl then
selected = true; pressed = true
end
else
-- With NoAutoSelect, using Shift+keyboard performs a write/copy
if is_shift then
pressed = true
elseif not is_ctrl then
apply_to_range_src = true -- Since if (pressed) {} main block is not running we update this
end
end
end
if apply_to_range_src then
storage.RangeSrcItem = item_data
storage.RangeSelected = selected and 1 or 0 -- Will be updated at the end of this function anyway.
end
-- Box-select toggle handling
if ms.BoxSelectId ~= 0 then
local bs = ImGui.GetBoxSelectState(ms.BoxSelectId)
if bs then
local item_rect = MultiSelectScratchItemRect
ImRect_Copy(item_rect, g.LastItemData.Rect)
if not window.DC.NavIsScrollPushableX then -- FIXME: Rename to be more generic.
local table_ = g.CurrentTable
if table_ and table_.CurrentColumn ~= -1 then
-- FIXME: We cannot solely use current ClipRect as it includes HostClipRect.
-- However we account for ClipRect being larger than current column (e.g. when using SpanAllColumns)
-- A more generic version would be nice, but window.WorkRect.Min/Max exclude CellPadding. (#7994, #9383)
local column = table_.Columns[table_.CurrentColumn]
-- [Port] upstream tests ItemFlags against ImGuiItemStatusFlags_HasClipRect (sic), reproduced as-is.
local has_clip_rect = bit32.band(g.LastItemData.ItemFlags, 512 --[[ImGuiItemStatusFlags.HasClipRect]]) ~= 0
local clip_min_x = has_clip_rect and g.LastItemData.ClipRect.Min.x or window.ClipRect.Min.x
local clip_max_x = has_clip_rect and g.LastItemData.ClipRect.Max.x or window.ClipRect.Max.x
if clip_min_x ~= clip_max_x then -- When zero sized we expect that bounds have been clamped and thus are unreliable
item_rect.Min.x = ImMax(item_rect.Min.x, ImMin(column.MinX, clip_min_x))
item_rect.Max.x = ImMin(item_rect.Max.x, ImMax(column.MaxX, clip_max_x))
else
item_rect.Min.x = ImMax(item_rect.Min.x, column.MinX)
item_rect.Max.x = ImMin(item_rect.Max.x, column.MaxX)
end
--GetForegroundDrawList():AddRect(item_rect.Min, item_rect.Max, IM_COL32(255, 0, 255, 255))
end
end
local rect_overlap_curr = bs.BoxSelectRectCurr:Overlaps(item_rect)
local rect_overlap_prev = bs.BoxSelectRectPrev:Overlaps(item_rect)
if (rect_overlap_curr and not rect_overlap_prev and not selected) or (rect_overlap_prev and not rect_overlap_curr) then
if storage.LastSelectionSize <= 0 and bs.IsStartedSetNavIdOnce then
pressed = true -- First item act as a pressed: code below will emit selection request and set NavId (whatever we emit here will be overridden anyway)
bs.IsStartedSetNavIdOnce = false
else
selected = not selected
ImGui.MultiSelectAddSetRange(ms, selected, 1, item_data, item_data)
end
storage.LastSelectionSize = ImMax(storage.LastSelectionSize + 1, 1)
end
end
end
-- Right-click handling.
-- FIXME-MULTISELECT: Maybe should be moved to Selectable()? Also see #5816, #8200, #9015
if hovered and ImGui.IsMouseClicked(1) and bit32.band(flags, bit32.bor(8 --[[ImGuiMultiSelectFlags.NoAutoSelect]], 131072 --[[ImGuiMultiSelectFlags.NoSelectOnRightClick]])) == 0 then
if g.ActiveId ~= 0 and g.ActiveId ~= id then
ImGui.ClearActiveID()
end
ImGui.SetFocusID(id, window)
if not pressed and not selected then
pressed = true
is_ctrl = false; is_shift = false
end
end
-- Unlike Space, Enter doesn't alter selection (but can still return a press) unless current item is not selected.
-- The later, "unless current item is not select", may become optional? It seems like a better default if Enter doesn't necessarily open something
-- (unlike e.g. Windows explorer). For use case where Enter always open something, we might decide to make this optional?
local enter_pressed = pressed and (g.NavActivateId == id) and bit32.band(g.NavActivateFlags, 1 --[[ImGuiActivateFlags.PreferInput]]) ~= 0
-- Alter selection
if pressed and (not enter_pressed or not selected) then
-- Box-select
local input_source = (g.NavJustMovedToId == id or g.NavActivateId == id) and g.NavInputSource or 1 --[[ImGuiInputSource.Mouse]]
if bit32.band(flags, bit32.bor(64 --[[ImGuiMultiSelectFlags.BoxSelect1d]], 128 --[[ImGuiMultiSelectFlags.BoxSelect2d]])) ~= 0 then
if not g.BoxSelectState.IsActive and not g.BoxSelectState.IsStarting and input_source == 1 --[[ImGuiInputSource.Mouse]] and g.IO.MouseClickedCount[0] == 1 then
BoxSelectPreStartDrag(ms.BoxSelectId, item_data)
end
end
------------------------------------------------------------------------------------------
-- ACTION | Begin | Pressed/Activated | End
------------------------------------------------------------------------------------------
-- Keys Navigated: | Clear | Src=item, Sel=1 SetRange 1
-- Keys Navigated: Ctrl | n/a | n/a
-- Keys Navigated: Shift | n/a | Dst=item, Sel=1, => Clear + SetRange 1
-- Keys Navigated: Ctrl+Shift | n/a | Dst=item, Sel=Src => Clear + SetRange Src-Dst
-- Keys Activated: | n/a | Src=item, Sel=1 => Clear + SetRange 1
-- Keys Activated: Ctrl | n/a | Src=item, Sel=!Sel => SetSange 1
-- Keys Activated: Shift | n/a | Dst=item, Sel=1 => Clear + SetSange 1
------------------------------------------------------------------------------------------
-- Mouse Pressed: | n/a | Src=item, Sel=1, => Clear + SetRange 1
-- Mouse Pressed: Ctrl | n/a | Src=item, Sel=!Sel => SetRange 1
-- Mouse Pressed: Shift | n/a | Dst=item, Sel=1, => Clear + SetRange 1
-- Mouse Pressed: Ctrl+Shift | n/a | Dst=item, Sel=!Sel => SetRange Src-Dst
------------------------------------------------------------------------------------------
if bit32.band(flags, 16 --[[ImGuiMultiSelectFlags.NoAutoClear]]) == 0 then
local request_clear = false
if is_singleselect then
request_clear = true
elseif (input_source == 1 --[[ImGuiInputSource.Mouse]] or g.NavActivateId == id) and not is_ctrl then
if bit32.band(flags, 32 --[[ImGuiMultiSelectFlags.NoAutoClearOnReselect]]) ~= 0 then request_clear = not selected else request_clear = true end
elseif (input_source == 2 --[[ImGuiInputSource.Keyboard]] or input_source == 3 --[[ImGuiInputSource.Gamepad]]) and is_shift and not is_ctrl then
request_clear = true -- With is_shift==false the RequestClear was done in BeginIO, not necessary to do again.
end
if request_clear then
ImGui.MultiSelectAddSetAll(ms, false)
end
end
local range_direction
local range_selected
if is_shift and not is_singleselect then
--IM_ASSERT(storage.HasRangeSrc and storage.HasRangeValue)
if storage.RangeSrcItem == (-1) --[[ImGuiSelectionUserData_Invalid]] then
storage.RangeSrcItem = item_data
end
if bit32.band(flags, 1048576 --[[ImGuiMultiSelectFlags.CheckboxMode_]]) == 0 then
-- Shift+Arrow always select
-- Ctrl+Shift+Arrow copy source selection state (already stored by BeginMultiSelect() in storage.RangeSelected)
if is_ctrl and storage.RangeSelected ~= -1 then range_selected = (storage.RangeSelected ~= 0) else range_selected = true end
else
-- Shift+Arrow copy source selection state
-- Shift+Click always copy from target selection state
if ms.IsKeyboardSetRange then
if storage.RangeSelected ~= -1 then range_selected = (storage.RangeSelected ~= 0) else range_selected = true end
else
range_selected = not selected
end
end
range_direction = ms.RangeSrcPassedBy and 1 or -1
else
-- Ctrl inverts selection, otherwise always select
if bit32.band(flags, 1048576 --[[ImGuiMultiSelectFlags.CheckboxMode_]]) == 0 then
if is_ctrl then selected = not selected else selected = true end
else
selected = not selected
end
storage.RangeSrcItem = item_data
range_selected = selected
range_direction = 1
end
ImGui.MultiSelectAddSetRange(ms, range_selected, range_direction, storage.RangeSrcItem, item_data)
end
-- Update/store the selection state of the Source item (used by Ctrl+Shift, when Source is unselected we perform a range unselect)
if storage.RangeSrcItem == item_data then
storage.RangeSelected = selected and 1 or 0
end
-- Update/store the selection state of focused item
if g.NavId == id then
storage.NavIdItem = item_data
storage.NavIdSelected = selected and 1 or 0
end
if storage.NavIdItem == item_data then
ms.NavIdPassedBy = true
end
return selected, pressed
end
--- @param ms ImGuiMultiSelectTempData
--- @param selected bool
function ImGui.MultiSelectAddSetAll(ms, selected)
local req = MultiSelectScratchRequest -- ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, selected, 0, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid }
req.Type = 1 --[[ImGuiSelectionRequestType.SetAll]]; req.Selected = selected; req.RangeDirection = 0; req.RangeFirstItem = (-1) --[[ImGuiSelectionUserData_Invalid]]; req.RangeLastItem = (-1) --[[ImGuiSelectionUserData_Invalid]]
ms.IO.Requests:resize(0) -- Can always clear previous requests
ms.IO.Requests:push_back(req) -- Add new request
end
--- @param ms ImGuiMultiSelectTempData
--- @param selected bool
--- @param range_dir int
--- @param first_item ImGuiSelectionUserData
--- @param last_item ImGuiSelectionUserData
function ImGui.MultiSelectAddSetRange(ms, selected, range_dir, first_item, last_item)
-- Merge contiguous spans into same request (unless NoRangeSelect is set which guarantees single-item ranges)
-- FIXME-OPT: Disabled on 2026/04/09 as this would break with any form of coarse clipping that we don't know about (e.g. TableNextColumn() return value).
-- The low-hanging fruit would be to know that ImGuiSelectionUserData are sequential indices, in which case we can trivially compare PrevSubmittedItem + RangeDir == FirstItem.
-- User can always perform this merge if required.
-- (#if 0 block not ported)
local req = MultiSelectScratchRequest -- ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, (ImS8)range_dir, (range_dir > 0) ? first_item : last_item, (range_dir > 0) ? last_item : first_item }
req.Type = 2 --[[ImGuiSelectionRequestType.SetRange]]; req.Selected = selected; req.RangeDirection = range_dir
if range_dir > 0 then
req.RangeFirstItem = first_item; req.RangeLastItem = last_item
else
req.RangeFirstItem = last_item; req.RangeLastItem = first_item
end
ms.IO.Requests:push_back(req) -- Add new request
end
--- @param storage ImGuiMultiSelectState
function ImGui.DebugNodeMultiSelectState(storage)
local is_active = (storage.LastFrameActive >= ImGui.GetFrameCount() - 2) -- Note that fully clipped early out scrolling tables will appear as inactive here.
if not is_active then ImGui.PushStyleColor(0 --[[ImGuiCol.Text]], ImGui.GetStyleColorVec4(1 --[[ImGuiCol.TextDisabled]])) end
local open = ImGui.TreeNode(storage.ID, "MultiSelect 0x%08X in '%s'%s", storage.ID, storage.Window and storage.Window.Name or "N/A", is_active and "" or " *Inactive*")
if not is_active then ImGui.PopStyleColor() end
if not open then
return
end
-- [Port] ImGuiSelectionUserData may be any Lua value: printed with tostring() instead of %lld (0x%llX)
ImGui.Text("RangeSrcItem = %s, RangeSelected = %d", tostring(storage.RangeSrcItem), storage.RangeSelected)
ImGui.Text("NavIdItem = %s, NavIdSelected = %d", tostring(storage.NavIdItem), storage.NavIdSelected)
ImGui.Text("LastSelectionSize = %d", storage.LastSelectionSize) -- Provided by user
ImGui.TreePop()
end
-------------------------------------------------------------------------------
-- [SECTION] Widgets: Multi-Select helpers
-------------------------------------------------------------------------------
-- - ImGuiSelectionBasicStorage
-- - ImGuiSelectionExternalStorage
-------------------------------------------------------------------------------
-- [Port] Storage representation:
-- - Upstream _Storage is an ImGuiStorage (vector of {ImGuiID key, int val_i} pairs sorted by key, val_i = selection order
-- or 0 when unselected). Here _Storage is a plain Lua table: [id] = selection order (always > 0); unselected ids are
-- removed (nil) instead of being kept with val_i == 0. Contains()/SetItemSelected() are O(1).
-- - Iteration: `local it, id = nil, 0; local has_more; while true do has_more, it, id = selection:GetNextSelectedItem(it) if not has_more then break end ... end`
-- (C++: void* it = NULL; ImGuiID id; while (selection.GetNextSelectedItem(&it, &id)) {...}). The opaque iterator is an
-- integer position into _IterKeys, a snapshot of the selected ids sorted by key (or by selection order when
-- PreserveOrder is set), rebuilt when an iteration starts (it == nil). Ids unselected during an iteration are skipped.
-- A storage supports one iteration at a time (starting a new one rebuilds the shared snapshot).
-- - Keys returned by AdapterIndexToStorageId must be mutually comparable with '<' (numbers by default) for the sort.
--- @class ImGuiSelectionBasicStorage
--- @field Size int # Number of selected items, maintained by this helper.
--- @field PreserveOrder bool # = false // GetNextSelectedItem() will return ordered selection (currently implemented by two additional sorts of selection. Could be improved)
--- @field UserData any # = nil // User data for use by adapter function // e.g. selection.UserData = my_items
--- @field AdapterIndexToStorageId fun(self: ImGuiSelectionBasicStorage, idx: int): ImGuiID # e.g. selection.AdapterIndexToStorageId = function(self, idx) return self.UserData[idx + 1].ID end
--- @field _SelectionOrder int # [Internal] Increasing counter to store selection order
--- @field _Storage table # [Internal] Selection set. Think of this as similar to e.g. std::set<ImGuiID>. Prefer not accessing directly: iterate with GetNextSelectedItem().
MT.ImGuiSelectionBasicStorage = {}
MT.ImGuiSelectionBasicStorage.__index = MT.ImGuiSelectionBasicStorage
local function ImGuiSelectionBasicStorage_DefaultAdapterIndexToStorageId(_, idx) return idx end
--- @return ImGuiSelectionBasicStorage
function ImGuiSelectionBasicStorage()
local this = setmetatable({}, MT.ImGuiSelectionBasicStorage)
this.Size = 0
this.PreserveOrder = false
this.UserData = nil
this.AdapterIndexToStorageId = ImGuiSelectionBasicStorage_DefaultAdapterIndexToStorageId -- [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }
this._SelectionOrder = 1 -- Always >0
this._Storage = {}
this._IterKeys = {} -- [Port] GetNextSelectedItem() snapshot
return this
end
function MT.ImGuiSelectionBasicStorage:Clear()
self.Size = 0
self._SelectionOrder = 1 -- Always >0
table.clear(self._Storage)
end
--- @param r ImGuiSelectionBasicStorage
function MT.ImGuiSelectionBasicStorage:Swap(r)
self.Size, r.Size = r.Size, self.Size
self._SelectionOrder, r._SelectionOrder = r._SelectionOrder, self._SelectionOrder
self._Storage, r._Storage = r._Storage, self._Storage
end
--- @param id ImGuiID
--- @return bool
function MT.ImGuiSelectionBasicStorage:Contains(id)
return self._Storage[id] ~= nil
end
local PairComparerStorage = nil
local function PairComparerByValueInt(lhs, rhs)
return PairComparerStorage[lhs] < PairComparerStorage[rhs]
end
-- GetNextSelectedItem() is an abstraction allowing us to change our underlying actual storage system without impacting user.
-- (e.g. store unselected vs compact down, compact down on demand, use raw ImVector<ImGuiID> instead of ImGuiStorage...)
-- [Port] `has_more, opaque_it, out_id = selection:GetNextSelectedItem(opaque_it)`: pass nil to start, then the returned opaque_it.
--- @param opaque_it any
--- @return bool, any, ImGuiID
function MT.ImGuiSelectionBasicStorage:GetNextSelectedItem(opaque_it)
local storage = self._Storage
local keys = self._IterKeys
local it = opaque_it
if it == nil then
table.clear(keys)
local n = 0
for id in pairs(storage) do n = n + 1; keys[n] = id end
if self.PreserveOrder then
PairComparerStorage = storage
table.sort(keys, PairComparerByValueInt) -- ~ImGuiStorage::BuildSortByValueInt()
PairComparerStorage = nil
else
table.sort(keys) -- ImGuiStorage is sorted by key
end
it = 1
end
local it_end = #keys + 1
assert(it >= 1 and it <= it_end)
while it < it_end and storage[keys[it]] == nil do
it = it + 1
end
local has_more = (it ~= it_end)
if has_more then
return true, it + 1, keys[it]
end
return false, it, 0
end
--- @param id ImGuiID
--- @param selected bool
function MT.ImGuiSelectionBasicStorage:SetItemSelected(id, selected)
local storage = self._Storage
local p_int = storage[id]
if selected and p_int == nil then storage[id] = self._SelectionOrder; self._SelectionOrder = self._SelectionOrder + 1; self.Size = self.Size + 1
elseif not selected and p_int ~= nil then storage[id] = nil; self.Size = self.Size - 1 end
end
--- @param idx int
--- @return ImGuiID
function MT.ImGuiSelectionBasicStorage:GetStorageIdFromIndex(idx) return self.AdapterIndexToStorageId(self, idx) end -- Convert index to item id based on provided adapter.
-- Optimized for batch edits (with same value of 'selected')
-- [Port] With a hash table there is nothing to defer: insert/remove in place. size_before_amends is unused.
local function ImGuiSelectionBasicStorage_BatchSetItemSelected(selection, id, selected, size_before_amends, selection_order)
local storage = selection._Storage
local is_contained = storage[id] ~= nil
if selected == is_contained then
return
end
if selected then
storage[id] = selection_order
else
storage[id] = nil
end
selection.Size = selection.Size + (selected and 1 or -1)
end
-- [Port] Upstream sorts the ImGuiStorage vector here (BuildSortByKey). The Lua table needs no sorting: GetNextSelectedItem() sorts its snapshot.
local function ImGuiSelectionBasicStorage_BatchFinish(selection, selected, size_before_amends)
end
-- Apply requests coming from BeginMultiSelect() and EndMultiSelect().
-- - Enable 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen.
-- - Honoring SetRange requests requires that you can iterate/interpolate between RangeFirstItem and RangeLastItem.
-- - In this demo we often submit indices to SetNextItemSelectionUserData() + store the same indices in persistent selection.
-- - Your code may do differently. If you store pointers or objects ID in ImGuiSelectionUserData you may need to perform
-- a lookup in order to have some way to iterate/interpolate between two items.
-- - A full-featured application is likely to allow search/filtering which is likely to lead to using indices
-- and constructing a view index <> object id/ptr data structure anyway.
-- WHEN YOUR APPLICATION SETTLES ON A CHOICE, YOU WILL PROBABLY PREFER TO GET RID OF THIS UNNECESSARY 'ImGuiSelectionBasicStorage' INDIRECTION LOGIC.
-- Notice that with the simplest adapter (using indices everywhere), all functions return their parameters.
-- The most simple implementation (using indices everywhere) would look like:
-- for _, req in ms_io.Requests:iter() do
-- if req.Type == ImGuiSelectionRequestType.SetAll then Clear(); if req.Selected then for n = 0, items_count - 1 do SetItemSelected(n, true) end end end
-- if req.Type == ImGuiSelectionRequestType.SetRange then for n = req.RangeFirstItem, req.RangeLastItem do SetItemSelected(n, req.Selected) end end
-- end
--- @param ms_io ImGuiMultiSelectIO
function MT.ImGuiSelectionBasicStorage:ApplyRequests(ms_io)
-- For convenience we obtain ItemsCount as passed to BeginMultiSelect(), which is optional.
-- It makes sense when using ImGuiSelectionBasicStorage to simply pass your items count to BeginMultiSelect().
-- Other scheme may handle SetAll differently.
assert(ms_io.ItemsCount ~= -1, "Missing value for items_count in BeginMultiSelect() call!")
assert(self.AdapterIndexToStorageId ~= nil)
-- This is optimized/specialized to cope with very large selections (e.g. 100k+ items)
-- - A simpler version could call SetItemSelected() directly instead of ImGuiSelectionBasicStorage_BatchSetItemSelected() + ImGuiSelectionBasicStorage_BatchFinish().
-- - Optimized select can append unsorted, then sort in a second pass. Optimized unselect can clear in-place then compact in a second pass.
-- - A more optimal version wouldn't even use ImGuiStorage but directly a ImVector<ImGuiID> to reduce bandwidth, but this is a reasonable trade off to reuse code.
-- - There are many ways this could be better optimized. The worse case scenario being: using BoxSelect2d in a grid, box-select scrolling down while wiggling
-- left and right: it affects coarse clipping + can emit multiple SetRange with 1 item each.
-- FIXME-OPT: For each block of consecutive SetRange request:
-- - add all requests to a sorted list, store ID, selected, offset in ImGuiStorage.
-- - rewrite sorted storage a single time.
for _, req in ms_io.Requests:iter() do
if req.Type == 1 --[[ImGuiSelectionRequestType.SetAll]] then
self:Clear()
if req.Selected then
local size_before_amends = self.Size -- [Port] _Storage.Data.Size (0 after Clear())
for idx = 0, ms_io.ItemsCount - 1 do
ImGuiSelectionBasicStorage_BatchSetItemSelected(self, self:GetStorageIdFromIndex(idx), req.Selected, size_before_amends, self._SelectionOrder)
self._SelectionOrder = self._SelectionOrder + 1
end
ImGuiSelectionBasicStorage_BatchFinish(self, req.Selected, size_before_amends)
end
elseif req.Type == 2 --[[ImGuiSelectionRequestType.SetRange]] then
local selection_changes = req.RangeLastItem - req.RangeFirstItem + 1
--local g = GImGui; IMGUI_DEBUG_LOG_SELECTION("Req %d/%d: set %d to %d\n", ms_io.Requests:index_from_ptr(req), ms_io.Requests.Size, selection_changes, req.Selected)
if selection_changes == 1 or (selection_changes < math.floor(self.Size / 100)) then
-- Multiple sorted insertion + copy likely to be faster.
-- Technically we could do a single copy with a little more work (sort sequential SetRange requests)
for idx = req.RangeFirstItem, req.RangeLastItem do
self:SetItemSelected(self:GetStorageIdFromIndex(idx), req.Selected)
end
else
-- Append insertion + single sort likely be faster.
-- Use req.RangeDirection to set order field so that Shift+Clicking from 1 to 5 is different than Shift+Clicking from 5 to 1
local size_before_amends = self.Size
local selection_order = self._SelectionOrder + ((req.RangeDirection < 0) and selection_changes - 1 or 0)
for idx = req.RangeFirstItem, req.RangeLastItem do
ImGuiSelectionBasicStorage_BatchSetItemSelected(self, self:GetStorageIdFromIndex(idx), req.Selected, size_before_amends, selection_order)
selection_order = selection_order + req.RangeDirection
end
if req.Selected then
self._SelectionOrder = self._SelectionOrder + selection_changes
end
ImGuiSelectionBasicStorage_BatchFinish(self, req.Selected, size_before_amends)
end
end
end
end
-------------------------------------------------------------------------------
-- Optional helper to apply multi-selection requests to existing randomly accessible storage.
-- Convenient if you want to quickly wire multi-select API on e.g. an array of bool or items storing their own selection state.
--- @class ImGuiSelectionExternalStorage
--- @field UserData any # User data for use by adapter function // e.g. selection.UserData = my_items
--- @field AdapterSetItemSelected fun(self: ImGuiSelectionExternalStorage, idx: int, selected: bool) # e.g. AdapterSetItemSelected = function(self, idx, selected) self.UserData[idx + 1].Selected = selected end
MT.ImGuiSelectionExternalStorage = {}
MT.ImGuiSelectionExternalStorage.__index = MT.ImGuiSelectionExternalStorage
--- @return ImGuiSelectionExternalStorage
function ImGuiSelectionExternalStorage()
local this = setmetatable({}, MT.ImGuiSelectionExternalStorage)
this.UserData = nil
this.AdapterSetItemSelected = nil
return this
end
-- Apply requests coming from BeginMultiSelect() and EndMultiSelect().
-- We also pull 'ms_io.ItemsCount' as passed for BeginMultiSelect() for consistency with ImGuiSelectionBasicStorage
-- This makes no assumption about underlying storage.
--- @param ms_io ImGuiMultiSelectIO
function MT.ImGuiSelectionExternalStorage:ApplyRequests(ms_io)
assert(self.AdapterSetItemSelected)
for _, req in ms_io.Requests:iter() do
if req.Type == 1 --[[ImGuiSelectionRequestType.SetAll]] then
for idx = 0, ms_io.ItemsCount - 1 do
self.AdapterSetItemSelected(self, idx, req.Selected)
end
end
if req.Type == 2 --[[ImGuiSelectionRequestType.SetRange]] then
for idx = req.RangeFirstItem, req.RangeLastItem do
self.AdapterSetItemSelected(self, idx, req.Selected)
end
end
end
end
end -- do (Typing-Select, Box-Select, Multi-Select)
-------------------------------------------------------------------------------
-- [SECTION] Widgets: ListBox
-------------------------------------------------------------------------------
-- - BeginListBox()
-- - EndListBox()
-- - ListBox()
-------------------------------------------------------------------------------
-- This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags.FrameStyle flag for stylistic changes + displaying a label.
-- This handle some subtleties with capturing info from the label.
-- If you don't need a label you can pretty much directly use ImGui.BeginChild() with ImGuiChildFlags.FrameStyle.
-- Tip: To have a list filling the entire window width, use size.x = -FLT_MIN and pass an non-visible label e.g. "##empty"
-- Tip: If your vertical size is calculated from an item count (e.g. 10 * item_height) consider adding a fractional part to facilitate seeing scrolling boundaries (e.g. 10.5f * item_height).
--- @param label string
--- @param size_arg? ImVec2
function ImGui.BeginListBox(label, size_arg)
if size_arg == nil then size_arg = ImVec2(0, 0) end
local g = GImGui
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
local style = g.Style
local id = ImGui.GetID(label)
local label_end = ImGui.FindRenderedTextEnd(label)
local label_size = ImGui.CalcTextSize(label, label_end, false)
-- Size default to hold ~7.25 items.
-- Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
local size = ImTrunc(ImGui.CalcItemSize(size_arg, ImGui.CalcItemWidth(), ImGui.GetTextLineHeightWithSpacing() * 7.25 + style.FramePadding.y * 2.0))
local frame_size = ImVec2(size.x, ImMax(size.y, label_size.y))
local frame_bb = ImRect(window.DC.CursorPos, window.DC.CursorPos + frame_size)
local bb = ImRect(frame_bb.Min, frame_bb.Max + ImVec2((label_size.x > 0.0) and (style.ItemInnerSpacing.x + label_size.x) or 0.0, 0.0))
g.NextItemData:ClearFlags()
if not ImGui.IsRectVisible(bb.Min, bb.Max) then
ImGui.ItemSize(bb:GetSize(), style.FramePadding.y)
ImGui.ItemAdd(bb, 0, frame_bb)
g.NextWindowData:ClearFlags() -- We behave like Begin() and need to consume those values
return false
end
-- FIXME-OPT: We could omit the BeginGroup() if label_size.x == 0.0f but would need to omit the EndGroup() as well.
ImGui.BeginGroup()
if label_size.x > 0.0 then
local label_pos = ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y)
ImGui.RenderText(label_pos, label, 1, label_end, false)
ImVec2_Copy(window.DC.CursorMaxPos, ImMax(window.DC.CursorMaxPos, label_pos + label_size))
ImGui.AlignTextToFramePadding()
end
ImGui.BeginChild(id, frame_bb:GetSize(), 128 --[[ImGuiChildFlags.FrameStyle]])
return true
end
function ImGui.EndListBox()
local g = GImGui
local window = g.CurrentWindow
assert(bit32.band(window.Flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]]) ~= 0, "Mismatched BeginListBox/EndListBox calls. Did you test the return value of BeginListBox?")
ImGui.EndChild()
ImGui.EndGroup() -- This is only required to be able to do IsItemXXX query on the whole ListBox including label
end
-- This is merely a helper around BeginListBox(), EndListBox().
-- Considering using those directly to submit custom data or store selection differently.
-- Overloads: (label, current_item, items[], items_count?, height_in_items?) or (label, current_item, getter, user_data, items_count, height_in_items?)
--- @param current_item int # 0-based
--- @return int current_item
--- @return bool value_changed
function ImGui.ListBox(label, current_item, a, b, c, d)
local g = GImGui
local getter, user_data, items_count, height_in_items = Items_ResolveArgs(a, b, c, d)
if height_in_items == nil then height_in_items = -1 end
-- Calculate size from "height_in_items"
if height_in_items < 0 then
height_in_items = ImMin(items_count, 7)
end
local height_in_items_f = height_in_items + 0.25
local size = ImVec2(0.0, ImTrunc(ImGui.GetTextLineHeightWithSpacing() * height_in_items_f + g.Style.FramePadding.y * 2.0))
if not ImGui.BeginListBox(label, size) then
return current_item, false
end
-- Assume all items have even height (= 1 line of text). If you need items of different height,
-- you can create a custom version of ListBox() in your code without using the clipper.
local value_changed = false
local clipper = ImGuiListClipper()
clipper:Begin(items_count, ImGui.GetTextLineHeightWithSpacing()) -- We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to.
clipper:IncludeItemByIndex(current_item)
while clipper:Step() do
for i = clipper.DisplayStart, clipper.DisplayEnd - 1 do
local item_text = getter(user_data, i)
if item_text == nil then
item_text = "*Unknown item*"
end
ImGui.PushID(i)
local item_selected = (i == current_item)
if ImGui.Selectable(item_text, item_selected) then
current_item = i
value_changed = true
end
if item_selected then
ImGui.SetItemDefaultFocus()
end
ImGui.PopID()
end
end
ImGui.EndListBox()
if value_changed then
ImGui.MarkItemEdited(g.LastItemData.ID)
end
return current_item, value_changed
end
-------------------------------------------------------------------------------
-- [SECTION] Widgets: PlotLines, PlotHistogram
-------------------------------------------------------------------------------
-- - PlotEx() [Internal]
-- - PlotLines()
-- - PlotHistogram()
-------------------------------------------------------------------------------
-- Plot/Graph widgets are not very good.
-- Consider using ImPlot (https://github.com/epezent/implot) which is much better!
-------------------------------------------------------------------------------
--- @param plot_type ImGuiPlotType
--- @param label string
--- @param values_getter fun(data?: table, idx: int): float
--- @param data? table # 1-based table
--- @param values_count int
--- @param values_offset int
--- @param overlay_text? string
--- @param scale_min float
--- @param scale_max float
--- @param size_arg ImVec2
--- @return int
function ImGui.PlotEx(plot_type, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, size_arg)
local g = GImGui
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return -1
end
local style = g.Style
local id = window:GetID(label)
local label_end = ImGui.FindRenderedTextEnd(label)
local label_size = ImGui.CalcTextSize(label, label_end, false)
local frame_size = ImGui.CalcItemSize(size_arg, ImGui.CalcItemWidth(), label_size.y + style.FramePadding.y * 2.0)
local frame_bb = ImRect(window.DC.CursorPos, window.DC.CursorPos + frame_size)
local inner_bb = ImRect(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding)
local total_bb = ImRect(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0 and style.ItemInnerSpacing.x + label_size.x or 0.0, 0))
ImGui.ItemSize(total_bb, style.FramePadding.y)
if not ImGui.ItemAdd(total_bb, id, frame_bb, 2 --[[ImGuiItemFlags.NoNav]]) then
return -1
end
local _, hovered, _ = ImGui.ButtonBehavior(frame_bb, id)
-- Determine scale from values if not specified
if scale_min == 3.4028234663853e+38 --[[FLT_MAX]] or scale_max == 3.4028234663853e+38 --[[FLT_MAX]] then
local v_min = 3.4028234663853e+38 --[[FLT_MAX]]
local v_max = -3.4028234663853e+38 --[[FLT_MAX]]
for i = 1, values_count do
local v = values_getter(data, i) -- NaN isn't checked here
v_min = ImMin(v_min, v)
v_max = ImMax(v_max, v)
end
if scale_min == 3.4028234663853e+38 --[[FLT_MAX]] then
scale_min = v_min
end
if scale_max == 3.4028234663853e+38 --[[FLT_MAX]] then
scale_max = v_max
end
end
ImGui.RenderFrame(frame_bb.Min, frame_bb.Max, ImGui.GetColorU32(7 --[[ImGuiCol.FrameBg]]), true, style.FrameRounding)
local values_count_min = (plot_type == 0 --[[ImGuiPlotType.Lines]]) and 2 or 1
local idx_hovered = -1
if values_count >= values_count_min then
local res_w = ImMin(math.floor(frame_size.x), values_count) + ((plot_type == 0 --[[ImGuiPlotType.Lines]]) and -1 or 0)
local item_count = values_count + ((plot_type == 0 --[[ImGuiPlotType.Lines]]) and -1 or 0)
-- Tooltip on hover
if hovered and inner_bb:Contains(g.IO.MousePos) then
local t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0, 0.999)
local v_idx = math.floor(t * item_count) + 1
assert(v_idx >= 1 and v_idx <= values_count)
local v0 = values_getter(data, (v_idx - 1 + values_offset) % values_count + 1)
local v1 = values_getter(data, (v_idx - 1 + 1 + values_offset) % values_count + 1)
if plot_type == 0 --[[ImGuiPlotType.Lines]] then
ImGui.SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx + 1, v1)
elseif plot_type == 1 --[[ImGuiPlotType.Histogram]] then
ImGui.SetTooltip("%d: %8.4g", v_idx, v0)
end
idx_hovered = v_idx
end
local t_step = 1.0 / res_w
local inv_scale = (scale_min == scale_max) and 0.0 or (1.0 / (scale_max - scale_min))
local v0 = values_getter(data, (0 + values_offset) % values_count + 1)
local t0 = 0.0
local tp0 = ImVec2(t0, 1.0 - ImSaturate((v0 - scale_min) * inv_scale)) -- Point in the normalized space of our target rectangle
local histogram_zero_line_t = (scale_min * scale_max < 0.0) and (1 + scale_min * inv_scale) or (scale_min < 0.0 and 0.0 or 1.0) -- Where does the zero line stands
local col_base = ImGui.GetColorU32((plot_type == 0 --[[ImGuiPlotType.Lines]]) and 44 --[[ImGuiCol.PlotLines]] or 46 --[[ImGuiCol.PlotHistogram]])
local col_hovered = ImGui.GetColorU32((plot_type == 0 --[[ImGuiPlotType.Lines]]) and 45 --[[ImGuiCol.PlotLinesHovered]] or 47 --[[ImGuiCol.PlotHistogramHovered]])
for _ = 0, res_w - 1 do
local t1 = t0 + t_step
local v1_idx = math.floor(t0 * item_count + 0.5) + 1
assert(v1_idx >= 1 and v1_idx <= values_count)
local v1 = values_getter(data, (v1_idx - 1 + values_offset + 1) % values_count + 1)
local tp1 = ImVec2(t1, 1.0 - ImSaturate((v1 - scale_min) * inv_scale))
-- NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU.
local pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0)
local pos1
if plot_type == 0 --[[ImGuiPlotType.Lines]] then
pos1 = ImLerp(inner_bb.Min, inner_bb.Max, tp1)
else
pos1 = ImLerp(inner_bb.Min, inner_bb.Max, ImVec2(tp1.x, histogram_zero_line_t))
end
if plot_type == 0 --[[ImGuiPlotType.Lines]] then
window.DrawList:AddLine(pos0, pos1, idx_hovered == v1_idx and col_hovered or col_base)
elseif plot_type == 1 --[[ImGuiPlotType.Histogram]] then
if pos1.x >= pos0.x + 2.0 then
pos1.x = pos1.x - 1.0
end
window.DrawList:AddRectFilled(pos0, pos1, idx_hovered == v1_idx and col_hovered or col_base)
end
t0 = t1
tp0 = tp1
end
end
-- Text overlay
if overlay_text then
ImGui.RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, nil, nil, ImVec2(0.5, 0.0))
end
if label_size.x > 0.0 then
ImGui.RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label, 1, label_end, false)
end
-- Return hovered index or -1 if none are hovered.
-- This is currently not exposed in the public API because we need a larger redesign of the whole thing, but in the short-term we are making it available in PlotEx().
return idx_hovered
end
--- @class ImGuiPlotArrayGetterData
--- @field Values float[]
--- @field Stride int
--- @param values float[]
--- @param stride int
--- @return ImGuiPlotArrayGetterData
--- @nodiscard
function ImGuiPlotArrayGetterData(values, stride)
return {
Values = values,
Stride = stride
}
end
--- @param data ImGuiPlotArrayGetterData
--- @param idx int
--- @return float
--- @package
local function Plot_ArrayGetter(data, idx)
return data.Values[idx * data.Stride]
end
--- @param label string
--- @param values_or_getter table|fun(data:table, i:int) # 1-based table or a function
--- @param data? table # 1-based table
--- @param values_count int
--- @param values_offset? int # Defaults to 0
--- @param overlay_text? string
--- @param scale_min? float # Defaults to FLT_MAX
--- @param scale_max? float # Defaults to FLT_MAX
--- @param graph_size? ImVec2 # Defaults to ImVec2(0, 0)
--- @param stride? int # Defaults to 1
function ImGui.PlotLines(label, values_or_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size, stride)
if values_offset == nil then values_offset = 0 end
if scale_min == nil then scale_min = 3.4028234663853e+38 --[[FLT_MAX]] end
if scale_max == nil then scale_max = 3.4028234663853e+38 --[[FLT_MAX]] end
if graph_size == nil then graph_size = ImVec2(0, 0) end
if stride == nil then stride = 1 end
if type(values_or_getter) == "function" then
ImGui.PlotEx(0 --[[ImGuiPlotType.Lines]], label, values_or_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size)
else
data = ImGuiPlotArrayGetterData(values_or_getter, stride)
ImGui.PlotEx(0 --[[ImGuiPlotType.Lines]], label, Plot_ArrayGetter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size)
end
end
-- Plot Histogram (the data provided _is_ histogram data. it doesn't compute the histogram of your data)
--- @param label string
--- @param values_or_getter table|fun(data:table, i:int) # 1-based table or a function
--- @param data? table # 1-based table
--- @param values_count int
--- @param values_offset? int # Defaults to 0
--- @param overlay_text? string
--- @param scale_min? float # Defaults to FLT_MAX
--- @param scale_max? float # Defaults to FLT_MAX
--- @param graph_size? ImVec2 # Defaults to ImVec2(0, 0)
--- @param stride? int # Defaults to 1
function ImGui.PlotHistogram(label, values_or_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size, stride)
if values_offset == nil then values_offset = 0 end
if scale_min == nil then scale_min = 3.4028234663853e+38 --[[FLT_MAX]] end
if scale_max == nil then scale_max = 3.4028234663853e+38 --[[FLT_MAX]] end
if graph_size == nil then graph_size = ImVec2(0, 0) end
if stride == nil then stride = 1 end
if type(values_or_getter) == "function" then
ImGui.PlotEx(1 --[[ImGuiPlotType.Histogram]], label, values_or_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size)
else
data = ImGuiPlotArrayGetterData(values_or_getter, stride)
ImGui.PlotEx(1 --[[ImGuiPlotType.Histogram]], label, Plot_ArrayGetter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size)
end
end
-------------------------------------------------------------------------------
-- [SECTION] MenuItem, BeginMenu, EndMenu, etc.
-------------------------------------------------------------------------------
-- - ImGuiMenuColumns [Internal]
-- - BeginMenuBar()
-- - EndMenuBar()
-- - BeginMainMenuBar()
-- - EndMainMenuBar()
-- - BeginMenu()
-- - EndMenu()
-- - MenuItemEx() [Internal]
-- - MenuItem()
-------------------------------------------------------------------------------
-- Helpers for internal use
--- @param spacing float
--- @param window_reappearing bool
function MT.ImGuiMenuColumns:Update(spacing, window_reappearing)
if window_reappearing then
for i = 1, #self.Widths do self.Widths[i] = 0 end
end
self.Spacing = spacing
self:CalcNextTotalWidth(true)
for i = 1, #self.Widths do self.Widths[i] = 0 end
self.TotalWidth = self.NextTotalWidth
self.NextTotalWidth = 0
end
--- @param update_offsets bool
function MT.ImGuiMenuColumns:CalcNextTotalWidth(update_offsets)
local offset = 0
local want_spacing = false
for i = 1, #self.Widths do
local width = self.Widths[i]
if want_spacing and width > 0 then
offset = offset + self.Spacing
end
want_spacing = want_spacing or (width > 0)
if update_offsets then
if i == 2 then self.OffsetLabel = offset end
if i == 3 then self.OffsetShortcut = offset end
if i == 4 then self.OffsetMark = offset end
end
offset = offset + width
end
self.NextTotalWidth = offset
end
--- @param w_icon float
--- @param w_label float
--- @param w_shortcut float
--- @param w_mark float
function MT.ImGuiMenuColumns:DeclColumns(w_icon, w_label, w_shortcut, w_mark)
self.Widths[1] = ImMax(self.Widths[1], (ImU16)(w_icon))
self.Widths[2] = ImMax(self.Widths[2], (ImU16)(w_label))
self.Widths[3] = ImMax(self.Widths[3], (ImU16)(w_shortcut))
self.Widths[4] = ImMax(self.Widths[4], (ImU16)(w_mark))
self:CalcNextTotalWidth(false)
return ImMax(self.TotalWidth, self.NextTotalWidth)
end
-- FIXME: Provided a rectangle perhaps e.g. a BeginMenuBarEx() could be used anywhere..
-- Currently the main responsibility of this function being to setup clip-rect + horizontal layout + menu navigation layer.
-- Ideally we also want this to be responsible for claiming space out of the main window scrolling rectangle, in which case ImGuiWindowFlags.MenuBar will become unnecessary.
-- Then later the same system could be used for multiple menu-bars, scrollbars, side-bars.
function ImGui.BeginMenuBar()
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
if bit32.band(window.Flags, 1024 --[[ImGuiWindowFlags.MenuBar]]) == 0 then
return false
end
assert(not window.DC.MenuBarAppending)
ImGui.BeginGroup() -- Backup position on layer 0 // FIXME: Misleading to use a group for that backup/restore
ImGui.PushID("##MenuBar")
-- We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect.
-- We remove 1 worth of rounding to Max.x to that text in long menus and small windows don't tend to display over the lower-right rounded area, which looks particularly glitchy.
local border_top = ImMax(IM_ROUND(window.WindowBorderSize * 0.5 - window.TitleBarHeight), 0.0)
local border_half = IM_ROUND(window.WindowBorderSize * 0.5)
local bar_rect = window:MenuBarRect()
local clip_rect = ImRect(ImFloor(bar_rect.Min.x + border_half), ImFloor(bar_rect.Min.y + border_top), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - ImMax(window.WindowRounding, border_half))), ImFloor(bar_rect.Max.y))
clip_rect:ClipWith(window.OuterRectClipped)
ImGui.PushClipRect(clip_rect.Min, clip_rect.Max, false)
-- We overwrite CursorMaxPos because BeginGroup sets it to CursorPos (essentially the .EmitItem hack in EndMenuBar() would need something analogous here, maybe a BeginGroupEx() with flags).
ImVec2_Copy(window.DC.CursorPos, ImVec2(bar_rect.Min.x + window.DC.MenuBarOffset.x, bar_rect.Min.y + window.DC.MenuBarOffset.y))
ImVec2_Copy(window.DC.CursorMaxPos, window.DC.CursorPos)
window.DC.LayoutType = 0 --[[ImGuiLayoutType.Horizontal]]
window.DC.IsSameLine = false
window.DC.NavLayerCurrent = 1 --[[ImGuiNavLayer.Menu]]
window.DC.MenuBarAppending = true
ImGui.AlignTextToFramePadding()
return true
end
function ImGui.EndMenuBar()
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
local g = GImGui
assert(bit32.band(window.Flags, 1024 --[[ImGuiWindowFlags.MenuBar]]) ~= 0)
assert(window.DC.MenuBarAppending)
-- Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings.
if ImGui.NavMoveRequestButNoResultYet() and (g.NavMoveDir == 0 --[[ImGuiDir.Left]] or g.NavMoveDir == 1 --[[ImGuiDir.Right]]) and bit32.band(g.NavWindow.Flags, 268435456 --[[ImGuiWindowFlags.ChildMenu]]) ~= 0 then
-- Try to find out if the request is for one of our child menu
local nav_earliest_child = g.NavWindow
while nav_earliest_child.ParentWindow and bit32.band(nav_earliest_child.ParentWindow.Flags, 268435456 --[[ImGuiWindowFlags.ChildMenu]]) ~= 0 do
nav_earliest_child = nav_earliest_child.ParentWindow
end
if nav_earliest_child.ParentWindow == window and nav_earliest_child.DC.ParentLayoutType == 0 --[[ImGuiLayoutType.Horizontal]] and bit32.band(g.NavMoveFlags, 128 --[[ImGuiNavMoveFlags.Forwarded]]) == 0 then
-- To do so we claim focus back, restore NavId and then process the movement request for yet another frame.
-- This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth bothering)
local layer = 1 --[[ImGuiNavLayer.Menu]]
assert(bit32.band(window.DC.NavLayersActiveMaskNext, bit32.lshift(1, layer)) ~= 0) -- Sanity check (FIXME: Seems unnecessary)
ImGui.FocusWindow(window)
ImGui.SetNavID(window.NavLastIds[layer], layer, 0, window.NavRectRel[layer])
-- FIXME-NAV: How to deal with this when not using g.IO.ConfigNavCursorVisibleAuto?
if g.NavCursorVisible then
g.NavCursorVisible = false -- Hide nav cursor for the current frame so we don't see the intermediary selection. Will be set again
g.NavCursorHideFrames = 2
end
g.NavHighlightItemUnderNav = true
g.NavMousePosDirty = true
ImGui.NavMoveRequestForward(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags) -- Repeat
end
else
ImGui.NavMoveRequestTryWrapping(window, 4 --[[ImGuiNavMoveFlags.WrapX]])
end
ImGui.PopClipRect()
ImGui.PopID()
window.DC.MenuBarOffset.x = window.DC.CursorPos.x - window.Pos.x -- Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos.
-- FIXME: Extremely confusing, cleanup by (a) working on WorkRect stack system (b) not using a Group confusingly here.
local group_data = g.GroupStack:back()
group_data.EmitItem = false
local restore_cursor_max_pos = ImVec2()
ImVec2_Copy(restore_cursor_max_pos, group_data.BackupCursorMaxPos)
window.DC.IdealMaxPos.x = ImMax(window.DC.IdealMaxPos.x, window.DC.CursorMaxPos.x - window.Scroll.x) -- Convert ideal extents for scrolling layer equivalent.
ImGui.EndGroup() -- Restore position on layer 0 // FIXME: Misleading to use a group for that backup/restore
window.DC.LayoutType = 1 --[[ImGuiLayoutType.Vertical]]
window.DC.IsSameLine = false
window.DC.NavLayerCurrent = 0 --[[ImGuiNavLayer.Main]]
window.DC.MenuBarAppending = false
ImVec2_Copy(window.DC.CursorMaxPos, restore_cursor_max_pos)
end
-- Important: calling order matters!
-- FIXME: Somehow overlapping with docking tech.
-- FIXME: The "rect-cut" aspect of this could be formalized into a lower-level helper (rect-cut: https://halt.software/dead-simple-layouts)
--- @param name string
--- @param viewport_p? ImGuiViewport
--- @param dir ImGuiDir
--- @param axis_size float
--- @param window_flags ImGuiWindowFlags
function ImGui.BeginViewportSideBar(name, viewport_p, dir, axis_size, window_flags)
assert(dir ~= (-1) --[[ImGuiDir.None]])
local bar_window = ImGui.FindWindowByName(name)
local viewport = viewport_p or ImGui.GetMainViewport()
if bar_window == nil or bar_window.BeginCount == 0 then
-- Calculate and set window size/position
local avail_rect = viewport:GetBuildWorkRect()
local axis = (dir == 2 --[[ImGuiDir.Up]] or dir == 3 --[[ImGuiDir.Down]]) and 2 --[[ImGuiAxis.Y]] or 1 --[[ImGuiAxis.X]]
local pos = ImVec2(avail_rect.Min.x, avail_rect.Min.y)
if dir == 1 --[[ImGuiDir.Right]] or dir == 3 --[[ImGuiDir.Down]] then
pos[axis] = avail_rect.Max[axis] - axis_size
end
local size = avail_rect:GetSize()
size[axis] = axis_size
ImGui.SetNextWindowPos(pos)
ImGui.SetNextWindowSize(size)
-- Report our size into work area (for next frame) using actual window size
if dir == 2 --[[ImGuiDir.Up]] or dir == 0 --[[ImGuiDir.Left]] then
viewport.BuildWorkInsetMin[axis] = viewport.BuildWorkInsetMin[axis] + axis_size
elseif dir == 3 --[[ImGuiDir.Down]] or dir == 1 --[[ImGuiDir.Right]] then
viewport.BuildWorkInsetMax[axis] = viewport.BuildWorkInsetMax[axis] + axis_size
end
end
window_flags = bit32.bor(window_flags or 0, 1 --[[ImGuiWindowFlags.NoTitleBar]], 2 --[[ImGuiWindowFlags.NoResize]], 4 --[[ImGuiWindowFlags.NoMove]], 524288 --[[ImGuiWindowFlags.NoDocking]])
ImGui.SetNextWindowViewport(viewport.ID)
ImGui.PushStyleVar(3 --[[ImGuiStyleVar.WindowRounding]], 0.0)
ImGui.PushStyleVar(5 --[[ImGuiStyleVar.WindowMinSize]], ImVec2(0, 0)) -- Lift normal size constraint
local _, is_open = ImGui.Begin(name, nil, window_flags)
ImGui.PopStyleVar(2)
return is_open
end
function ImGui.BeginMainMenuBar()
local g = GImGui
local viewport = ImGui.GetMainViewport()
-- Notify of viewport change so GetFrameHeight() can be accurate in case of DPI change
ImGui.SetCurrentViewport(nil, viewport)
-- For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set.
-- FIXME: This could be generalized as an opt-in way to clamp window.DC.CursorStartPos to avoid SafeArea?
-- FIXME: Consider removing support for safe area down the line... it's messy. Nowadays consoles have support for TV calibration in OS settings.
g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0))
local window_flags = bit32.bor(8 --[[ImGuiWindowFlags.NoScrollbar]], 256 --[[ImGuiWindowFlags.NoSavedSettings]], 1024 --[[ImGuiWindowFlags.MenuBar]])
local height = ImGui.GetFrameHeight()
local is_open = ImGui.BeginViewportSideBar("##MainMenuBar", viewport, 2 --[[ImGuiDir.Up]], height, window_flags)
g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0, 0.0)
if not is_open then
ImGui.End()
return false
end
-- Temporarily disable _NoSavedSettings, in the off-chance that tables or child windows submitted within the menu-bar may want to use settings. (#8356)
g.CurrentWindow.Flags = bit32.band(g.CurrentWindow.Flags, bit32.bnot(256 --[[ImGuiWindowFlags.NoSavedSettings]]))
ImGui.BeginMenuBar()
return is_open
end
function ImGui.EndMainMenuBar()
local g = GImGui
IM_ASSERT_USER_ERROR_RET(g.CurrentWindow.DC.MenuBarAppending, "Calling EndMainMenuBar() not from a menu-bar!") -- Not technically testing that it is the main menu bar
ImGui.EndMenuBar()
g.CurrentWindow.Flags = bit32.bor(g.CurrentWindow.Flags, 256 --[[ImGuiWindowFlags.NoSavedSettings]]) -- Restore _NoSavedSettings (#8356)
-- When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window
-- FIXME: With this strategy we won't be able to restore a nil focus.
if g.CurrentWindow == g.NavWindow and g.NavLayer == 0 --[[ImGuiNavLayer.Main]] and not g.NavAnyRequest and g.ActiveId == 0 then
ImGui.FocusTopMostWindowUnderOne(g.NavWindow, nil, nil, bit32.bor(2 --[[ImGuiFocusRequestFlags.UnlessBelowModal]], 1 --[[ImGuiFocusRequestFlags.RestoreFocusedChild]]))
end
ImGui.End()
end
local function IsRootOfOpenMenuSet()
local g = GImGui
local window = g.CurrentWindow
if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) or (bit32.band(window.Flags, 268435456 --[[ImGuiWindowFlags.ChildMenu]]) ~= 0) then
return false
end
-- Initially we used 'upper_popup.OpenParentId == window.IDStack:back()' to differentiate multiple menu sets from each others
-- (e.g. inside menu bar vs loose menu items) based on parent ID.
-- This would however prevent the use of e.g. PushID() user code submitting menus.
-- Previously this worked between popup and a first child menu because the first child menu always had the _ChildWindow flag,
-- making hovering on parent popup possible while first child menu was focused - but this was generally a bug with other side effects.
-- Instead we don't treat Popup specifically (in order to consistently support menu features in them), maybe the first child menu of a Popup
-- doesn't have the _ChildWindow flag, and we rely on this IsRootOfOpenMenuSet() check to allow hovering between root window/popup and first child menu.
-- In the end, lack of ID check made it so we could no longer differentiate between separate menu sets. To compensate for that, we at least check parent window nav layer.
-- This fixes the most common case of menu opening on hover when moving between window content and menu bar. Multiple different menu sets in same nav layer would still
-- open on hover, but that should be a lesser problem, because if such menus are close in proximity in window content then it won't feel weird and if they are far apart
-- it likely won't be a problem anyone runs into.
local upper_popup = g.OpenPopupStack.Data[g.BeginPopupStack.Size + 1]
if window.DC.NavLayerCurrent ~= upper_popup.ParentNavLayer then
return false
end
return upper_popup.Window and (bit32.band(upper_popup.Window.Flags, 268435456 --[[ImGuiWindowFlags.ChildMenu]]) ~= 0) and ImGui.IsWindowChildOf(upper_popup.Window, window, true, false)
end
--- @param label string
--- @param icon? string
--- @param enabled bool
function ImGui.BeginMenuEx(label, icon, enabled)
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
local g = GImGui
local style = g.Style
local id = window:GetID(label)
local menu_is_open = ImGui.IsPopupOpen(id, 0 --[[ImGuiPopupFlags.None]])
-- Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu)
-- The first menu in a hierarchy isn't so hovering doesn't get across (otherwise e.g. resizing borders with ImGuiButtonFlags.FlattenChildren would react), but top-most BeginMenu() will bypass that limitation.
local window_flags = bit32.bor(268435456 --[[ImGuiWindowFlags.ChildMenu]], 64 --[[ImGuiWindowFlags.AlwaysAutoResize]], 4 --[[ImGuiWindowFlags.NoMove]], 1 --[[ImGuiWindowFlags.NoTitleBar]], 256 --[[ImGuiWindowFlags.NoSavedSettings]], 131072 --[[ImGuiWindowFlags.NoNavFocus]])
if bit32.band(window.Flags, 268435456 --[[ImGuiWindowFlags.ChildMenu]]) ~= 0 then
window_flags = bit32.bor(window_flags, 16777216 --[[ImGuiWindowFlags.ChildWindow]])
end
-- If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin().
-- We are relying on a O(N) search - so O(N log N) over the frame - which seems like the most efficient for the expected small amount of BeginMenu() calls per frame.
-- If somehow this is ever becoming a problem we can switch to use e.g. ImGuiStorage mapping key to last frame used.
if g.MenusIdSubmittedThisFrame:contains(id) then
if menu_is_open then
menu_is_open = ImGui.BeginPopupMenuEx(id, label, window_flags) -- menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
else
g.NextWindowData:ClearFlags() -- we behave like Begin() and need to consume those values
end
return menu_is_open
end
-- Tag menu as used. Next time BeginMenu() with same ID is called it will append to existing menu
g.MenusIdSubmittedThisFrame:push_back(id)
local label_end = ImGui.FindRenderedTextEnd(label)
local label_size = ImGui.CalcTextSize(label, label_end, false)
-- Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window)
-- This is only done for items for the menu set and not the full parent window.
local menuset_is_open = IsRootOfOpenMenuSet()
if menuset_is_open then
g.NextItemData.ItemFlagsSet = bit32.bor(g.NextItemData.ItemFlagsSet, 8192 --[[ImGuiItemFlags.NoWindowHoverableCheck]])
end
-- The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu,
-- However the final position is going to be different! It is chosen by FindBestWindowPosForPopup().
-- e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering.
local popup_pos
local pos = ImVec2()
ImVec2_Copy(pos, window.DC.CursorPos)
ImGui.PushID(label)
if not enabled then
ImGui.BeginDisabled()
end
local pressed
local backup_rounding = style.SelectableRounding
style.SelectableRounding = style.MenuItemRounding
local selectable_flags = bit32.bor(4194304 --[[ImGuiSelectableFlags.SelectOnClick]], 1 --[[ImGuiSelectableFlags.NoAutoClosePopups]])
local offsets = window.DC.MenuColumns
if window.DC.LayoutType == 0 --[[ImGuiLayoutType.Horizontal]] then
-- Menu inside a horizontal menu bar
-- Selectable extend their highlight by half ItemSpacing in each direction.
-- For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin()
window.DC.CursorPos.x = window.DC.CursorPos.x + IM_TRUNC(style.ItemSpacing.x * 0.5)
ImGui.PushStyleVarX(14 --[[ImGuiStyleVar.ItemSpacing]], style.ItemSpacing.x * 2.0)
local text_pos = ImVec2(window.DC.CursorPos.x + offsets.OffsetLabel, pos.y + window.DC.CurrLineTextBaseOffset)
pressed = ImGui.Selectable("", menu_is_open, selectable_flags, label_size)
-- TODO: ImGui.LogSetNextTextDecoration("[", "]")
ImGui.RenderText(text_pos, label, 1, label_end, false)
ImGui.PopStyleVar()
window.DC.CursorPos.x = window.DC.CursorPos.x + IM_TRUNC(style.ItemSpacing.x * (-1.0 + 0.5)) -- -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
popup_pos = ImVec2(pos.x - 1.0 - IM_TRUNC(style.ItemSpacing.x * 0.5), text_pos.y - style.FramePadding.y + window.MenuBarHeight)
else
-- Menu inside a regular/vertical menu
-- (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.
-- Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.)
local icon_w
if (icon and icon ~= "") then icon_w = ImGui.CalcTextSize(icon, nil).x else icon_w = 0.0 end
local checkmark_w = IM_TRUNC(g.FontSize * 1.20)
local min_w = offsets:DeclColumns(icon_w, label_size.x, 0.0, checkmark_w) -- Feedback to next frame
local extra_w = ImMax(0.0, ImGui.GetContentRegionAvail().x - min_w)
local text_pos = ImVec2(window.DC.CursorPos.x, pos.y + window.DC.CurrLineTextBaseOffset)
pressed = ImGui.Selectable("", menu_is_open, bit32.bor(selectable_flags, 16777216 --[[ImGuiSelectableFlags.SpanAvailWidth]]), ImVec2(min_w, label_size.y))
-- ImGui.LogSetNextTextDecoration("", ">")
ImGui.RenderText(ImVec2(text_pos.x + offsets.OffsetLabel, text_pos.y), label, 1, label_end, false)
if icon_w > 0.0 then
--- @cast icon string
ImGui.RenderText(ImVec2(text_pos.x + offsets.OffsetIcon, text_pos.y), icon)
end
ImGui.RenderArrow(window.DrawList, ImVec2(text_pos.x + offsets.OffsetMark + extra_w + g.FontSize * 0.30, text_pos.y), ImGui.GetColorU32(0 --[[ImGuiCol.Text]]), 1 --[[ImGuiDir.Right]])
popup_pos = ImVec2(pos.x, text_pos.y - style.WindowPadding.y)
end
style.SelectableRounding = backup_rounding
if not enabled then
ImGui.EndDisabled()
end
-- Once dragged, release ActiveId + key ownership. This is to allow the idiom of mouse down a menu, dragging elsewhere, up on some other MenuItem(). (#8233, #9394)
-- Could move logic into lower-level ImGuiButtonFlags.AutoReleaseActiveId + ImGuiButtonFlags.AutoReleaseKeyOwner? Easier once we get rid of the Selectable() middle-man here.
if g.ActiveId == id and g.HoveredId ~= id and g.ActiveIdSource == 1 --[[ImGuiInputSource.Mouse]] and ImGui.IsMouseDragging(0) then
ImGui.ClearActiveID()
ImGui.SetKeyOwner(656 --[[ImGuiKey.MouseLeft]], 4294967295 --[[ImGuiKeyOwner_NoOwner]])
end
local hovered = g.HoveredId == id and enabled and not g.NavHighlightItemUnderNav
local want_open = false
local want_open_nav_init = false
local want_close = false
if window.DC.LayoutType == 1 --[[ImGuiLayoutType.Vertical]] then -- (window.Flags & (ImGuiWindowFlags.Popup|ImGuiWindowFlags.ChildMenu))
-- Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu
-- Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
local moving_toward_child_menu = false
local child_popup = (g.BeginPopupStack.Size < g.OpenPopupStack.Size) and g.OpenPopupStack[g.BeginPopupStack.Size + 1] or nil -- Popup candidate (testing below)
local child_menu_window = (child_popup and child_popup.Window and child_popup.Window.ParentWindow == window) and child_popup.Window or nil
if g.HoveredWindow == window and child_menu_window ~= nil then
local ref_unit = g.FontSize -- FIXME-DPI
local child_dir = (window.Pos.x < child_menu_window.Pos.x) and 1.0 or -1.0
local next_window_rect = child_menu_window:Rect()
local ta = (g.IO.MousePos - g.IO.MouseDelta)
local tb
local tc
if child_dir > 0.0 then
tb = next_window_rect:GetTL()
tc = next_window_rect:GetBL()
else
tb = next_window_rect:GetTR()
tc = next_window_rect:GetBR()
end
local pad_farmost_h = ImClamp(ImFabs(ta.x - tb.x) * 0.30, ref_unit * 0.5, ref_unit * 2.5) -- Add a bit of extra slack.
ta.x = ta.x + child_dir * -0.5
tb.x = tb.x + child_dir * ref_unit
tc.x = tc.x + child_dir * ref_unit
tb.y = ta.y + ImMax((tb.y - pad_farmost_h) - ta.y, -ref_unit * 8.0) -- Triangle has maximum height to limit the slope and the bias toward large sub-menus
tc.y = ta.y + ImMin((tc.y + pad_farmost_h) - ta.y, ref_unit * 8.0)
moving_toward_child_menu = ImStd.ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos)
-- ImGui.GetForegroundDrawList():AddTriangleFilled(ta, tb, tc, moving_toward_child_menu and IM_COL32(0, 128, 0, 128) or IM_COL32(128, 0, 0, 128)) -- [DEBUG]
end
-- The 'HovereWindow == window' check creates an inconsistency (e.g. moving away from menu slowly tends to hit same window, whereas moving away fast does not)
-- But we also need to not close the top-menu menu when moving over void. Perhaps we should extend the triangle check to a larger polygon.
-- (Remember to test this on BeginPopup("A")->BeginMenu("B") sequence which behaves slightly differently as B isn't a Child of A and hovering isn't shared.)
if menu_is_open and not hovered and g.HoveredWindow == window and not moving_toward_child_menu and not g.NavHighlightItemUnderNav and g.ActiveId == 0 then
want_close = true
end
-- Open
-- (note: at this point 'hovered' actually includes the NavDisableMouseHover == false test)
if not menu_is_open and pressed then -- Click/activate to open
want_open = true
elseif not menu_is_open and hovered and not moving_toward_child_menu then -- Hover to open
want_open = true
elseif not menu_is_open and hovered and g.HoveredIdTimer >= 0.30 and g.MouseStationaryTimer >= 0.30 then -- Hover to open (timer fallback)
want_open = true
end
if g.NavId == id and g.NavMoveDir == 1 --[[ImGuiDir.Right]] then -- Nav-Right to open
want_open = true
want_open_nav_init = true
ImGui.NavMoveRequestCancel()
ImGui.SetNavCursorVisibleAfterMove()
end
else
-- Menu bar
if menu_is_open and pressed and menuset_is_open then -- Click an open menu again to close it
want_close = true
want_open = false
menu_is_open = false
elseif pressed or (hovered and menuset_is_open and not menu_is_open) then -- First click to open, then hover to open others
want_open = true
elseif g.NavId == id and g.NavMoveDir == 3 --[[ImGuiDir.Down]] then -- Nav-Down to open
want_open = true
ImGui.NavMoveRequestCancel()
end
end
if not enabled then -- explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }'
want_close = true
end
if want_close and ImGui.IsPopupOpen(id, 0 --[[ImGuiPopupFlags.None]]) then
ImGui.ClosePopupToLevel(g.BeginPopupStack.Size, true)
end
-- IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0))
ImGui.PopID()
if g.ActiveId == id and want_open then
g.ActiveIdNoClearOnFocusLoss = true
end
if want_open and not menu_is_open and g.OpenPopupStack.Size > g.BeginPopupStack.Size then
-- Don't reopen/recycle same menu level in the same frame if it is a different menu ID, first close the other menu and yield for a frame.
ImGui.OpenPopup(label)
elseif want_open then
menu_is_open = true
ImGui.OpenPopup(label, 32 --[[ImGuiPopupFlags.NoReopen]]) -- | (want_open_nav_init ? ImGuiPopupFlags.NoReopenAlwaysNavInit : 0))
end
if menu_is_open then
local last_item_in_parent = ImGuiLastItemData()
ImGuiLastItemData_Copy(last_item_in_parent, g.LastItemData)
ImGui.SetNextWindowPos(popup_pos, 1 --[[ImGuiCond.Always]]) -- Note: misleading: the value will serve as reference for FindBestWindowPosForPopup(), not actual pos.
ImGui.PushStyleVar(7 --[[ImGuiStyleVar.ChildRounding]], style.PopupRounding) -- First level will use _PopupRounding, subsequent will use _ChildRounding
menu_is_open = ImGui.BeginPopupMenuEx(id, label, window_flags) -- menu_is_open may be 'false' when the popup is completely clipped (e.g. zero size display)
ImGui.PopStyleVar()
if menu_is_open then
-- Implement what ImGuiPopupFlags.NoReopenAlwaysNavInit would do:
-- Perform an init request in the case the popup was already open (via a previous mouse hover)
if want_open and want_open_nav_init and not g.NavInitRequest then
ImGui.FocusWindow(g.CurrentWindow, 2 --[[ImGuiFocusRequestFlags.UnlessBelowModal]])
ImGui.NavInitWindow(g.CurrentWindow, false)
end
-- Restore LastItemData so IsItemXXXX functions can work after BeginMenu()/EndMenu()
-- (This fixes using IsItemClicked() and IsItemHovered(), but IsItemHovered() also relies on its support for ImGuiItemFlags.NoWindowHoverableCheck)
ImGuiLastItemData_Copy(g.LastItemData, last_item_in_parent)
if g.HoveredWindow == window then
g.LastItemData.StatusFlags = bit32.bor(g.LastItemData.StatusFlags, 128 --[[ImGuiItemStatusFlags.HoveredWindow]])
end
end
else
g.NextWindowData:ClearFlags() -- We behave like Begin() and need to consume those values
end
return menu_is_open
end
--- @param label string
--- @param enabled? bool
function ImGui.BeginMenu(label, enabled)
if enabled == nil then enabled = true end
return ImGui.BeginMenuEx(label, nil, enabled)
end
function ImGui.EndMenu()
-- Nav: When a left move request our menu failed, close ourselves.
local g = GImGui
local window = g.CurrentWindow
IM_ASSERT_USER_ERROR_RET(bit32.band(window.Flags, bit32.bor(67108864 --[[ImGuiWindowFlags.Popup]], 268435456 --[[ImGuiWindowFlags.ChildMenu]])) == bit32.bor(67108864 --[[ImGuiWindowFlags.Popup]], 268435456 --[[ImGuiWindowFlags.ChildMenu]]), "Calling EndMenu() in wrong window!")
local parent_window = window.ParentWindow -- Should always be ~= nil is we passed assert.
if window.BeginCount == window.BeginCountPreviousFrame then
if g.NavMoveDir == 0 --[[ImGuiDir.Left]] and ImGui.NavMoveRequestButNoResultYet() then
if g.NavWindow and g.NavWindow.RootWindowForNav == window and parent_window.DC.LayoutType == 1 --[[ImGuiLayoutType.Vertical]] then
ImGui.ClosePopupToLevel(g.BeginPopupStack.Size - 1, true)
ImGui.NavMoveRequestCancel()
end
end
end
ImGui.EndPopup()
end
--- @param label string
--- @param icon? string
--- @param shortcut? string
--- @param selected? bool
--- @param enabled? bool
function ImGui.MenuItemEx(label, icon, shortcut, selected, enabled)
if selected == nil then selected = false end
if enabled == nil then enabled = true end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
end
local g = GImGui
local style = g.Style
local pos = ImVec2()
ImVec2_Copy(pos, window.DC.CursorPos)
local label_end = ImGui.FindRenderedTextEnd(label)
local label_size = ImGui.CalcTextSize(label, label_end, false)
-- See BeginMenuEx() for comments about this.
local menuset_is_open = IsRootOfOpenMenuSet()
if menuset_is_open then
g.NextItemData.ItemFlagsSet = bit32.bor(g.NextItemData.ItemFlagsSet, 8192 --[[ImGuiItemFlags.NoWindowHoverableCheck]])
end
-- We've been using the equivalent of ImGuiSelectableFlags.SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73),
-- but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only.
ImGui.PushID(label)
if not enabled then
ImGui.BeginDisabled()
end
local pressed
-- We use ImGuiSelectableFlags.NoSetKeyOwner to allow down on one menu item, move, up on another.
local backup_rounding = style.SelectableRounding
style.SelectableRounding = style.MenuItemRounding
local selectable_flags = bit32.bor(8388608 --[[ImGuiSelectableFlags.SelectOnRelease]], 33554432 --[[ImGuiSelectableFlags.SetNavIdOnHover]])
local offsets = window.DC.MenuColumns
if window.DC.LayoutType == 0 --[[ImGuiLayoutType.Horizontal]] then
-- Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful
-- Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark.
window.DC.CursorPos.x = window.DC.CursorPos.x + IM_TRUNC(style.ItemSpacing.x * 0.5)
local text_pos = ImVec2(window.DC.CursorPos.x + offsets.OffsetLabel, window.DC.CursorPos.y + window.DC.CurrLineTextBaseOffset)
ImGui.PushStyleVarX(14 --[[ImGuiStyleVar.ItemSpacing]], style.ItemSpacing.x * 2.0)
pressed = ImGui.Selectable("", selected, selectable_flags, ImVec2(label_size.x, 0.0))
ImGui.PopStyleVar()
if bit32.band(g.LastItemData.StatusFlags, 256 --[[ImGuiItemStatusFlags.Visible]]) ~= 0 then
ImGui.RenderText(text_pos, label, 1, label_end, false)
end
window.DC.CursorPos.x = window.DC.CursorPos.x + IM_TRUNC(style.ItemSpacing.x * (-1.0 + 0.5)) -- -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
else
-- Menu item inside a vertical menu
-- (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.
-- Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.)
local icon_w = 0.0
if icon and icon ~= "" then
icon_w = ImGui.CalcTextSize(icon, nil).x
end
local shortcut_w = 0.0
if shortcut and shortcut ~= "" then
shortcut_w = ImGui.CalcTextSize(shortcut, nil).x
end
local checkmark_w = IM_TRUNC(g.FontSize * 1.20) -- FIXME: Always accounted for, even in a menu with none, because 'selected' has no neutral setting.
local min_w = offsets:DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w) -- Feedback for next frame
local stretch_w = ImMax(0.0, ImGui.GetContentRegionAvail().x - min_w)
local text_pos = ImVec2(pos.x, pos.y + window.DC.CurrLineTextBaseOffset)
pressed = ImGui.Selectable("", false, bit32.bor(selectable_flags, 16777216 --[[ImGuiSelectableFlags.SpanAvailWidth]]), ImVec2(min_w, label_size.y))
if bit32.band(g.LastItemData.StatusFlags, 256 --[[ImGuiItemStatusFlags.Visible]]) ~= 0 then
ImGui.RenderText(text_pos + ImVec2(offsets.OffsetLabel, 0.0), label, 1, label_end, false)
if icon_w > 0.0 then
--- @cast icon string
ImGui.RenderText(text_pos + ImVec2(offsets.OffsetIcon, 0.0), icon)
end
if shortcut_w > 0.0 then
ImGui.PushStyleColor(0 --[[ImGuiCol.Text]], style.Colors[1 --[[ImGuiCol.TextDisabled]]])
-- ImGui.LogSetNextTextDecoration("(", ")")
--- @cast shortcut string
ImGui.RenderText(text_pos + ImVec2(offsets.OffsetShortcut + stretch_w, 0.0), shortcut, nil, nil, false)
ImGui.PopStyleColor()
end
if selected then
ImGui.RenderCheckMark(window.DrawList, text_pos + ImVec2(offsets.OffsetMark + stretch_w + g.FontSize * 0.40, g.FontSize * 0.134 * 0.5), ImGui.GetColorU32(0 --[[ImGuiCol.Text]]), g.FontSize * 0.866)
end
end
end
style.SelectableRounding = backup_rounding
-- Once dragged, release ActiveId + key ownership. This is to allow the idiom of mouse down a menu, dragging elsewhere, up on some other MenuItem(). (#8233, #9394)
-- Could move logic into lower-level ImGuiButtonFlags_AutoReleaseActiveId + ImGuiButtonFlags_AutoReleaseKeyOwner? Easier once we get rid of the Selectable() middle-man here.
local id = g.LastItemData.ID
if g.ActiveId == id and g.HoveredId ~= id and g.ActiveIdSource == 1 --[[ImGuiInputSource.Mouse]] and ImGui.IsMouseDragging(0) then
ImGui.ClearActiveID()
ImGui.SetKeyOwner(656 --[[ImGuiKey.MouseLeft]], 4294967295 --[[ImGuiKeyOwner_NoOwner]])
end
-- IMGUI_TEST_ENGINE_ITEM_INFO()
if not enabled then
ImGui.EndDisabled()
end
ImGui.PopID()
return pressed
end
--- @param label string
--- @param shortcut? string
--- @param selected? bool
--- @param enabled? bool
function ImGui.MenuItem(label, shortcut, selected, enabled)
if ImGui.MenuItemEx(label, nil, shortcut, selected, enabled) then
if selected ~= nil then
selected = not selected
end
return true, selected
end
return false, selected
end
do --[[ [SECTION] Widgets: BeginTabBar, EndTabBar, etc. (was imgui_tabs.lua) ]]
--- Dear ImGui
-- Tab bars & tab items: 1:1 port of imgui_widgets.cpp [SECTION] Widgets: BeginTabBar/BeginTabItem (docking branch)
--
-- Port notes:
-- - tab_bar.Tabs is an ImVector of ImGuiTabItem tables (references). C++ pointer arithmetic on the array is replaced by
-- index lookups. tab_bar.LastTabItemIdx and tab orders are 0-based like upstream.
-- - Tab names are stored on the tab itself (tab.Name) instead of tab_bar.TabsNames.
-- - ImGui.BeginTabItem(label, p_open, flags) returns `visible, p_open`.
local MT = ImGui.GetMetatables()
local GImGui
local function SyncContext() GImGui = ImGui.GetCurrentContext() end
local function ImLinearSweep(current, target, speed)
if current < target then return ImMin(current + speed, target) end
if current > target then return ImMax(current - speed, target) end
return current
end
local function TabItemGetSectionIdx(tab)
if bit32.band(tab.Flags, 64 --[[ImGuiTabItemFlags.Leading]]) ~= 0 then return 0 end
if bit32.band(tab.Flags, 128 --[[ImGuiTabItemFlags.Trailing]]) ~= 0 then return 2 end
return 1
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)
local function SortTabsBySection(tab_bar)
table.sort(tab_bar.Tabs.Data, TabItemComparerBySection)
end
local function SortTabsByBeginOrder(tab_bar)
local data = tab_bar.Tabs.Data
if not data then return end
local arr = {}
for i = 1, tab_bar.Tabs.Size do arr[i] = data[i] end
table.sort(arr, TabItemComparerByBeginOrder)
for i = 1, tab_bar.Tabs.Size do data[i] = arr[i] end
end
-- Scrolling happens only in the central section (leading/trailing sections are not scrolling)
local function TabBarCalcScrollableWidth(tab_bar, sections)
return tab_bar.BarRect:GetWidth() - sections[1].Width - sections[3].Width - sections[2].Spacing
end
-- TabCount: Number of tabs in this section.
-- Width: Sum of width of tabs in this section (after shrinking down)
-- 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
-- [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.
-- 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
function ImGui.ShrinkWidths(items, first, count, width_excess, width_min)
if count <= 0 then return end
if count == 1 then
local it = items[first]
if it.Width >= 0.0 then it.Width = ImMax(it.Width - width_excess, width_min) end
return
end
-- Sort largest first, smallest last.
local slice = {}
for n = 1, count do slice[n] = items[first + n - 1] end
table.sort(slice, ShrinkWidthItemComparer)
for n = 1, count do items[first + n - 1] = slice[n] end
local it = slice
local count_same_width = 1
while width_excess > 0.001 and count_same_width < count do
while count_same_width < count and it[1].Width <= it[count_same_width + 1].Width do
count_same_width = count_same_width + 1
end
local max_width_to_remove_per_item = (count_same_width < count and it[count_same_width + 1].Width >= 0.0)
and (it[1].Width - it[count_same_width + 1].Width) or (it[1].Width - 1.0)
max_width_to_remove_per_item = ImMin(it[1].Width - width_min, max_width_to_remove_per_item)
if max_width_to_remove_per_item <= 0.0 then break end
local base_width_to_remove_per_item = ImMin(width_excess / count_same_width, max_width_to_remove_per_item)
for item_n = 1, count_same_width do
local w = ImMin(base_width_to_remove_per_item, it[item_n].Width - width_min)
it[item_n].Width = it[item_n].Width - w
width_excess = width_excess - w
end
end
-- Round width and redistribute remainder
-- Ensure that e.g. the right-most tab of a shrunk tab-bar always reaches exactly at the same distance from the right-most edge of the tab bar separator.
width_excess = 0.0
for n = 1, count do
local width_rounded = ImTrunc(it[n].Width)
width_excess = width_excess + it[n].Width - width_rounded
it[n].Width = width_rounded
end
local guard = 0
while width_excess > 0.0 and guard < 10000 do
guard = guard + 1
local progressed = false
for n = 1, count do
if width_excess <= 0.0 then break end
local width_to_add = ImMin(it[n].InitialWidth - it[n].Width, 1.0)
if width_to_add > 0 then progressed = true end
it[n].Width = it[n].Width + width_to_add
width_excess = width_excess - width_to_add
end
if not progressed then break end
end
end
-------------------------------------------------------------------------------
-- [SECTION] Widgets: BeginTabBar, EndTabBar, etc.
-------------------------------------------------------------------------------
-- - BeginTabBar()
-- - BeginTabBarEx() [Internal]
-- - EndTabBar()
-- - TabBarLayout() [Internal]
-- - TabBarCalcTabID() [Internal]
-- - TabBarCalcMaxTabWidth() [Internal]
-- - TabBarFindTabById() [Internal]
-- - TabBarFindTabByOrder() [Internal]
-- - TabBarFindMostRecentlySelectedTabForActiveWindow() [Internal]
-- - TabBarGetCurrentTab() [Internal]
-- - TabBarGetTabName() [Internal]
-- - TabBarAddTab() [Internal]
-- - TabBarRemoveTab() [Internal]
-- - TabBarCloseTab() [Internal]
-- - TabBarScrollClamp() [Internal]
-- - TabBarScrollToTab() [Internal]
-- - TabBarQueueFocus() [Internal]
-- - TabBarQueueReorder() [Internal]
-- - TabBarProcessReorderFromMousePos() [Internal]
-- - TabBarProcessReorder() [Internal]
-- - TabBarScrollingButtons() [Internal]
-- - TabBarTabListPopupButton() [Internal]
-------------------------------------------------------------------------------
function ImGui.TabBarFindByID(id)
SyncContext()
return GImGui.TabBars:GetByKey(id)
end
-- Remove TabBar data (currently only used by TestEngine)
function ImGui.TabBarRemove(tab_bar)
SyncContext()
GImGui.TabBars:Remove(tab_bar.ID, tab_bar)
end
function ImGui.BeginTabBar(str_id, flags)
if flags == nil then flags = 0 end
SyncContext()
local g = GImGui
local window = g.CurrentWindow
if window.SkipItems then return false end
local id = window:GetID(str_id)
local tab_bar = g.TabBars:GetOrAddByKey(id)
local tab_bar_bb = ImRect(window.DC.CursorPos.x, window.DC.CursorPos.y, window.WorkRect.Max.x, window.DC.CursorPos.y + g.FontSize + g.Style.FramePadding.y * 2)
tab_bar.ID = id
tab_bar.SeparatorMinX = tab_bar_bb.Min.x - IM_TRUNC(window.WindowPadding.x * 0.5)
tab_bar.SeparatorMaxX = tab_bar_bb.Max.x + IM_TRUNC(window.WindowPadding.x * 0.5)
-- if (g.NavWindow and ImGui.IsWindowChildOf(g.NavWindow, window, false, false))
flags = bit32.bor(flags, 2097152 --[[ImGuiTabBarFlags.IsFocused]])
return ImGui.BeginTabBarEx(tab_bar, tab_bar_bb, flags)
end
function ImGui.BeginTabBarEx(tab_bar, tab_bar_bb, flags)
SyncContext()
local g = GImGui
local window = g.CurrentWindow
if window.SkipItems then return false end
assert(tab_bar.ID ~= 0)
if bit32.band(flags, 1048576 --[[ImGuiTabBarFlags.DockNode]]) == 0 then -- Already done
ImGui.PushOverrideID(tab_bar.ID)
end
-- Add to stack
g.CurrentTabBarStack:push_back(GetTabBarRefFromTabBar(tab_bar))
g.CurrentTabBar = tab_bar
tab_bar.Window = window
-- Append with multiple BeginTabBar()/EndTabBar() pairs.
ImVec2_Copy(tab_bar.BackupCursorPos, window.DC.CursorPos)
if tab_bar.CurrFrameVisible == g.FrameCount then
ImVec2_CopyV(window.DC.CursorPos, tab_bar.BarRect.Min.x, tab_bar.BarRect.Max.y + tab_bar.ItemSpacingY)
tab_bar.BeginCount = tab_bar.BeginCount + 1
return true
end
-- Ensure correct ordering when toggling ImGuiTabBarFlags.Reorderable flag, or when a new tab was added while being not reorderable
if bit32.band(flags, 1 --[[ImGuiTabBarFlags.Reorderable]]) ~= bit32.band(tab_bar.Flags, 1 --[[ImGuiTabBarFlags.Reorderable]])
or (tab_bar.TabsAddedNew and bit32.band(flags, 1 --[[ImGuiTabBarFlags.Reorderable]]) == 0) then
if bit32.band(flags, 1048576 --[[ImGuiTabBarFlags.DockNode]]) == 0 then -- FIXME: TabBar with DockNode can now be hybrid
SortTabsByBeginOrder(tab_bar)
end
end
tab_bar.TabsAddedNew = false
-- Flags
if bit32.band(flags, 896 --[[ImGuiTabBarFlags.FittingPolicyMask_]]) == 0 then
flags = bit32.bor(flags, 128 --[[ImGuiTabBarFlags.FittingPolicyDefault_]])
end
tab_bar.Flags = flags
tab_bar.BarRect = ImRect(tab_bar_bb.Min, tab_bar_bb.Max)
tab_bar.WantLayout = true -- Layout will be done on the first call to ItemTab()
tab_bar.PrevFrameVisible = tab_bar.CurrFrameVisible
tab_bar.CurrFrameVisible = g.FrameCount
tab_bar.PrevTabsContentsHeight = tab_bar.CurrTabsContentsHeight
tab_bar.CurrTabsContentsHeight = 0.0
tab_bar.ItemSpacingY = g.Style.ItemSpacing.y
ImVec2_Copy(tab_bar.FramePadding, g.Style.FramePadding)
tab_bar.TabsActiveCount = 0
tab_bar.LastTabItemIdx = -1
tab_bar.BeginCount = 1
-- Set cursor pos in a way which only be used in the off-chance the user erroneously submits item before BeginTabItem(): items will overlap
ImVec2_CopyV(window.DC.CursorPos, tab_bar.BarRect.Min.x, tab_bar.BarRect.Max.y + tab_bar.ItemSpacingY)
-- Draw separator
-- (it would be misleading to draw this in EndTabBar() suggesting that it may be drawn over tabs, as tab bar are appendable)
local col = ImGui.GetColorU32(bit32.band(flags, 2097152 --[[ImGuiTabBarFlags.IsFocused]]) ~= 0 and 37 --[[ImGuiCol.TabSelected]] or 40 --[[ImGuiCol.TabDimmedSelected]])
if g.Style.TabBarBorderSize > 0.0 then
local y = tab_bar.BarRect.Max.y
window.DrawList:AddRectFilled(ImVec2(tab_bar.SeparatorMinX, y - g.Style.TabBarBorderSize), ImVec2(tab_bar.SeparatorMaxX, y), col)
end
return true
end
function ImGui.EndTabBar()
SyncContext()
local g = GImGui
local window = g.CurrentWindow
if window.SkipItems then return end
local tab_bar = g.CurrentTabBar
assert(tab_bar ~= nil, "Mismatched BeginTabBar()/EndTabBar()!")
-- Fallback in case no TabItem have been submitted
if tab_bar.WantLayout then ImGui.TabBarLayout(tab_bar) end
-- Restore the last visible height if no tab is visible, this reduce vertical flicker/movement when a tabs gets removed without calling SetTabItemClosed().
local tab_bar_appearing = (tab_bar.PrevFrameVisible + 1 < g.FrameCount)
if tab_bar.VisibleTabWasSubmitted or tab_bar.VisibleTabId == 0 or tab_bar_appearing then
tab_bar.CurrTabsContentsHeight = ImMax(window.DC.CursorPos.y - tab_bar.BarRect.Max.y, tab_bar.CurrTabsContentsHeight)
window.DC.CursorPos.y = tab_bar.BarRect.Max.y + tab_bar.CurrTabsContentsHeight
else
window.DC.CursorPos.y = tab_bar.BarRect.Max.y + tab_bar.PrevTabsContentsHeight
end
if tab_bar.BeginCount > 1 then
ImVec2_Copy(window.DC.CursorPos, tab_bar.BackupCursorPos)
end
tab_bar.LastTabItemIdx = -1
if bit32.band(tab_bar.Flags, 1048576 --[[ImGuiTabBarFlags.DockNode]]) == 0 then -- Already done
ImGui.PopID()
end
g.CurrentTabBarStack:pop_back()
if g.CurrentTabBarStack.Size == 0 then g.CurrentTabBar = nil else g.CurrentTabBar = GetTabBarFromTabBarRef(g.CurrentTabBarStack:back()) end
end
-- This is called only once a frame before by the first call to ItemTab()
-- The reason we're not calling it in BeginTabBar() is to leave a chance to the user to call the SetTabItemClosed() functions.
function ImGui.TabBarLayout(tab_bar)
SyncContext()
local g = GImGui
tab_bar.WantLayout = false
-- Track selected tab when resizing our parent down
local scroll_to_selected_tab = (tab_bar.BarRectPrevWidth > tab_bar.BarRect:GetWidth())
tab_bar.BarRectPrevWidth = tab_bar.BarRect:GetWidth()
-- Garbage collect by compacting list
-- Detect if we need to sort out tab list (e.g. in rare case where a tab changed section)
local tabs = tab_bar.Tabs
local tab_dst_n = 0
local need_sort_by_section = false
local sections = { ImGuiTabBarSection(), ImGuiTabBarSection(), ImGuiTabBarSection() } -- Layout sections: Leading, Central, Trailing (1-based: [1]=Leading,[2]=Central,[3]=Trailing)
for tab_src_n = 0, tabs.Size - 1 do
local tab = tabs.Data[tab_src_n + 1]
if tab.LastFrameVisible < tab_bar.PrevFrameVisible or tab.WantClose then
-- Remove tab
if tab_bar.VisibleTabId == tab.ID then tab_bar.VisibleTabId = 0 end
if tab_bar.SelectedTabId == tab.ID then tab_bar.SelectedTabId = 0 end
if tab_bar.NextSelectedTabId == tab.ID then tab_bar.NextSelectedTabId = 0 end
else
if tab_dst_n ~= tab_src_n then tabs.Data[tab_dst_n + 1] = tab end
tab.IndexDuringLayout = tab_dst_n
-- We will need sorting if tabs have changed section (e.g. moved from one of Leading/Central/Trailing to another)
local curr_tab_section_n = TabItemGetSectionIdx(tab)
if tab_dst_n > 0 then
local prev_tab = tabs.Data[tab_dst_n]
local prev_tab_section_n = TabItemGetSectionIdx(prev_tab)
if curr_tab_section_n == 0 and prev_tab_section_n ~= 0 then need_sort_by_section = true end
if prev_tab_section_n == 2 and curr_tab_section_n ~= 2 then need_sort_by_section = true end
end
sections[curr_tab_section_n + 1].TabCount = sections[curr_tab_section_n + 1].TabCount + 1
tab_dst_n = tab_dst_n + 1
end
end
if tabs.Size ~= tab_dst_n then
for i = tab_dst_n + 1, tabs.Size do tabs.Data[i] = nil end
tabs.Size = tab_dst_n
end
if need_sort_by_section then SortTabsBySection(tab_bar) end
-- Calculate spacing between sections
local tab_spacing = g.Style.ItemInnerSpacing.x
sections[1].Spacing = (sections[1].TabCount > 0 and (sections[2].TabCount + sections[3].TabCount) > 0) and tab_spacing or 0.0
sections[2].Spacing = (sections[2].TabCount > 0 and sections[3].TabCount > 0) and tab_spacing or 0.0
-- Setup next selected tab
local scroll_to_tab_id = 0
if tab_bar.NextScrollToTabId ~= 0 then
scroll_to_tab_id = tab_bar.NextScrollToTabId
tab_bar.NextScrollToTabId = 0
end
if tab_bar.NextSelectedTabId ~= 0 then
tab_bar.SelectedTabId = tab_bar.NextSelectedTabId
tab_bar.NextSelectedTabId = 0
scroll_to_tab_id = tab_bar.SelectedTabId
end
-- Process order change request (we could probably process it when requested but it's just saner to do it in a single spot).
if tab_bar.ReorderRequestTabId ~= 0 then
if ImGui.TabBarProcessReorder(tab_bar) then
if tab_bar.ReorderRequestTabId == tab_bar.SelectedTabId then
scroll_to_tab_id = tab_bar.ReorderRequestTabId
end
end
tab_bar.ReorderRequestTabId = 0
end
-- Tab List Popup (will alter tab_bar.BarRect and therefore the available width!)
local tab_list_popup_button = bit32.band(tab_bar.Flags, 4 --[[ImGuiTabBarFlags.TabListPopupButton]]) ~= 0
if tab_list_popup_button then
local tab_to_select = ImGui.TabBarTabListPopupButton(tab_bar) -- NB: Will alter BarRect.Min.x!
if tab_to_select then
tab_bar.SelectedTabId = tab_to_select.ID
scroll_to_tab_id = tab_to_select.ID
end
end
-- Leading/Trailing tabs will be shrink only if central one aren't visible anymore, so layout the shrink data as: leading, trailing, central
-- (whereas our tabs are stored as: leading, central, trailing)
-- (shrink_buffer_indexes holds 0-based offsets)
local shrink_buffer_indexes = { 0, sections[1].TabCount + sections[3].TabCount, sections[1].TabCount }
local swb = g.ShrinkWidthBuffer
swb:resize(tabs.Size)
for i = 1, tabs.Size do if swb.Data[i] == nil then swb.Data[i] = ImGuiShrinkWidthItem() end end
-- Minimum shrink width
local shrink_min_width = (bit32.band(tab_bar.Flags, 128 --[[ImGuiTabBarFlags.FittingPolicyMixed]]) ~= 0) and g.Style.TabMinWidthShrink or 1.0
-- Compute ideal tabs widths + store them into shrink buffer
local most_recently_selected_tab = nil
local curr_section_n = -1
local found_selected_tab_id = false
for tab_n = 0, tabs.Size - 1 do
local tab = tabs.Data[tab_n + 1]
assert(tab.LastFrameVisible >= tab_bar.PrevFrameVisible)
if (most_recently_selected_tab == nil or most_recently_selected_tab.LastFrameSelected < tab.LastFrameSelected) and bit32.band(tab.Flags, 2097152 --[[ImGuiTabItemFlags.Button]]) == 0 then
most_recently_selected_tab = tab
end
if tab.ID == tab_bar.SelectedTabId then found_selected_tab_id = true end
if scroll_to_tab_id == 0 and g.NavJustMovedToId == tab.ID then scroll_to_tab_id = tab.ID end
-- Refresh tab width immediately, otherwise changes of style e.g. style.FramePadding.x would noticeably lag in the tab bar.
-- Additionally, when using TabBarAddTab() to manipulate tab bar order we occasionally insert new tabs that don't have a width yet,
-- and we cannot wait for the next BeginTabItem() call. We cannot compute this width within TabBarAddTab() because font size depends on the active window.
local tab_name = ImGui.TabBarGetTabName(tab_bar, tab)
local has_close_button_or_unsaved_marker = bit32.band(tab.Flags, 1048576 --[[ImGuiTabItemFlags.NoCloseButton]]) == 0 or bit32.band(tab.Flags, 1 --[[ImGuiTabItemFlags.UnsavedDocument]]) ~= 0
tab.ContentWidth = (tab.RequestedWidth >= 0.0) and tab.RequestedWidth or ImGui.TabItemCalcSize(tab_name, has_close_button_or_unsaved_marker).x
if bit32.band(tab.Flags, 2097152 --[[ImGuiTabItemFlags.Button]]) == 0 then
tab.ContentWidth = ImMax(tab.ContentWidth, g.Style.TabMinWidthBase)
end
local section_n = TabItemGetSectionIdx(tab)
local section = sections[section_n + 1]
section.Width = section.Width + tab.ContentWidth + ((section_n == curr_section_n) and tab_spacing or 0.0)
section.WidthAfterShrinkMinWidth = section.WidthAfterShrinkMinWidth + ImMin(tab.ContentWidth, shrink_min_width) + ((section_n == curr_section_n) and tab_spacing or 0.0)
curr_section_n = section_n
-- Store data so we can build an array sorted by width if we need to shrink tabs down
local item = swb.Data[shrink_buffer_indexes[section_n + 1] + 1]
shrink_buffer_indexes[section_n + 1] = shrink_buffer_indexes[section_n + 1] + 1
item.Index = tab_n
item.Width = tab.ContentWidth
item.InitialWidth = tab.ContentWidth
tab.Width = ImMax(tab.ContentWidth, 1.0)
end
-- Compute total ideal width (used for e.g. auto-resizing a window)
local width_all_tabs_after_min_width_shrink = 0.0
tab_bar.WidthAllTabsIdeal = 0.0
for section_n = 1, 3 do
tab_bar.WidthAllTabsIdeal = tab_bar.WidthAllTabsIdeal + sections[section_n].Width + sections[section_n].Spacing
width_all_tabs_after_min_width_shrink = width_all_tabs_after_min_width_shrink + sections[section_n].WidthAfterShrinkMinWidth + sections[section_n].Spacing
end
-- Horizontal scrolling buttons
-- - Important: note that TabBarScrollButtons() will alter BarRect.Max.x.
-- - Selection skips buttons and might cross through sections if there are Tabs in Leading/Trailing sections.
local can_scroll = bit32.band(tab_bar.Flags, 512 --[[ImGuiTabBarFlags.FittingPolicyScroll]]) ~= 0 or bit32.band(tab_bar.Flags, 128 --[[ImGuiTabBarFlags.FittingPolicyMixed]]) ~= 0
local width_all_tabs_to_use_for_scroll = (bit32.band(tab_bar.Flags, 512 --[[ImGuiTabBarFlags.FittingPolicyScroll]]) ~= 0) and tab_bar.WidthAllTabs or width_all_tabs_after_min_width_shrink
tab_bar.ScrollButtonEnabled = (width_all_tabs_to_use_for_scroll > tab_bar.BarRect:GetWidth() and tabs.Size > 1)
and bit32.band(tab_bar.Flags, 16 --[[ImGuiTabBarFlags.NoTabListScrollingButtons]]) == 0 and can_scroll
if tab_bar.ScrollButtonEnabled then
local scroll_and_select_tab = ImGui.TabBarScrollingButtons(tab_bar)
if scroll_and_select_tab then
if bit32.band(scroll_and_select_tab.Flags, 2097152 --[[ImGuiTabItemFlags.Button]]) == 0 then
tab_bar.SelectedTabId = scroll_and_select_tab.ID
end
scroll_to_tab_id = scroll_and_select_tab.ID
end
end
if scroll_to_tab_id == 0 and scroll_to_selected_tab then
scroll_to_tab_id = tab_bar.SelectedTabId
end
-- Shrink widths if full tabs don't fit in their allocated space
local section_0_w = sections[1].Width + sections[1].Spacing
local section_1_w = sections[2].Width + sections[2].Spacing
local section_2_w = sections[3].Width + sections[3].Spacing
local central_section_is_visible = (section_0_w + section_2_w) < tab_bar.BarRect:GetWidth()
local width_excess
if central_section_is_visible then
width_excess = ImMax(section_1_w - (tab_bar.BarRect:GetWidth() - section_0_w - section_2_w), 0.0) -- Excess used to shrink central section
else
width_excess = (section_0_w + section_2_w) - tab_bar.BarRect:GetWidth() -- Excess used to shrink leading/trailing section
end
-- With ImGuiTabBarFlags.FittingPolicyScroll policy, we will only shrink leading/trailing if the central section is not visible anymore
local can_shrink = bit32.band(tab_bar.Flags, 256 --[[ImGuiTabBarFlags.FittingPolicyShrink]]) ~= 0 or bit32.band(tab_bar.Flags, 128 --[[ImGuiTabBarFlags.FittingPolicyMixed]]) ~= 0
if width_excess >= 1.0 and (can_shrink or not central_section_is_visible) then
local shrink_data_count = central_section_is_visible and sections[2].TabCount or (sections[1].TabCount + sections[3].TabCount)
local shrink_data_offset = central_section_is_visible and (sections[1].TabCount + sections[3].TabCount) or 0
ImGui.ShrinkWidths(swb.Data, shrink_data_offset + 1, shrink_data_count, width_excess, shrink_min_width)
-- Apply shrunk values into tabs and sections
for tab_n = shrink_data_offset, shrink_data_offset + shrink_data_count - 1 do
local item = swb.Data[tab_n + 1]
local tab = tabs.Data[item.Index + 1]
local shrinked_width = IM_TRUNC(item.Width)
if shrinked_width >= 0.0 then
shrinked_width = ImMax(1.0, shrinked_width)
local section_n = TabItemGetSectionIdx(tab)
sections[section_n + 1].Width = sections[section_n + 1].Width - (tab.Width - shrinked_width)
tab.Width = shrinked_width
end
end
end
-- Layout all active tabs
local section_tab_index = 0
local tab_offset = 0.0
tab_bar.WidthAllTabs = 0.0
for section_n = 0, 2 do
local section = sections[section_n + 1]
if section_n == 2 then
tab_offset = ImMin(ImMax(0.0, tab_bar.BarRect:GetWidth() - section.Width), tab_offset)
end
for tab_n = 0, section.TabCount - 1 do
local tab = tabs.Data[section_tab_index + tab_n + 1]
tab.Offset = tab_offset
tab.NameOffset = -1
tab_offset = tab_offset + tab.Width + ((tab_n < section.TabCount - 1) and g.Style.ItemInnerSpacing.x or 0.0)
end
tab_bar.WidthAllTabs = tab_bar.WidthAllTabs + ImMax(section.Width + section.Spacing, 0.0)
tab_offset = tab_offset + section.Spacing
section_tab_index = section_tab_index + section.TabCount
end
-- If we have lost the selected tab, select the next most recently active one
local tab_bar_appearing = (tab_bar.PrevFrameVisible + 1 < g.FrameCount)
if found_selected_tab_id == false and not tab_bar_appearing then
tab_bar.SelectedTabId = 0
end
if tab_bar.SelectedTabId == 0 and tab_bar.NextSelectedTabId == 0 and most_recently_selected_tab ~= nil then
tab_bar.SelectedTabId = most_recently_selected_tab.ID
scroll_to_tab_id = tab_bar.SelectedTabId
end
-- Lock in visible tab
tab_bar.VisibleTabId = tab_bar.SelectedTabId
tab_bar.VisibleTabWasSubmitted = false
-- CTRL+TAB can override visible tab temporarily
if g.NavWindowingTarget ~= nil and g.NavWindowingTarget.DockNode and g.NavWindowingTarget.DockNode.TabBar == tab_bar then
tab_bar.VisibleTabId = g.NavWindowingTarget.TabId
scroll_to_tab_id = tab_bar.VisibleTabId
end
-- Apply request requests
if scroll_to_tab_id ~= 0 then
ImGui.TabBarScrollToTab(tab_bar, scroll_to_tab_id, sections)
elseif tab_bar.ScrollButtonEnabled and ImGui.IsMouseHoveringRect(tab_bar.BarRect.Min, tab_bar.BarRect.Max, true) and ImGui.IsWindowContentHoverable(g.CurrentWindow) then
local wheel = g.IO.MouseWheelRequestAxisSwap and g.IO.MouseWheel or g.IO.MouseWheelH
local wheel_key = g.IO.MouseWheelRequestAxisSwap and 662 --[[ImGuiKey.MouseWheelY]] or 661 --[[ImGuiKey.MouseWheelX]]
if ImGui.TestKeyOwner(wheel_key, tab_bar.ID) and wheel ~= 0.0 then
local scroll_step = wheel * TabBarCalcScrollableWidth(tab_bar, sections) / 3.0
tab_bar.ScrollingTargetDistToVisibility = 0.0
tab_bar.ScrollingTarget = ImGui.TabBarScrollClamp(tab_bar, tab_bar.ScrollingTarget - scroll_step)
end
ImGui.SetKeyOwner(wheel_key, tab_bar.ID)
end
-- Update scrolling
tab_bar.ScrollingAnim = ImGui.TabBarScrollClamp(tab_bar, tab_bar.ScrollingAnim)
tab_bar.ScrollingTarget = ImGui.TabBarScrollClamp(tab_bar, tab_bar.ScrollingTarget)
if tab_bar.ScrollingAnim ~= tab_bar.ScrollingTarget then
-- Scrolling speed adjust itself so we can always reach our target in 1/3 seconds.
-- Teleport if we are aiming far off the visible line
tab_bar.ScrollingSpeed = ImMax(tab_bar.ScrollingSpeed, 70.0 * g.FontSize)
tab_bar.ScrollingSpeed = ImMax(tab_bar.ScrollingSpeed, math.abs(tab_bar.ScrollingTarget - tab_bar.ScrollingAnim) / 0.3)
local teleport = (tab_bar.PrevFrameVisible + 1 < g.FrameCount) or (tab_bar.ScrollingTargetDistToVisibility > 10.0 * g.FontSize)
tab_bar.ScrollingAnim = teleport and tab_bar.ScrollingTarget or ImLinearSweep(tab_bar.ScrollingAnim, tab_bar.ScrollingTarget, g.IO.DeltaTime * tab_bar.ScrollingSpeed)
else
tab_bar.ScrollingSpeed = 0.0
end
tab_bar.ScrollingRectMinX = tab_bar.BarRect.Min.x + sections[1].Width + sections[1].Spacing
tab_bar.ScrollingRectMaxX = tab_bar.BarRect.Max.x - sections[3].Width - sections[2].Spacing
-- Actual layout in host window (we don't do it in BeginTabBar() so as not to waste an extra frame)
local window = g.CurrentWindow
ImVec2_Copy(window.DC.CursorPos, tab_bar.BarRect.Min)
ImGui.ItemSize(ImVec2(tab_bar.WidthAllTabs, tab_bar.BarRect:GetHeight()), tab_bar.FramePadding.y)
window.DC.IdealMaxPos.x = ImMax(window.DC.IdealMaxPos.x, tab_bar.BarRect.Min.x + tab_bar.WidthAllTabsIdeal)
end
-- Dockable windows uses Name/ID in the global namespace. Non-dockable items use the ID stack.
function ImGui.TabBarCalcTabID(tab_bar, label, docked_window)
if docked_window ~= nil then
assert(bit32.band(tab_bar.Flags, 1048576 --[[ImGuiTabBarFlags.DockNode]]) ~= 0)
local id = docked_window.TabId
ImGui.KeepAliveID(id)
return id
end
return ImGui.GetCurrentContext().CurrentWindow:GetID(label)
end
function ImGui.TabBarCalcMaxTabWidth()
return ImGui.GetCurrentContext().FontSize * 20.0
end
function ImGui.TabBarFindTabByID(tab_bar, tab_id)
if tab_id ~= 0 then
for n = 1, tab_bar.Tabs.Size do
local tab = tab_bar.Tabs.Data[n]
if tab.ID == tab_id then return tab end
end
end
return nil
end
-- Order = visible order, not submission order! (which is tab.BeginOrder)
--- @param order int # 0-based visible order
function ImGui.TabBarFindTabByOrder(tab_bar, order)
if order < 0 or order >= tab_bar.Tabs.Size then return nil end
return tab_bar.Tabs.Data[order + 1]
end
--- @return int # 0-based order
function ImGui.TabBarGetTabOrder(tab_bar, tab)
local i = tab_bar.Tabs:find_index(tab)
return i and (i - 1) or -1
end
-- FIXME: See references to #2304 in TODO.txt
function ImGui.TabBarFindMostRecentlySelectedTabForActiveWindow(tab_bar)
local most_recently_selected_tab = nil
for n = 1, tab_bar.Tabs.Size do
local tab = tab_bar.Tabs.Data[n]
if most_recently_selected_tab == nil or most_recently_selected_tab.LastFrameSelected < tab.LastFrameSelected then
if tab.Window and tab.Window.WasActive then most_recently_selected_tab = tab end
end
end
return most_recently_selected_tab
end
function ImGui.TabBarGetCurrentTab(tab_bar)
if tab_bar.LastTabItemIdx < 0 or tab_bar.LastTabItemIdx >= tab_bar.Tabs.Size then return nil end
return tab_bar.Tabs.Data[tab_bar.LastTabItemIdx + 1]
end
function ImGui.TabBarGetTabName(tab_bar, tab)
if tab.Window then return tab.Window.Name end
if tab.Name == nil then return "N/A" end
return tab.Name
end
function ImGui.TabBarGetTabPos(tab_bar, tab)
if bit32.band(tab.Flags, 192 --[[ImGuiTabItemFlags.SectionMask_]]) == 0 then
return tab_bar.BarRect.Min + ImVec2(IM_TRUNC(tab.Offset - tab_bar.ScrollingAnim), 0.0)
end
return tab_bar.BarRect.Min + ImVec2(tab.Offset, 0.0)
end
-- The purpose of this call is to register tab in advance so we can control their order at the time they appear.
-- Otherwise calling this is unnecessary as tabs are appending as needed by the BeginTabItem() function.
function ImGui.TabBarAddTab(tab_bar, tab_flags, window)
local g = ImGui.GetCurrentContext()
assert(ImGui.TabBarFindTabByID(tab_bar, window.TabId) == nil)
assert(g.CurrentTabBar ~= tab_bar) -- Can't work while the tab bar is active as our tab doesn't have an X offset yet, in theory we could/should test something like (tab_bar.CurrFrameVisible < g.FrameCount) but we'd need to solve why triggers the commented early-out assert in BeginTabBarEx() (probably dock node going from implicit to explicit in same frame)
if not window.HasCloseButton then
tab_flags = bit32.bor(tab_flags, 1048576 --[[ImGuiTabItemFlags.NoCloseButton]]) -- Set _NoCloseButton immediately because it will be used for first-frame width calculation.
end
local new_tab = ImGuiTabItem()
new_tab.ID = window.TabId
new_tab.Flags = tab_flags
new_tab.LastFrameVisible = tab_bar.CurrFrameVisible -- Required so BeginTabBar() doesn't ditch the tab
if new_tab.LastFrameVisible == -1 then new_tab.LastFrameVisible = g.FrameCount - 1 end
new_tab.Window = window -- Required so tab bar layout can compute the tab width before tab submission
tab_bar.Tabs:push_back(new_tab)
end
-- The *TabId fields are already set by the docking system _before_ the actual TabItem was created, so we clear them regardless.
function ImGui.TabBarRemoveTab(tab_bar, tab_id)
local tab = ImGui.TabBarFindTabByID(tab_bar, tab_id)
if tab then tab_bar.Tabs:erase(tab_bar.Tabs:find_index(tab)) end
if tab_bar.VisibleTabId == tab_id then tab_bar.VisibleTabId = 0 end
if tab_bar.SelectedTabId == tab_id then tab_bar.SelectedTabId = 0 end
if tab_bar.NextSelectedTabId == tab_id then tab_bar.NextSelectedTabId = 0 end
end
-- Called on manual closure attempt
function ImGui.TabBarCloseTab(tab_bar, tab)
if bit32.band(tab.Flags, 2097152 --[[ImGuiTabItemFlags.Button]]) ~= 0 then return end -- A button appended with TabItemButton().
if bit32.band(tab.Flags, bit32.bor(1 --[[ImGuiTabItemFlags.UnsavedDocument]], 256 --[[ImGuiTabItemFlags.NoAssumedClosure]])) == 0 then
-- This will remove a frame of lag for selecting another tab on closure.
-- However we don't run it in the case where the 'Unsaved' flag is set, so user gets a chance to fully undo the closure
tab.WantClose = true
if tab_bar.VisibleTabId == tab.ID then
tab.LastFrameVisible = -1
tab_bar.SelectedTabId = 0
tab_bar.NextSelectedTabId = 0
end
else
-- Actually select before expecting closure attempt (on an UnsavedDocument tab user is expect to e.g. show a popup)
if tab_bar.VisibleTabId ~= tab.ID then ImGui.TabBarQueueFocus(tab_bar, tab) end
end
end
function ImGui.TabBarScrollClamp(tab_bar, scrolling)
scrolling = ImMin(scrolling, tab_bar.WidthAllTabs - tab_bar.BarRect:GetWidth())
return ImMax(scrolling, 0.0)
end
-- Note: we may scroll to tab that are not selected! e.g. using keyboard arrow keys
function ImGui.TabBarScrollToTab(tab_bar, tab_id, sections)
local tab = ImGui.TabBarFindTabByID(tab_bar, tab_id)
if tab == nil then return end
-- Clamp attempt to scroll to leading/trailing sections items.
-- This could be done at the TabBarScrollingButtons() call site as well, but here works.
if bit32.band(tab.Flags, 64 --[[ImGuiTabItemFlags.Leading]]) ~= 0 then
tab = tab_bar.Tabs.Data[sections[1].TabCount + 1]
elseif bit32.band(tab.Flags, 128 --[[ImGuiTabItemFlags.Trailing]]) ~= 0 then
tab = tab_bar.Tabs.Data[sections[1].TabCount + sections[2].TabCount + 1]
end
if tab == nil or bit32.band(tab.Flags, 192 --[[ImGuiTabItemFlags.SectionMask_]]) ~= 0 then return end
-- When scrolling to make Tab N+1 visible always make a bit of N visible to suggest more scrolling area (since we don't have a scrollbar)
-- Disable the margin if the scrolling section is too small for the target tab: prefer displaying a maximum of the label.
local g = ImGui.GetCurrentContext()
local margin = ImClamp(tab_bar.ScrollingRectMaxX - tab_bar.ScrollingRectMinX - tab.Width, g.Style.ItemInnerSpacing.x, g.FontSize * 1.0)
local order = ImGui.TabBarGetTabOrder(tab_bar, tab)
-- Scrolling happens only in the central section (leading/trailing sections are not scrolling)
local scrollable_width = TabBarCalcScrollableWidth(tab_bar, sections)
-- We make all tabs positions all relative Sections[0].Width to make code simpler
local tab_x1 = tab.Offset - sections[1].Width + ((order > sections[1].TabCount - 1) and -margin or 0.0)
local tab_x2 = tab.Offset - sections[1].Width + tab.Width + ((order + 1 < tab_bar.Tabs.Size - sections[3].TabCount) and margin or 1.0)
tab_bar.ScrollingTargetDistToVisibility = 0.0
if tab_bar.ScrollingTarget > tab_x1 or (tab_x2 - tab_x1 >= scrollable_width) then
-- Scroll to the left
tab_bar.ScrollingTargetDistToVisibility = ImMax(tab_bar.ScrollingAnim - tab_x2, 0.0)
tab_bar.ScrollingTarget = tab_x1
elseif tab_bar.ScrollingTarget < tab_x2 - scrollable_width then
-- Scroll to the right
tab_bar.ScrollingTargetDistToVisibility = ImMax((tab_x1 - scrollable_width) - tab_bar.ScrollingAnim, 0.0)
tab_bar.ScrollingTarget = tab_x2 - scrollable_width
end
end
--- TabBarQueueFocus(tab_bar, tab) or TabBarQueueFocus(tab_bar, tab_name)
function ImGui.TabBarQueueFocus(tab_bar, tab)
if type(tab) == "string" then
assert(bit32.band(tab_bar.Flags, 1048576 --[[ImGuiTabBarFlags.DockNode]]) == 0) -- Only supported for manual/explicit tab bars
tab_bar.NextSelectedTabId = ImGui.TabBarCalcTabID(tab_bar, tab, nil)
return
end
tab_bar.NextSelectedTabId = tab.ID
end
function ImGui.TabBarQueueReorder(tab_bar, tab, offset)
assert(offset ~= 0)
assert(tab_bar.ReorderRequestTabId == 0)
tab_bar.ReorderRequestTabId = tab.ID
tab_bar.ReorderRequestOffset = offset
end
function ImGui.TabBarQueueReorderFromMousePos(tab_bar, src_tab, mouse_pos)
local g = ImGui.GetCurrentContext()
assert(tab_bar.ReorderRequestTabId == 0)
if bit32.band(tab_bar.Flags, 1 --[[ImGuiTabBarFlags.Reorderable]]) == 0 then return end
local tab_spacing = g.Style.ItemInnerSpacing.x
local is_central_section = bit32.band(src_tab.Flags, 192 --[[ImGuiTabItemFlags.SectionMask_]]) == 0
local bar_offset = tab_bar.BarRect.Min.x - (is_central_section and tab_bar.ScrollingTarget or 0)
-- Count number of contiguous tabs we are crossing over
local dir = ((bar_offset + src_tab.Offset) > mouse_pos.x) and -1 or 1
local src_idx = ImGui.TabBarGetTabOrder(tab_bar, src_tab)
local dst_idx = src_idx
local i = src_idx
while i >= 0 and i < tab_bar.Tabs.Size do
-- Reordered tabs must share the same section
local dst_tab = tab_bar.Tabs.Data[i + 1]
if bit32.band(dst_tab.Flags, 32 --[[ImGuiTabItemFlags.NoReorder]]) ~= 0 then break end
if bit32.band(dst_tab.Flags, 192 --[[ImGuiTabItemFlags.SectionMask_]]) ~= bit32.band(src_tab.Flags, 192 --[[ImGuiTabItemFlags.SectionMask_]]) then break end
dst_idx = i
-- Include spacing after tab, so when mouse cursor is between tabs we would not continue checking further tabs that are not hovered.
local x1 = bar_offset + dst_tab.Offset - tab_spacing
local x2 = bar_offset + dst_tab.Offset + dst_tab.Width + tab_spacing
-- ImGui.GetForegroundDrawList():AddRect(ImVec2(x1, tab_bar.BarRect.Min.y), ImVec2(x2, tab_bar.BarRect.Max.y), IM_COL32(255, 0, 0, 255))
if (dir < 0 and mouse_pos.x > x1) or (dir > 0 and mouse_pos.x < x2) then break end
i = i + dir
end
if dst_idx ~= src_idx then
ImGui.TabBarQueueReorder(tab_bar, src_tab, dst_idx - src_idx)
end
end
function ImGui.TabBarProcessReorder(tab_bar)
local tab1 = ImGui.TabBarFindTabByID(tab_bar, tab_bar.ReorderRequestTabId)
if tab1 == nil or bit32.band(tab1.Flags, 32 --[[ImGuiTabItemFlags.NoReorder]]) ~= 0 then return false end
-- IM_ASSERT(bit32.band(tab_bar.Flags, ImGuiTabBarFlags.Reorderable) ~= 0) -- <- this may happen when using debug tools
local tab1_order = ImGui.TabBarGetTabOrder(tab_bar, tab1)
local tab2_order = tab1_order + tab_bar.ReorderRequestOffset
if tab2_order < 0 or tab2_order >= tab_bar.Tabs.Size then return false end
-- Reordered tabs must share the same section
-- (Note: TabBarQueueReorderFromMousePos() also has a similar test but since we allow direct calls to TabBarQueueReorder() we do it here too)
local tab2 = tab_bar.Tabs.Data[tab2_order + 1]
if bit32.band(tab2.Flags, 32 --[[ImGuiTabItemFlags.NoReorder]]) ~= 0 then return false end
if bit32.band(tab1.Flags, 192 --[[ImGuiTabItemFlags.SectionMask_]]) ~= bit32.band(tab2.Flags, 192 --[[ImGuiTabItemFlags.SectionMask_]]) then return false end
-- Move tab1 to tab2's position, shifting the tabs in between
table.remove(tab_bar.Tabs.Data, tab1_order + 1)
table.insert(tab_bar.Tabs.Data, tab2_order + 1, tab1)
if bit32.band(tab_bar.Flags, 4194304 --[[ImGuiTabBarFlags.SaveSettings]]) ~= 0 then ImGui.MarkIniSettingsDirty() end
return true
end
function ImGui.TabBarScrollingButtons(tab_bar)
local g = ImGui.GetCurrentContext()
local window = g.CurrentWindow
local arrow_button_size = ImVec2(g.FontSize - 2.0, g.FontSize + g.Style.FramePadding.y * 2.0)
local scrolling_buttons_width = arrow_button_size.x * 2.0
local backup_cursor_pos = ImVec2(window.DC.CursorPos.x, window.DC.CursorPos.y)
-- window.DrawList:AddRect(ImVec2(tab_bar.BarRect.Max.x - scrolling_buttons_width, tab_bar.BarRect.Min.y), ImVec2(tab_bar.BarRect.Max.x, tab_bar.BarRect.Max.y), IM_COL32(255,0,0,255))
local select_dir = 0
local tc = g.Style.Colors[0 --[[ImGuiCol.Text]]]
local arrow_col = ImVec4(tc.x, tc.y, tc.z, tc.w * 0.5)
ImGui.PushStyleColor(0 --[[ImGuiCol.Text]], arrow_col)
ImGui.PushStyleColor(22 --[[ImGuiCol.Button]], ImVec4(0, 0, 0, 0))
ImGui.PushItemFlag(bit32.bor(8 --[[ImGuiItemFlags.ButtonRepeat]], 2 --[[ImGuiItemFlags.NoNav]]), true)
local backup_repeat_delay = g.IO.KeyRepeatDelay
local backup_repeat_rate = g.IO.KeyRepeatRate
g.IO.KeyRepeatDelay = 0.250
g.IO.KeyRepeatRate = 0.200
local x = ImMax(tab_bar.BarRect.Min.x, tab_bar.BarRect.Max.x - scrolling_buttons_width)
ImVec2_CopyV(window.DC.CursorPos, x, tab_bar.BarRect.Min.y)
if ImGui.ArrowButtonEx("##<", 0 --[[ImGuiDir.Left]], arrow_button_size, 16 --[[ImGuiButtonFlags.PressedOnClick]]) then select_dir = -1 end
ImVec2_CopyV(window.DC.CursorPos, x + arrow_button_size.x, tab_bar.BarRect.Min.y)
if ImGui.ArrowButtonEx("##>", 1 --[[ImGuiDir.Right]], arrow_button_size, 16 --[[ImGuiButtonFlags.PressedOnClick]]) then select_dir = 1 end
ImGui.PopItemFlag()
ImGui.PopStyleColor(2)
g.IO.KeyRepeatRate = backup_repeat_rate
g.IO.KeyRepeatDelay = backup_repeat_delay
local tab_to_scroll_to = nil
if select_dir ~= 0 then
local tab_item = ImGui.TabBarFindTabByID(tab_bar, tab_bar.SelectedTabId)
if tab_item then
local selected_order = ImGui.TabBarGetTabOrder(tab_bar, tab_item)
local target_order = selected_order + select_dir
-- Skip tab item buttons until another tab item is found or end is reached
while tab_to_scroll_to == nil do
-- If we are at the end of the list, still scroll to make our tab visible
local idx = (target_order >= 0 and target_order < tab_bar.Tabs.Size) and target_order or selected_order
tab_to_scroll_to = tab_bar.Tabs.Data[idx + 1]
-- Cross through buttons
-- (even if first/last item is a button, return it so we can update the scroll)
if bit32.band(tab_to_scroll_to.Flags, 2097152 --[[ImGuiTabItemFlags.Button]]) ~= 0 then
target_order = target_order + select_dir
selected_order = selected_order + select_dir
if not (target_order < 0 or target_order >= tab_bar.Tabs.Size) then tab_to_scroll_to = nil end
end
end
end
end
ImVec2_Copy(window.DC.CursorPos, backup_cursor_pos)
tab_bar.BarRect.Max.x = tab_bar.BarRect.Max.x - (scrolling_buttons_width + 1.0)
return tab_to_scroll_to
end
function ImGui.TabBarTabListPopupButton(tab_bar)
local g = ImGui.GetCurrentContext()
local window = g.CurrentWindow
-- We use g.Style.FramePadding.y to match the square ArrowButton size
local tab_list_popup_button_width = g.FontSize + g.Style.FramePadding.y
local backup_cursor_pos = ImVec2(window.DC.CursorPos.x, window.DC.CursorPos.y)
ImVec2_CopyV(window.DC.CursorPos, tab_bar.BarRect.Min.x - g.Style.FramePadding.y, tab_bar.BarRect.Min.y)
tab_bar.BarRect.Min.x = tab_bar.BarRect.Min.x + tab_list_popup_button_width
local tc = g.Style.Colors[0 --[[ImGuiCol.Text]]]
ImGui.PushStyleColor(0 --[[ImGuiCol.Text]], ImVec4(tc.x, tc.y, tc.z, tc.w * 0.5))
ImGui.PushStyleColor(22 --[[ImGuiCol.Button]], ImVec4(0, 0, 0, 0))
local open = ImGui.BeginCombo("##v", nil, bit32.bor(64 --[[ImGuiComboFlags.NoPreview]], 16 --[[ImGuiComboFlags.HeightLargest]]))
ImGui.PopStyleColor(2)
local tab_to_select = nil
if open then
for n = 1, tab_bar.Tabs.Size do
local tab = tab_bar.Tabs.Data[n]
if bit32.band(tab.Flags, 2097152 --[[ImGuiTabItemFlags.Button]]) == 0 then
local tab_name = ImGui.TabBarGetTabName(tab_bar, tab)
if ImGui.Selectable(tab_name, tab_bar.SelectedTabId == tab.ID) then
tab_to_select = tab
end
end
end
ImGui.EndCombo()
end
ImVec2_Copy(window.DC.CursorPos, backup_cursor_pos)
return tab_to_select
end
-------------------------------------------------------------------------------
-- [SECTION] Widgets: BeginTabItem, EndTabItem, etc.
-------------------------------------------------------------------------------
-- - BeginTabItem()
-- - EndTabItem()
-- - TabItemButton()
-- - TabItemEx() [Internal]
-- - SetTabItemClosed()
-- - TabItemCalcSize() [Internal]
-- - TabItemBackground() [Internal]
-- - TabItemLabelAndCloseButton() [Internal]
-------------------------------------------------------------------------------
--- @return bool visible, bool? p_open
function ImGui.BeginTabItem(label, p_open, flags)
if flags == nil then flags = 0 end
local g = ImGui.GetCurrentContext()
local window = g.CurrentWindow
if window.SkipItems then return false, p_open end
local tab_bar = g.CurrentTabBar
assert(tab_bar ~= nil, "Needs to be called between BeginTabBar() and EndTabBar()!")
assert(bit32.band(flags, 2097152 --[[ImGuiTabItemFlags.Button]]) == 0) -- BeginTabItem() Can't be used with button flags, use TabItemButton() instead!
local ret
ret, p_open = ImGui.TabItemEx(tab_bar, label, p_open, flags, nil)
if ret and bit32.band(flags, 8 --[[ImGuiTabItemFlags.NoPushId]]) == 0 then
local tab = tab_bar.Tabs.Data[tab_bar.LastTabItemIdx + 1]
ImGui.PushOverrideID(tab.ID) -- We already hashed 'label' so push into the ID stack directly instead of doing another hash through PushID(label)
end
return ret, p_open
end
function ImGui.EndTabItem()
local g = ImGui.GetCurrentContext()
local window = g.CurrentWindow
if window.SkipItems then return end
local tab_bar = g.CurrentTabBar
assert(tab_bar ~= nil, "Needs to be called between BeginTabBar() and EndTabBar()!")
assert(tab_bar.LastTabItemIdx >= 0)
local tab = tab_bar.Tabs.Data[tab_bar.LastTabItemIdx + 1]
if bit32.band(tab.Flags, 8 --[[ImGuiTabItemFlags.NoPushId]]) == 0 then
ImGui.PopID()
end
end
function ImGui.TabItemButton(label, flags)
if flags == nil then flags = 0 end
local g = ImGui.GetCurrentContext()
local window = g.CurrentWindow
if window.SkipItems then return false end
local tab_bar = g.CurrentTabBar
assert(tab_bar ~= nil, "Needs to be called between BeginTabBar() and EndTabBar()!")
return (ImGui.TabItemEx(tab_bar, label, nil, bit32.bor(flags, 2097152 --[[ImGuiTabItemFlags.Button]], 32 --[[ImGuiTabItemFlags.NoReorder]]), nil))
end
function ImGui.TabItemSpacing(str_id, flags, width)
local g = ImGui.GetCurrentContext()
local window = g.CurrentWindow
if window.SkipItems then return end
local tab_bar = g.CurrentTabBar
assert(tab_bar ~= nil, "Needs to be called between BeginTabBar() and EndTabBar()!")
ImGui.SetNextItemWidth(width)
ImGui.TabItemEx(tab_bar, str_id, nil, bit32.bor(flags or 0, 2097152 --[[ImGuiTabItemFlags.Button]], 32 --[[ImGuiTabItemFlags.NoReorder]], 4194304 --[[ImGuiTabItemFlags.Invisible]]), nil)
end
--- @return bool ret, bool? p_open
function ImGui.TabItemEx(tab_bar, label, p_open, flags, docked_window)
-- Layout whole tab bar if not already done
local g = ImGui.GetCurrentContext()
if tab_bar.WantLayout then
local backup_next_item_data = {}
for k, v in pairs(g.NextItemData) do backup_next_item_data[k] = v end
ImGui.TabBarLayout(tab_bar)
for k, v in pairs(backup_next_item_data) do g.NextItemData[k] = v end
end
local window = g.CurrentWindow
if window.SkipItems then return false, p_open end
local style = g.Style
local id = ImGui.TabBarCalcTabID(tab_bar, label, docked_window)
-- If the user called us with p_open == false, we early out and don't render.
-- We make a call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID.
if p_open ~= nil and not p_open then
ImGui.ItemAdd(ImRect(), id, nil, 2 --[[ImGuiItemFlags.NoNav]])
return false, p_open
end
assert(p_open == nil or bit32.band(flags, 2097152 --[[ImGuiTabItemFlags.Button]]) == 0)
assert(bit32.band(flags, bit32.bor(64 --[[ImGuiTabItemFlags.Leading]], 128 --[[ImGuiTabItemFlags.Trailing]])) ~= bit32.bor(64 --[[ImGuiTabItemFlags.Leading]], 128 --[[ImGuiTabItemFlags.Trailing]])) -- Can't use both Leading and Trailing
-- Store into ImGuiTabItemFlags.NoCloseButton, also honor ImGuiTabItemFlags.NoCloseButton passed by user (although not documented)
local has_p_open = (p_open ~= nil)
if bit32.band(flags, 1048576 --[[ImGuiTabItemFlags.NoCloseButton]]) ~= 0 then
has_p_open = false
elseif not has_p_open then
flags = bit32.bor(flags, 1048576 --[[ImGuiTabItemFlags.NoCloseButton]])
end
-- Acquire tab data
local tab = ImGui.TabBarFindTabByID(tab_bar, id)
local tab_is_new = false
if tab == nil then
tab = ImGuiTabItem()
tab_bar.Tabs:push_back(tab)
tab.ID = id
tab_bar.TabsAddedNew = true
tab_is_new = true
end
tab_bar.LastTabItemIdx = ImGui.TabBarGetTabOrder(tab_bar, tab)
-- Calculate tab contents size
local size = ImGui.TabItemCalcSize(label, has_p_open or bit32.band(flags, 1 --[[ImGuiTabItemFlags.UnsavedDocument]]) ~= 0)
tab.RequestedWidth = -1.0
if bit32.band(g.NextItemData.HasFlags, 1 --[[ImGuiNextItemDataFlags.HasWidth]]) ~= 0 then
size.x = g.NextItemData.Width
tab.RequestedWidth = size.x
end
if tab_is_new then tab.Width = ImMax(1.0, size.x) end
tab.ContentWidth = size.x
tab.BeginOrder = tab_bar.TabsActiveCount
tab_bar.TabsActiveCount = tab_bar.TabsActiveCount + 1
local tab_bar_appearing = (tab_bar.PrevFrameVisible + 1 < g.FrameCount)
local tab_bar_focused = bit32.band(tab_bar.Flags, 2097152 --[[ImGuiTabBarFlags.IsFocused]]) ~= 0
local tab_appearing = (tab.LastFrameVisible + 1 < g.FrameCount)
local tab_just_unsaved = bit32.band(flags, 1 --[[ImGuiTabItemFlags.UnsavedDocument]]) ~= 0 and bit32.band(tab.Flags, 1 --[[ImGuiTabItemFlags.UnsavedDocument]]) == 0
local is_tab_button = bit32.band(flags, 2097152 --[[ImGuiTabItemFlags.Button]]) ~= 0
tab.LastFrameVisible = g.FrameCount
tab.Flags = flags
tab.Window = docked_window
-- Append name (port: stored on tab.Name)
-- (regular tabs are permitted in a DockNode tab bar, but window tabs not permitted in a non-DockNode tab bar)
if docked_window ~= nil then
assert(bit32.band(tab_bar.Flags, 1048576 --[[ImGuiTabBarFlags.DockNode]]) ~= 0)
tab.NameOffset = -1
tab.Name = nil
else
tab.NameOffset = 0
tab.Name = label
end
-- Update selected tab
if not is_tab_button then
if tab_appearing and bit32.band(tab_bar.Flags, 2 --[[ImGuiTabBarFlags.AutoSelectNewTabs]]) ~= 0 and tab_bar.NextSelectedTabId == 0 then
if not tab_bar_appearing or tab_bar.SelectedTabId == 0 then
ImGui.TabBarQueueFocus(tab_bar, tab) -- New tabs gets activated
end
end
if bit32.band(flags, 2 --[[ImGuiTabItemFlags.SetSelected]]) ~= 0 and tab_bar.SelectedTabId ~= id then -- _SetSelected can only be passed on explicit tab bar
ImGui.TabBarQueueFocus(tab_bar, tab)
end
end
-- Lock visibility
-- (Note: tab_contents_visible ~= tab_selected... because Ctrl+Tab operations may preview some tabs without selecting them!)
local tab_contents_visible = (tab_bar.VisibleTabId == id)
if tab_contents_visible then tab_bar.VisibleTabWasSubmitted = true end
-- On the very first frame of a tab bar we let first tab contents be visible to minimize appearing glitches
if not tab_contents_visible and tab_bar.SelectedTabId == 0 and tab_bar_appearing and docked_window == nil then
if tab_bar.Tabs.Size == 1 and bit32.band(tab_bar.Flags, 2 --[[ImGuiTabBarFlags.AutoSelectNewTabs]]) == 0 then
tab_contents_visible = true
end
end
-- Note that tab_is_new is not necessarily the same as tab_appearing! When a tab bar stops being submitted
-- and then gets submitted again, the tabs will have 'tab_appearing=true' but 'tab_is_new=false'.
if tab_appearing and (not tab_bar_appearing or tab_is_new) then
ImGui.ItemAdd(ImRect(), id, nil, 2 --[[ImGuiItemFlags.NoNav]])
if is_tab_button then return false, p_open end
return tab_contents_visible, p_open
end
if tab_bar.SelectedTabId == id then tab.LastFrameSelected = g.FrameCount end
-- Backup current layout position
local backup_main_cursor_pos = ImVec2(window.DC.CursorPos.x, window.DC.CursorPos.y)
-- Layout
local is_central_section = bit32.band(tab.Flags, 192 --[[ImGuiTabItemFlags.SectionMask_]]) == 0
size.x = tab.Width
local pos = ImGui.TabBarGetTabPos(tab_bar, tab)
ImVec2_Copy(window.DC.CursorPos, pos)
local bb = ImRect(pos, pos + size)
-- We don't have CPU clipping primitives to clip the CloseButton (until it becomes a texture), so need to add an extra draw call (temporary in the case of vertical animation)
local want_clip_rect = is_central_section and (bb.Min.x < tab_bar.ScrollingRectMinX or bb.Max.x > tab_bar.ScrollingRectMaxX)
if want_clip_rect then
ImGui.PushClipRect(ImVec2(ImClamp(bb.Min.x, tab_bar.ScrollingRectMinX, tab_bar.ScrollingRectMaxX), bb.Min.y - 1), ImVec2(tab_bar.ScrollingRectMaxX, bb.Max.y), true)
end
local backup_cursor_max_pos = ImVec2(window.DC.CursorMaxPos.x, window.DC.CursorMaxPos.y)
ImGui.ItemSize(bb:GetSize(), style.FramePadding.y)
ImVec2_Copy(window.DC.CursorMaxPos, backup_cursor_max_pos)
if not ImGui.ItemAdd(bb, id) then
if want_clip_rect then ImGui.PopClipRect() end
ImVec2_Copy(window.DC.CursorPos, backup_main_cursor_pos)
return tab_contents_visible, p_open
end
-- Click to Select a tab
local button_flags = bit32.bor(is_tab_button and 32 --[[ImGuiButtonFlags.PressedOnClickRelease]] or 16 --[[ImGuiButtonFlags.PressedOnClick]], 4096 --[[ImGuiButtonFlags.AllowOverlap]])
if g.DragDropActive and not ImGuiPayload_IsDataType(g.DragDropPayload, IMGUI_PAYLOAD_TYPE_WINDOW) then -- FIXME: May be an opt-in property of the payload to disable this
button_flags = bit32.bor(button_flags, 512 --[[ImGuiButtonFlags.PressedOnDragDropHold]])
end
local hovered, held, pressed
if bit32.band(flags, 4194304 --[[ImGuiTabItemFlags.Invisible]]) ~= 0 then
hovered, held, pressed = false, false, false
else
pressed, hovered, held = ImGui.ButtonBehavior(bb, id, button_flags)
end
if pressed and not is_tab_button then ImGui.TabBarQueueFocus(tab_bar, tab) end
-- Transfer active id window so the active id is not owned by the dock host (as StartMouseMovingWindow()
-- will only do it on the drag). This allows FocusWindow() to be more conservative in how it clears active id.
if held and docked_window and g.ActiveId == id and g.ActiveIdIsJustActivated then
g.ActiveIdWindow = docked_window
end
-- Drag and drop a single floating window node moves it
local node = docked_window and docked_window.DockNode or nil
local single_floating_window_node = node and node:IsFloatingNode() and (node.Windows.Size == 1)
if held and single_floating_window_node and ImGui.IsMouseDragging(0, 0.0) then
-- Move
ImGui.StartMouseMovingWindow(docked_window)
elseif held and not tab_appearing and ImGui.IsMouseDragging(0) then
-- Drag and drop: re-order tabs
local drag_dir = 0
local drag_distance_from_edge_x = 0.0
if not g.DragDropActive and (bit32.band(tab_bar.Flags, 1 --[[ImGuiTabBarFlags.Reorderable]]) ~= 0 or docked_window ~= nil) then
-- While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x
if g.IO.MouseDelta.x < 0.0 and g.IO.MousePos.x < bb.Min.x then
drag_dir = -1
drag_distance_from_edge_x = bb.Min.x - g.IO.MousePos.x
ImGui.TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos)
elseif g.IO.MouseDelta.x > 0.0 and g.IO.MousePos.x > bb.Max.x then
drag_dir = 1
drag_distance_from_edge_x = g.IO.MousePos.x - bb.Max.x
ImGui.TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos)
end
end
-- Extract a Dockable window out of it's tab bar
local can_undock = docked_window ~= nil and bit32.band(docked_window.Flags, 4 --[[ImGuiWindowFlags.NoMove]]) == 0 and bit32.band(node.MergedFlags, 128 --[[ImGuiDockNodeFlags.NoUndocking]]) == 0
if can_undock then
-- We use a variable threshold to distinguish dragging tabs within a tab bar and extracting them out of the tab bar
local undocking_tab = (g.DragDropActive and g.DragDropPayload.SourceId == id)
if not undocking_tab then -- and (not g.IO.ConfigDockingWithShift or g.IO.KeyShift)
local threshold_base = g.FontSize
local threshold_x = threshold_base * 2.2
local threshold_y = (threshold_base * 1.5) + ImClamp((math.abs(g.IO.MouseDragMaxDistanceAbs[0].x) - threshold_base * 2.0) * 0.20, 0.0, threshold_base * 4.0)
-- ImGui.GetForegroundDrawList():AddRect(ImVec2(bb.Min.x - threshold_x, bb.Min.y - threshold_y), ImVec2(bb.Max.x + threshold_x, bb.Max.y + threshold_y), IM_COL32_WHITE) -- [DEBUG]
local distance_from_edge_y = ImMax(bb.Min.y - g.IO.MousePos.y, g.IO.MousePos.y - bb.Max.y)
if distance_from_edge_y >= threshold_y then undocking_tab = true end
if drag_distance_from_edge_x > threshold_x then
if (drag_dir < 0 and ImGui.TabBarGetTabOrder(tab_bar, tab) == 0) or (drag_dir > 0 and ImGui.TabBarGetTabOrder(tab_bar, tab) == tab_bar.Tabs.Size - 1) then
undocking_tab = true
end
end
end
if undocking_tab then
-- Undock
-- FIXME: refactor to share more code with e.g. StartMouseMovingWindow
ImGui.DockContextQueueUndockWindow(g, docked_window)
g.MovingWindow = docked_window
ImGui.SetActiveID(g.MovingWindow.MoveId, g.MovingWindow)
ImVec2_Copy(g.ActiveIdClickOffset, g.ActiveIdClickOffset - (g.MovingWindow.Pos - bb.Min))
g.ActiveIdNoClearOnFocusLoss = true
ImGui.SetActiveIdUsingAllKeyboardKeys()
end
end
end
-- Render tab shape
local is_visible = bit32.band(g.LastItemData.StatusFlags, 256 --[[ImGuiItemStatusFlags.Visible]]) ~= 0 and bit32.band(flags, 4194304 --[[ImGuiTabItemFlags.Invisible]]) == 0
if is_visible then
local display_draw_list = window.DrawList
local col_idx
if held or hovered then col_idx = 35 --[[ImGuiCol.TabHovered]]
elseif tab_contents_visible then col_idx = tab_bar_focused and 37 --[[ImGuiCol.TabSelected]] or 40 --[[ImGuiCol.TabDimmedSelected]]
else col_idx = tab_bar_focused and 36 --[[ImGuiCol.Tab]] or 39 --[[ImGuiCol.TabDimmed]] end
ImGui.TabItemBackground(display_draw_list, bb, flags, ImGui.GetColorU32(col_idx))
if tab_contents_visible and bit32.band(tab_bar.Flags, 64 --[[ImGuiTabBarFlags.DrawSelectedOverline]]) ~= 0 and style.TabBarOverlineSize > 0.0 then
-- Might be moved to TabItemBackground() ?
local tl = ImVec2(bb.Min.x, bb.Min.y + style.TabBarOverlineSize * 0.5)
local tr = ImVec2(bb.Max.x, bb.Min.y + style.TabBarOverlineSize * 0.5)
local overline_col = ImGui.GetColorU32(tab_bar_focused and 38 --[[ImGuiCol.TabSelectedOverline]] or 41 --[[ImGuiCol.TabDimmedSelectedOverline]])
if style.TabRounding > 0.0 then
local rounding = style.TabRounding
display_draw_list:PathArcToFast(tl + ImVec2(rounding, rounding), rounding, 7, 9)
display_draw_list:PathArcToFast(tr + ImVec2(-rounding, rounding), rounding, 9, 11)
display_draw_list:PathStroke(overline_col, style.TabBarOverlineSize)
else
display_draw_list:AddLine(tl, tr, overline_col, style.TabBarOverlineSize)
end
end
ImGui.RenderNavCursor(bb, id)
-- Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget.
local hovered_unblocked = ImGui.IsItemHovered(32 --[[ImGuiHoveredFlags.AllowWhenBlockedByPopup]])
if tab_bar.SelectedTabId ~= tab.ID and hovered_unblocked and (ImGui.IsMouseClicked(1) or ImGui.IsMouseReleased(1)) and not is_tab_button then
ImGui.TabBarQueueFocus(tab_bar, tab)
end
if bit32.band(tab_bar.Flags, 8 --[[ImGuiTabBarFlags.NoCloseWithMiddleMouseButton]]) ~= 0 then
flags = bit32.bor(flags, 4 --[[ImGuiTabItemFlags.NoCloseWithMiddleMouseButton]])
end
-- Render tab label, process close button
local close_button_id = has_p_open and ImGui.GetIDWithSeed("#CLOSE", nil, docked_window and docked_window.ID or id) or 0
local label_flags = tab_just_unsaved and bit32.band(flags, bit32.bnot(1 --[[ImGuiTabItemFlags.UnsavedDocument]])) or flags
local just_closed, text_clipped = ImGui.TabItemLabelAndCloseButton(display_draw_list, bb, label_flags, tab_bar.FramePadding, label, id, close_button_id, tab_contents_visible)
if just_closed and has_p_open then
p_open = false
ImGui.TabBarCloseTab(tab_bar, tab)
end
-- Forward Hovered state so IsItemHovered() after Begin() can work (even though we are technically hovering our parent)
-- That state is copied to window.DockTabItemStatusFlags by our caller.
if docked_window and (hovered or g.HoveredId == close_button_id) then
g.LastItemData.StatusFlags = bit32.bor(g.LastItemData.StatusFlags, 128 --[[ImGuiItemStatusFlags.HoveredWindow]])
end
-- Tooltip
-- (Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer-> seems ok)
-- (We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar, which g.HoveredId ignores)
-- FIXME: This is a mess.
-- FIXME: We may want disabled tab to still display the tooltip?
if text_clipped and g.HoveredId == id and not held then
if bit32.band(tab_bar.Flags, 32 --[[ImGuiTabBarFlags.NoTooltip]]) == 0 and bit32.band(tab.Flags, 16 --[[ImGuiTabItemFlags.NoTooltip]]) == 0 then
local label_end = ImGui.FindRenderedTextEnd(label)
ImGui.SetItemTooltip("%s", string.sub(label, 1, label_end - 1))
end
end
end
-- Restore main window position so user can draw there
if want_clip_rect then ImGui.PopClipRect() end
ImVec2_Copy(window.DC.CursorPos, backup_main_cursor_pos)
if is_tab_button then return pressed, p_open end
return tab_contents_visible, p_open
end
-- [Public] This is call is 100% optional but it allows to remove some one-frame glitches when a tab has been unexpectedly removed.
-- To use it to need to call the function SetTabItemClosed() between BeginTabBar() and EndTabBar().
-- Tabs closed by the close button will automatically be flagged to avoid this issue.
function ImGui.SetTabItemClosed(label)
local g = ImGui.GetCurrentContext()
local is_within_manual_tab_bar = g.CurrentTabBar and bit32.band(g.CurrentTabBar.Flags, 1048576 --[[ImGuiTabBarFlags.DockNode]]) == 0
if is_within_manual_tab_bar then
local tab_bar = g.CurrentTabBar
local tab_id = ImGui.TabBarCalcTabID(tab_bar, label, nil)
local tab = ImGui.TabBarFindTabByID(tab_bar, tab_id)
if tab then tab.WantClose = true end -- Will be processed by next call to TabBarLayout()
else
local window = ImGui.FindWindowByName(label)
if window and window.DockIsActive then
local node = window.DockNode
if node then
local tab_id = ImGui.TabBarCalcTabID(node.TabBar, label, window)
ImGui.TabBarRemoveTab(node.TabBar, tab_id)
window.DockTabWantClose = true
end
end
end
end
--- TabItemCalcSize(label, has_close_button_or_unsaved_marker) or TabItemCalcSize(window)
function ImGui.TabItemCalcSize(label, has_close_button_or_unsaved_marker)
if type(label) == "table" then
local window = label
return ImGui.TabItemCalcSize(window.Name, window.HasCloseButton or bit32.band(window.Flags, 262144 --[[ImGuiWindowFlags.UnsavedDocument]]) ~= 0)
end
local g = ImGui.GetCurrentContext()
local label_size = ImGui.CalcTextSize(label, nil, true)
local size = ImVec2(label_size.x + g.Style.FramePadding.x, label_size.y + g.Style.FramePadding.y * 2.0)
if has_close_button_or_unsaved_marker then
size.x = size.x + g.Style.FramePadding.x + (g.Style.ItemInnerSpacing.x + g.FontSize) -- We use Y intentionally to fit the close button circle.
else
size.x = size.x + g.Style.FramePadding.x + 1.0
end
return ImVec2(ImMin(size.x, ImGui.TabBarCalcMaxTabWidth()), size.y)
end
function ImGui.TabItemBackground(draw_list, bb, flags, col)
-- While rendering tabs, we trim 1 pixel off the top of our bounding box so they can fit within a regular frame height while looking "detached" from it.
local g = ImGui.GetCurrentContext()
local width = bb:GetWidth()
assert(width > 0.0)
local rounding = ImMax(0.0, ImMin(bit32.band(flags, 2097152 --[[ImGuiTabItemFlags.Button]]) ~= 0 and g.Style.FrameRounding or g.Style.TabRounding, width * 0.5 - 1.0))
local y1 = bb.Min.y + 1.0 -- Leave a bit of room in title bars.
local y2 = bb.Max.y - g.Style.TabBarBorderSize
draw_list:AddRectFilled(ImVec2(bb.Min.x, y1), ImVec2(bb.Max.x, y2), col, rounding, 48 --[[ImDrawFlags.RoundTop]])
if g.Style.TabBorderSize > 0.0 then
draw_list:PathLineTo(ImVec2(bb.Min.x, y2))
draw_list:PathArcToFast(ImVec2(bb.Min.x + rounding, y1 + rounding), rounding, 6, 9)
draw_list:PathArcToFast(ImVec2(bb.Max.x - rounding, y1 + rounding), rounding, 9, 12)
draw_list:PathLineTo(ImVec2(bb.Max.x, y2))
draw_list:PathStroke(ImGui.GetColorU32(5 --[[ImGuiCol.Border]]), g.Style.TabBorderSize, 393216 --[[ImDrawFlags.StrokeCenterBiased]]) -- Use _StrokeInside?
end
end
-- Render text label (with custom clipping) + Unsaved Document marker + Close Button logic
-- We tend to lock style.FramePadding for a given tab-bar, hence the 'frame_padding' parameter.
--- @return bool just_closed, bool text_clipped
function ImGui.TabItemLabelAndCloseButton(draw_list, bb, flags, frame_padding, label, tab_id, close_button_id, is_contents_visible)
local g = ImGui.GetCurrentContext()
local label_end = ImGui.FindRenderedTextEnd(label)
local label_size = ImGui.CalcTextSize(label, label_end, false)
if bb:GetWidth() <= 1.0 then return false, false end
-- Render text label (with clipping + alpha gradient) + unsaved marker
local text_ellipsis_clip_bb = ImRect(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y)
-- Return clipped state ignoring the close button
local text_clipped = (text_ellipsis_clip_bb.Min.x + label_size.x) > text_ellipsis_clip_bb.Max.x
-- draw_list:AddCircle(text_ellipsis_clip_bb.Min, 3.0, text_clipped and IM_COL32(255, 0, 0, 255) or IM_COL32(0, 255, 0, 255))
local button_sz = g.FontSize
local button_pos = ImVec2(ImMax(bb.Min.x, bb.Max.x - frame_padding.x - button_sz), bb.Min.y + frame_padding.y)
-- Close Button & Unsaved Marker
-- We are relying on a subtle and confusing distinction between 'hovered' and 'g.HoveredId' which happens because we are using ImGuiButtonFlags_AllowOverlapMode + SetItemAllowOverlap()
-- 'hovered' will be true when hovering the Tab but NOT when hovering the close button
-- 'g.HoveredId==id' will be true when hovering the Tab including when hovering the close button
-- 'g.ActiveId==close_button_id' will be true when we are holding on the close button, in which case both hovered booleans are false
local close_button_pressed = false
local close_button_visible = false
local is_hovered = g.HoveredId == tab_id or g.HoveredId == close_button_id or g.ActiveId == tab_id or g.ActiveId == close_button_id -- Any interaction account for this too.
if close_button_id ~= 0 then
if is_contents_visible then
close_button_visible = (g.Style.TabCloseButtonMinWidthSelected < 0.0) or (is_hovered and bb:GetWidth() >= ImMax(button_sz, g.Style.TabCloseButtonMinWidthSelected))
else
close_button_visible = (g.Style.TabCloseButtonMinWidthUnselected < 0.0) or (is_hovered and bb:GetWidth() >= ImMax(button_sz, g.Style.TabCloseButtonMinWidthUnselected))
end
end
-- When tabs/document is unsaved, the unsaved marker takes priority over the close button.
local unsaved_marker_visible = bit32.band(flags, 1 --[[ImGuiTabItemFlags.UnsavedDocument]]) ~= 0 and (button_pos.x + button_sz <= bb.Max.x) and (not close_button_visible or not is_hovered)
if unsaved_marker_visible then
local bullet_pos = button_pos + ImVec2(button_sz, button_sz) * 0.5
ImGui.RenderBullet(draw_list, bullet_pos, ImGui.GetColorU32(58 --[[ImGuiCol.UnsavedMarker]]))
elseif close_button_visible then
local last_item_backup = ImGuiLastItemData()
ImGuiLastItemData_Copy(last_item_backup, g.LastItemData)
if ImGui.CloseButton(close_button_id, button_pos) then close_button_pressed = true end
ImGuiLastItemData_Copy(g.LastItemData, last_item_backup)
-- Close with middle mouse button
if is_hovered and bit32.band(flags, 4 --[[ImGuiTabItemFlags.NoCloseWithMiddleMouseButton]]) == 0 and ImGui.IsMouseClicked(2) then
close_button_pressed = true
end
end
-- This is all rather complicated
-- (the main idea is that because the close button only appears on hover, we don't want it to alter the ellipsis position)
-- FIXME: if FramePadding is noticeably large, ellipsis_max_x will be wrong here (e.g. #3497), maybe for consistency that parameter of RenderTextEllipsis() shouldn't exist..
local ellipsis_max_x = text_ellipsis_clip_bb.Max.x
if close_button_visible or unsaved_marker_visible then
local visible_without_hover = unsaved_marker_visible or ((is_contents_visible and g.Style.TabCloseButtonMinWidthSelected or g.Style.TabCloseButtonMinWidthUnselected) < 0.0)
if visible_without_hover then
text_ellipsis_clip_bb.Max.x = text_ellipsis_clip_bb.Max.x - button_sz * 0.90
ellipsis_max_x = ellipsis_max_x - button_sz * 0.90
else
text_ellipsis_clip_bb.Max.x = text_ellipsis_clip_bb.Max.x - button_sz * 1.00
end
end
ImGui.LogSetNextTextDecoration("/", "\\")
ImGui.RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, ellipsis_max_x, label, label_end, label_size)
return close_button_pressed, text_clipped
end
end --[[ [SECTION] Widgets: BeginTabBar, EndTabBar, etc. (was imgui_tabs.lua) ]]
return true -- [Roblox] ModuleScripts must return exactly one value
end
__FILES['imstb_rectpack.lua'] = function()
--- Dear ImGui
-- This is a Lua port of original `imstb_rectpack.h`
-- ALL TABLES IN THIS FILE ARE 1-BASED!
-- Avoids table creation when possible
-- [DEAR IMGUI]
-- This is a slightly modified version of stb_rect_pack.h 1.01.
-- Grep for [DEAR IMGUI] to find the changes.
--
-- stb_rect_pack.h - v1.01 - public domain - rectangle packing
-- Sean Barrett 2014
--
-- Useful for e.g. packing rectangular textures into an atlas.
-- Does not do rotation.
--
-- Not necessarily the awesomest packing method, but better than
-- the totally naive one in stb_truetype (which is primarily what
-- this is meant to replace).
--
-- Has only had a few tests run, may have issues.
--
-- More docs to come.
--
-- Uses table.sort and assert (STBRP_SORT and STBRP_ASSERT below).
--
-- This library currently uses the Skyline Bottom-Left algorithm.
--
-- Please note: better rectangle packers are welcome! Please
-- implement them to the same API, but with a different init
-- function.
--
-- Credits
--
-- Library
-- Sean Barrett
-- Minor features
-- Martins Mozeiko
-- github:IntellectualKitty
--
-- Bugfixes / warning fixes
-- Jeremy Jaussaud
-- Fabian Giesen
--
-- Version history:
--
-- 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section
-- 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
-- 0.99 (2019-02-07) warning fixes
-- 0.11 (2017-03-03) return packing success/fail result
-- 0.10 (2016-10-25) remove cast-away-const to avoid warnings
-- 0.09 (2016-08-27) fix compiler warnings
-- 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
-- 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
-- 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
-- 0.05: added STBRP_ASSERT to allow replacing assert
-- 0.04: fixed minor bug in STBRP_LARGE_RECTS support
-- 0.01: initial release
--
-- LICENSE
--
-- See end of file for license information.
--- The `prev_link` can be stbrp_context or stbrp_node
--- @diagnostic disable
local bit32 = bit32
local STBRP_SORT = table.sort
local STBRP_ASSERT = assert
local STBRP__NOTUSED = function(_) end
-------------------------------------------------------------------------------
--
-- INCLUDE SECTION
--
local STBRP__MAXVAL = 0x7fffffff
-- Mostly for internal use, but this is the maximum supported coordinate value.
--- @enum STBRP_HEURISTIC_Skyline
local STBRP_HEURISTIC_Skyline = {
default = 0,
BL_sortHeight = 0,
BF_sortHeight = 1
}
local STBRP__INIT_skyline = 1
--- @enum STBRP__PREV_LINK_TYPE
local STBRP__PREV_LINK_TYPE = {
ACTIVE_HEAD = 0,
NEXT = 1
}
--- @alias stbrp_coord int
--- @class stbrp_rect
--- @field id int
--- @field w stbrp_coord
--- @field h stbrp_coord
--- @field x stbrp_coord
--- @field y stbrp_coord
--- @field was_packed int
--- @return stbrp_rect
--- @nodiscard
local function stbrp_rect()
return {
-- reserved for your use:
id = 0,
-- input:
w = 0,
h = 0,
-- output:
x = 0,
y = 0,
was_packed = 0 -- non-zero if valid packing
}
end
--- @class stbrp_node
--- @field x stbrp_coord
--- @field y stbrp_coord
--- @field next stbrp_node
--- @return stbrp_node
--- @nodiscard
local function stbrp_node()
return {
x = 0,
y = 0,
next = nil
}
end
--- @class stbrp_context
--- @field width int
--- @field height int
--- @field align int
--- @field init_mode int
--- @field heuristic int
--- @field num_nodes int
--- @field active_head stbrp_node
--- @field free_head stbrp_node
--- @field extra stbrp_node[]
--- @return stbrp_context
--- @nodiscard
local function stbrp_context()
return {
width = 0,
height = 0,
align = 0,
init_mode = 0,
heuristic = 0,
num_nodes = 0,
active_head = nil,
free_head = nil,
extra = {stbrp_node(), stbrp_node()} -- we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
}
end
--- @class stbrp__findresult
--- @field x int
--- @field y int
--- @field prev_link? table
--- @field prev_link_type? int
--- @return stbrp__findresult
local function stbrp__findresult()
return {
x = 0,
y = 0,
prev_link = nil,
prev_link_type = nil
}
end
-------------------------------------------------------------------------------
--
-- IMPLEMENTATION SECTION
--
-- Optionally select which packing heuristic the library should use. Different
-- heuristics will produce better/worse results for different data sets.
-- If you call init again, this will be reset to the default.
--- @param context stbrp_context
--- @param heuristic int
local function stbrp_setup_heuristic(context, heuristic)
if context.init_mode == STBRP__INIT_skyline then
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline.BL_sortHeight or heuristic == STBRP_HEURISTIC_Skyline.BF_sortHeight)
context.heuristic = heuristic
else
STBRP_ASSERT(false)
end
end
-- Optionally call this function after init but before doing any packing to
-- change the handling of the out-of-temp-memory scenario, described above.
-- If you call init again, this will be reset to the default (false).
--- @param context stbrp_context
--- @param allow_out_of_mem boolean
local function stbrp_setup_allow_out_of_mem(context, allow_out_of_mem)
if allow_out_of_mem then
-- if it's ok to run out of memory, then don't bother aligning them;
-- this gives better packing, but may fail due to OOM (even though
-- the rectangles easily fit). @TODO a smarter approach would be to only
-- quantize once we've hit OOM, then we could get rid of this parameter.
context.align = 1
else
-- if it's not ok to run out of memory, then quantize the widths
-- so that num_nodes is always enough nodes.
--
-- I.e. num_nodes * align >= width
-- align >= width / num_nodes
-- align = ceil(width/num_nodes)
context.align = (context.width + context.num_nodes - 1) / context.num_nodes
end
end
-- Initialize a rectangle packer to:
-- pack a rectangle that is 'width' by 'height' in dimensions
-- using temporary storage provided by the array 'nodes', which is 'num_nodes' long
--
-- You must call this function every time you start packing into a new target.
--
-- There is no "shutdown" function. The 'nodes' memory must stay valid for
-- the following stbrp_pack_rects() call (or calls), but can be freed after
-- the call (or calls) finish.
--
-- Note: to guarantee best results, either:
-- 1. make sure 'num_nodes' >= 'width'
-- or 2. call stbrp_setup_allow_out_of_mem() defined above with 'allow_out_of_mem = true'
--
-- If you don't do either of the above things, widths will be quantized to multiples
-- of small integers to guarantee the algorithm doesn't run out of temporary storage.
--
-- If you do #2, then the non-quantized algorithm will be used, but the algorithm
-- may run out of temporary storage and be unable to pack some rectangles.
--- @param context stbrp_context
--- @param width int
--- @param height int
--- @param nodes stbrp_node[]
--- @param num_nodes int
function stbrp_init_target(context, width, height, nodes, num_nodes)
for i = 1, num_nodes - 1 do
nodes[i].next = nodes[i + 1]
end
nodes[num_nodes].next = nil
context.init_mode = STBRP__INIT_skyline
context.heuristic = STBRP_HEURISTIC_Skyline.default
context.free_head = nodes[1]
context.active_head = context.extra[1]
context.width = width
context.height = height
context.num_nodes = num_nodes
stbrp_setup_allow_out_of_mem(context, false)
-- node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
context.extra[1].x = 0
context.extra[1].y = 0
context.extra[1].next = context.extra[2]
context.extra[2].x = width
context.extra[2].y = bit32.lshift(1, 30)
context.extra[2].next = nil
end
-- find minimum y position if it starts at x1
-- Returns (min_y, waste_area)
--- @param c stbrp_context
--- @param first stbrp_node
--- @param x0 int
--- @param width int
--- @return int, int
local function stbrp__skyline_find_min_y(c, first, x0, width)
local node = first
local x1 = x0 + width
STBRP__NOTUSED(c)
STBRP_ASSERT(first.x <= x0)
STBRP_ASSERT(node.next.x > x0) -- we ended up handling this in the caller for efficiency
STBRP_ASSERT(node.x <= x0)
local min_y = 0
local waste_area = 0
local visited_width = 0
while node.x < x1 do
if node.y > min_y then
-- raise min_y higher.
-- we've accounted for all waste up to min_y,
-- but we'll now add more waste for everything we've visited
waste_area = waste_area + visited_width * (node.y - min_y)
min_y = node.y
-- the first time through, visited_width might be reduced
if node.x < x0 then
visited_width = visited_width + node.next.x - x0
else
visited_width = visited_width + node.next.x - node.x
end
else
-- add waste area
local under_width = node.next.x - node.x
if under_width + visited_width > width then
under_width = width - visited_width
end
waste_area = waste_area + under_width * (min_y - node.y)
visited_width = visited_width + under_width
end
node = node.next
end
return min_y, waste_area
end
--- @param c stbrp_context
--- @param width int
--- @param height int
--- @return stbrp__findresult
local function stbrp__skyline_find_best_pos(c, width, height)
local best_waste = bit32.lshift(1, 30)
local best_x = 0
local best_y = bit32.lshift(1, 30)
local fr = stbrp__findresult()
local best_prev_link = nil
local best_prev_link_type = nil
-- align to multiple of c.align
width = width + c.align - 1
width = width - width % c.align
STBRP_ASSERT(width % c.align == 0)
-- if it can't possibly fit, bail immediately
if width > c.width or height > c.height then
fr.prev_link = nil
fr.prev_link_type = nil
fr.x = 0
fr.y = 0
return fr
end
local prev_link = c
local prev_link_type = STBRP__PREV_LINK_TYPE.ACTIVE_HEAD
local node = c.active_head
while (node.x + width <= c.width) do
local y, waste = stbrp__skyline_find_min_y(c, node, node.x, width)
if c.heuristic == STBRP_HEURISTIC_Skyline.BL_sortHeight then -- actually just want to test BL
-- bottom left
if y < best_y then
best_y = y
best_prev_link = prev_link
best_prev_link_type = prev_link_type
best_x = node.x
end
else
-- best-fit
if y + height <= c.height then
-- can only use it if it first vertically
if y < best_y or (y == best_y and waste < best_waste) then
best_y = y
best_waste = waste
best_prev_link = prev_link
best_prev_link_type = prev_link_type
best_x = node.x
end
end
end
prev_link = node
prev_link_type = STBRP__PREV_LINK_TYPE.NEXT
node = node.next
end
-- if doing best-fit (BF), we also have to try aligning right edge to each node position
--
-- e.g, if fitting
--
-- ____________________
-- |____________________|
--
-- into
--
-- | |
-- | ____________|
-- |____________|
--
-- then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
--
-- This makes BF take about 2x the time
if c.heuristic == STBRP_HEURISTIC_Skyline.BF_sortHeight then
local tail = c.active_head
local node_scan = c.active_head
local prev_link_scan = c
local prev_link_scan_type = STBRP__PREV_LINK_TYPE.ACTIVE_HEAD
-- find first node that's admissible
while tail and tail.x < width do
tail = tail.next
end
while tail do
local xpos = tail.x - width
STBRP_ASSERT(xpos >= 0)
-- find the left position that matches this
while node_scan.next and node_scan.next.x <= xpos do
prev_link_scan = node_scan
prev_link_scan_type = STBRP__PREV_LINK_TYPE.NEXT
node_scan = node_scan.next
end
if node_scan then
STBRP_ASSERT(node_scan.next.x > xpos and node_scan.x <= xpos)
local y, waste = stbrp__skyline_find_min_y(c, node_scan, xpos, width)
if y + height <= c.height then
if y <= best_y then
if y < best_y or waste < best_waste or (waste == best_waste and xpos < best_x) then
best_x = xpos
-- STBRP_ASSERT(y <= best_y); [DEAR IMGUI]
best_y = y
best_waste = waste
best_prev_link = prev_link_scan
best_prev_link_type = prev_link_scan_type
end
end
end
end
tail = tail.next
end
end
fr.prev_link = best_prev_link
fr.prev_link_type = best_prev_link_type
fr.x = best_x
fr.y = best_y
return fr
end
--- @param context stbrp_context
--- @param width int
--- @param height int
--- @return stbrp__findresult
local function stbrp__skyline_pack_rectangle(context, width, height)
-- find best position according to heuristic
local res = stbrp__skyline_find_best_pos(context, width, height)
-- bail if:
-- 1. it failed
-- 2. the best node doesn't fit (we don't always check this)
-- 3. we're out of memory
if res.prev_link == nil or res.y + height > context.height or context.free_head == nil then
res.prev_link = nil
res.prev_link_type = nil
return res
end
-- on success, create new node
local node = context.free_head
node.x = res.x
node.y = res.y + height
context.free_head = node.next
-- insert the new node into the right starting point, and
-- let 'cur' point to the remaining nodes needing to be
-- stitched back in
local cur
if res.prev_link_type == STBRP__PREV_LINK_TYPE.ACTIVE_HEAD then
cur = res.prev_link.active_head
else
cur = res.prev_link.next
end
if cur.x < res.x then
-- preserve the existing one, so start testing with the next one
local next = cur.next
cur.next = node
cur = next
else
if res.prev_link_type == STBRP__PREV_LINK_TYPE.ACTIVE_HEAD then
res.prev_link.active_head = node
else
res.prev_link.next = node
end
end
-- from here, traverse cur and free the nodes, until we get to one
-- that shouldn't be freed
while cur.next and cur.next.x <= res.x + width do
local next = cur.next
-- move the current node to the free list
cur.next = context.free_head
context.free_head = cur
cur = next
end
-- stitch the list back in
node.next = cur
if cur.x < res.x + width then
cur.x = res.x + width
end
return res
end
--- @param a stbrp_rect
--- @param b stbrp_rect
--- @return bool
--- @package
local function rect_height_compare(a, b)
if a.h > b.h then
return true
elseif a.h < b.h then
return false
else
return a.w > b.w
end
end
--- @param a stbrp_rect
--- @param b stbrp_rect
--- @return bool
--- @package
local function rect_original_order(a, b)
return a.was_packed < b.was_packed
end
-- Assign packed locations to rectangles. The rectangles are of type
-- 'stbrp_rect' defined above, stored in the array 'rects', and there
-- are 'num_rects' many of them.
--
-- Rectangles which are successfully packed have the 'was_packed' flag
-- set to a non-zero value and 'x' and 'y' store the minimum location
-- on each axis (i.e. bottom-left in cartesian coordinates, top-left
-- if you imagine y increasing downwards). Rectangles which do not fit
-- have the 'was_packed' flag set to 0.
--
-- You should not try to access the 'rects' array from another thread
-- while this function is running, as the function temporarily reorders
-- the array while it executes.
--
-- To pack into another rectangle, you need to call stbrp_init_target
-- again. To continue packing into the same rectangle, you can call
-- this function again. Calling this multiple times with multiple rect
-- arrays will probably produce worse packing results than calling it
-- a single time with the full rectangle array, but the option is
-- available.
--
-- The function returns 1 if all of the rectangles were successfully
-- packed and 0 otherwise.
--- @param context stbrp_context
--- @param rects stbrp_rect[]
--- @param num_rects int
--- @return int
local function stbrp_pack_rects(context, rects, num_rects)
local all_rects_packed = 1
-- we use the 'was_packed' field internally to allow sorting/unsorting
for i = 1, num_rects do
rects[i].was_packed = (i - 1)
end
-- sort according to heuristic
STBRP_SORT(rects, rect_height_compare)
for i = 1, num_rects do
if rects[i].w == 0 or rects[i].h == 0 then
rects[i].x = 0 -- empty rect needs no space
rects[i].y = 0
else
local fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h)
if fr.prev_link then
rects[i].x = fr.x
rects[i].y = fr.y
else
rects[i].x = STBRP__MAXVAL
rects[i].y = STBRP__MAXVAL
end
end
end
-- unsort
STBRP_SORT(rects, rect_original_order)
-- set was_packed flags and all_rects_packed status
for i = 1, num_rects do
rects[i].was_packed = (rects[i].x == STBRP__MAXVAL and rects[i].y == STBRP__MAXVAL) and 0 or 1
if rects[i].was_packed == 0 then
all_rects_packed = 0
end
end
-- return the all_rects_packed status
return all_rects_packed
end
return {
context = stbrp_context,
rect = stbrp_rect,
node = stbrp_node,
pack_rects = stbrp_pack_rects,
init_target = stbrp_init_target,
setup_allow_out_of_mem = stbrp_setup_allow_out_of_mem,
setup_heuristic = stbrp_setup_heuristic
}
-- ------------------------------------------------------------------------------
-- This software is available under 2 licenses -- choose whichever you prefer.
-- ------------------------------------------------------------------------------
-- ALTERNATIVE A - MIT License
-- Copyright (c) 2017 Sean Barrett
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
-- this software and associated documentation files (the "Software"), to deal in
-- the Software without restriction, including without limitation the rights to
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-- of the Software, and to permit persons to whom the Software is furnished to do
-- so, subject to the following conditions:
-- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software.
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
-- ------------------------------------------------------------------------------
-- ALTERNATIVE B - Public Domain (www.unlicense.org)
-- This is free and unencumbered software released into the public domain.
-- Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
-- software, either in source code form or as a compiled binary, for any purpose,
-- commercial or non-commercial, and by any means.
-- In jurisdictions that recognize copyright laws, the author or authors of this
-- software dedicate any and all copyright interest in the software to the public
-- domain. We make this dedication for the benefit of the public at large and to
-- the detriment of our heirs and successors. We intend this dedication to be an
-- overt act of relinquishment in perpetuity of all present and future rights to
-- this software under copyright law.
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-- ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-- ------------------------------------------------------------------------------
end
__FILES['imstb_textedit.lua'] = function()
--- Dear ImGui
-- This is a Lua port of original `imstb_textedit.h`
-- [DEAR IMGUI]
-- This is a slightly modified version of stb_textedit.h 1.14.
-- Those changes would need to be pushed into nothings/stb:
-- - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
-- - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
-- - Added name to struct or it may be forward declared in our code.
-- - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925)
-- - Changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion.
-- Grep for [DEAR IMGUI] to find some changes.
-- - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
-- stb_textedit.h - v1.14 - public domain - Sean Barrett
-- Development of this library was sponsored by RAD Game Tools
--
-- This C header file implements the guts of a multi-line text-editing
-- widget; you implement display, word-wrapping, and low-level string
-- insertion/deletion, and stb_textedit will map user inputs into
-- insertions & deletions, plus updates to the cursor position,
-- selection state, and undo state.
--
-- It is intended for use in games and other systems that need to build
-- their own custom widgets and which do not have heavy text-editing
-- requirements (this library is not recommended for use for editing large
-- texts, as its performance does not scale and it has limited undo).
--
-- Non-trivial behaviors are modelled after Windows text controls.
--
--
-- LICENSE
--
-- See end of file for license information.
--
--
-- DEPENDENCIES
--
-- Uses the C runtime function 'memmove', which you can override
-- by defining IMSTB_TEXTEDIT_memmove before the implementation.
-- (In this port: ImStb.TEXTEDIT_memmove.)
--
--
-- VERSION HISTORY
--
-- !!!! (2025-10-23) changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion.
-- 1.14 (2021-07-11) page up/down, various fixes
-- 1.13 (2019-02-07) fix bug in undo size management
-- 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash
-- 1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield
-- 1.10 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
-- 1.9 (2016-08-27) customizable move-by-word
-- 1.8 (2016-04-02) better keyboard handling when mouse button is down
-- 1.7 (2015-09-13) change y range handling in case baseline is non-0
-- 1.6 (2015-04-15) allow STB_TEXTEDIT_memmove
-- 1.5 (2014-09-10) add support for secondary keys for OS X
-- 1.4 (2014-08-17) fix signed/unsigned warnings
-- 1.3 (2014-06-19) fix mouse clicking to round to nearest char boundary
-- 1.2 (2014-05-27) fix some RAD types that had crept into the new code
-- 1.1 (2013-12-15) move-by-word (requires STB_TEXTEDIT_IS_SPACE )
-- 1.0 (2012-07-26) improve documentation, initial public release
-- 0.3 (2012-02-24) bugfixes, single-line mode; insert mode
-- 0.2 (2011-11-28) fixes to undo/redo
-- 0.1 (2010-07-08) initial version
--
-- ADDITIONAL CONTRIBUTORS
--
-- Ulf Winklemann: move-by-word in 1.1
-- Fabian Giesen: secondary key inputs in 1.5
-- Martins Mozeiko: STB_TEXTEDIT_memmove in 1.6
-- Louis Schnellbach: page up/down in 1.14
--
-- Bugfixes:
-- Scott Graham
-- Daniel Keller
-- Omar Cornut
-- Dan Thompson
--
-- USAGE
--
-- The basic concept is that you provide a "string" object which
-- behaves like an array of characters. stb_textedit uses indices to
-- refer to positions in the string, implicitly representing positions
-- in the displayed textedit. This is true for both plain text and
-- rich text; even with rich text stb_truetype interacts with your
-- code as if there was an array of all the displayed characters.
--
-- Symbols you must define for implementation mode (in this port: fields of ImStb, see below):
--
-- STB_TEXTEDIT_STRING the type of object representing a string being edited,
-- typically this is a wrapper object with other data you need
--
-- STB_TEXTEDIT_STRINGLEN(obj) the length of the string (ideally O(1))
-- STB_TEXTEDIT_LAYOUTROW(r,obj,n) returns the results of laying out a line of characters
-- starting from character #n (see discussion below)
-- STB_TEXTEDIT_GETWIDTH(obj,n,i) returns the pixel delta from the xpos of the i'th character
-- to the xpos of the i+1'th char for a line of characters
-- starting at character #n (i.e. accounts for kerning
-- with previous char)
-- STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 1-based in this port
-- STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize
-- as manually wordwrapping for end-of-line positioning
--
-- STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i
-- STB_TEXTEDIT_INSERTCHARS(obj,i,c,c_offset,n) try to insert n characters at i (taken from array c starting at c_offset)
-- returns number of characters actually inserted. [DEAR IMGUI]
--
-- STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key
--
-- STB_TEXTEDIT_K_LEFT keyboard input to move cursor left
-- STB_TEXTEDIT_K_RIGHT keyboard input to move cursor right
-- STB_TEXTEDIT_K_UP keyboard input to move cursor up
-- STB_TEXTEDIT_K_DOWN keyboard input to move cursor down
-- STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page
-- STB_TEXTEDIT_K_PGDOWN keyboard input to move cursor down a page
-- STB_TEXTEDIT_K_LINESTART keyboard input to move cursor to start of line -- e.g. HOME
-- STB_TEXTEDIT_K_LINEEND keyboard input to move cursor to end of line -- e.g. END
-- STB_TEXTEDIT_K_TEXTSTART keyboard input to move cursor to start of text -- e.g. ctrl-HOME
-- STB_TEXTEDIT_K_TEXTEND keyboard input to move cursor to end of text -- e.g. ctrl-END
-- STB_TEXTEDIT_K_DELETE keyboard input to delete selection or character under cursor
-- STB_TEXTEDIT_K_BACKSPACE keyboard input to delete selection or character left of cursor
-- STB_TEXTEDIT_K_UNDO keyboard input to perform undo
-- STB_TEXTEDIT_K_REDO keyboard input to perform redo
--
-- STB_TEXTEDIT_MOVEWORDLEFT(obj,i) custom handler for WORDLEFT, returns index to move cursor to
-- STB_TEXTEDIT_MOVEWORDRIGHT(obj,i) custom handler for WORDRIGHT, returns index to move cursor to
-- STB_TEXTEDIT_K_WORDLEFT keyboard input to move cursor left one word -- e.g. ctrl-LEFT
-- STB_TEXTEDIT_K_WORDRIGHT keyboard input to move cursor right one word -- e.g. ctrl-RIGHT
--
-- To support UTF-8:
--
-- STB_TEXTEDIT_GETPREVCHARINDEX returns index of previous character
-- STB_TEXTEDIT_GETNEXTCHARINDEX returns index of next character
-- Do NOT define STB_TEXTEDIT_KEYTOTEXT.
-- Instead, call stb_textedit_text() directly for text contents.
--
-- Keyboard input must be encoded as a single integer value; e.g. a character code
-- and some bitflags that represent shift states. to simplify the interface, SHIFT must
-- be a bitflag, so we can test the shifted state of cursor movements to allow selection,
-- i.e. (STB_TEXTEDIT_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow.
--
-- STB_TEXTEDIT_LAYOUTROW returns information about the shape of one displayed
-- row of characters assuming they start on the i'th character--the width and
-- the height and the number of characters consumed. This allows this library
-- to traverse the entire layout incrementally. You need to compute word-wrapping
-- here.
--
-- Each textfield keeps its own insert mode state, which is not how normal
-- applications work. To keep an app-wide insert mode, update/copy the
-- "insert_mode" field of STB_TexteditState before/after calling API functions.
--
-- API (in this port: fields of the table returned by this file)
--
-- initialize_state(state, is_single_line)
--
-- click(str, state, x, y)
-- drag(str, state, x, y)
-- cut(str, state) -> bool
-- paste(str, state, text, len) -> bool
-- key(str, state, key)
-- text(str, state, text, text_len)
--
-- Each of these functions potentially updates the string and updates the
-- state.
--
-- initialize_state:
-- set the textedit state to a known good default state when initially
-- constructing the textedit.
--
-- click:
-- call this with the mouse x,y on a mouse down; it will update the cursor
-- and reset the selection start/end to the cursor point. the x,y must
-- be relative to the text widget, with (0,0) being the top left.
--
-- drag:
-- call this with the mouse x,y on a mouse drag/up; it will update the
-- cursor and the selection end point
--
-- cut:
-- call this to delete the current selection; returns true if there was
-- one. you should FIRST copy the current selection to the system paste buffer.
-- (To copy, just copy the current selection out of the string yourself.)
--
-- paste:
-- call this to paste text at the current cursor point or over the current
-- selection if there is one.
--
-- key:
-- call this for keyboard inputs sent to the textfield. you can use it
-- for "key down" events or for "translated" key events.
--
-- text: (added 2025)
-- call this to directly send text input the textfield, which is required
-- for UTF-8 support, because stb_textedit_key() + STB_TEXTEDIT_KEYTOTEXT()
-- cannot infer text length.
--
--
-- When rendering, you can read the cursor position and selection state from
-- the STB_TexteditState.
--
--
-- Notes:
--
-- This is designed to be usable in IMGUI, so it allows for the possibility of
-- running in an IMGUI that has NOT cached the multi-line layout. For this
-- reason, it provides an interface that is compatible with computing the
-- layout incrementally--we try to make sure we make as few passes through
-- as possible. (For example, to locate the mouse pointer in the text, we
-- could define functions that return the X and Y positions of characters
-- and binary search Y and then X, but if we're doing dynamic layout this
-- will run the layout algorithm many times, so instead we manually search
-- forward in one pass. Similar logic applies to e.g. up-arrow and
-- down-arrow movement.)
--
-- If it's run in a widget that *has* cached the layout, then this is less
-- efficient, but it's not horrible on modern computers. But you wouldn't
-- want to edit million-line files with it.
-- ALL TABLES IN THIS FILE ARE 1-BASED!
-- Character Indexing:
-- idx=n, the n'th char (1-based)
-- Cursor Indexing:
-- idx=n, the position before the n'th char
-- idx=n+1, the position after the n'th char
-- Symbols that must be defined before using this:
local STB_TEXTEDIT_STRINGLEN = ImStb.TEXTEDIT_STRINGLEN
local STB_TEXTEDIT_GETCHAR = ImStb.TEXTEDIT_GETCHAR
local STB_TEXTEDIT_GETWIDTH = ImStb.TEXTEDIT_GETWIDTH
local STB_TEXTEDIT_LAYOUTROW = ImStb.TEXTEDIT_LAYOUTROW
-- [DEAR IMGUI]
-- Functions must be implemented for UTF8 support
-- Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
-- There is not necessarily a '[DEAR IMGUI]' at the usage sites.
local IMSTB_TEXTEDIT_GETNEXTCHARINDEX = ImStb.TEXTEDIT_GETNEXTCHARINDEX
local IMSTB_TEXTEDIT_GETPREVCHARINDEX = ImStb.TEXTEDIT_GETPREVCHARINDEX
local STB_TEXTEDIT_MOVEWORDLEFT = ImStb.TEXTEDIT_MOVEWORDLEFT
local STB_TEXTEDIT_MOVEWORDRIGHT = ImStb.TEXTEDIT_MOVEWORDRIGHT
local STB_TEXTEDIT_MOVELINESTART = ImStb.TEXTEDIT_MOVELINESTART
local STB_TEXTEDIT_MOVELINEEND = ImStb.TEXTEDIT_MOVELINEEND
local STB_TEXTEDIT_DELETECHARS = ImStb.TEXTEDIT_DELETECHARS
local STB_TEXTEDIT_INSERTCHARS = ImStb.TEXTEDIT_INSERTCHARS
STB_TEXTEDIT_NEWLINE = 10 -- the character returned by _GETCHAR() we recognize as manually wordwrapping for end-of-line positioning
STB_TEXTEDIT_K_LEFT = 0x200000 -- keyboard input to move cursor left
STB_TEXTEDIT_K_RIGHT = 0x200001 -- keyboard input to move cursor right
STB_TEXTEDIT_K_UP = 0x200002 -- keyboard input to move cursor up
STB_TEXTEDIT_K_DOWN = 0x200003 -- keyboard input to move cursor down
STB_TEXTEDIT_K_LINESTART = 0x200004 -- keyboard input to move cursor to start of line -- e.g. HOME
STB_TEXTEDIT_K_LINEEND = 0x200005 -- keyboard input to move cursor to end of line -- e.g. END
STB_TEXTEDIT_K_TEXTSTART = 0x200006 -- keyboard input to move cursor to start of text -- e.g. ctrl-HOME
STB_TEXTEDIT_K_TEXTEND = 0x200007 -- keyboard input to move cursor to end of text -- e.g. ctrl-END
STB_TEXTEDIT_K_DELETE = 0x200008 -- keyboard input to delete selection or character under cursor
STB_TEXTEDIT_K_BACKSPACE = 0x200009 -- keyboard input to delete selection or character left of cursor
STB_TEXTEDIT_K_UNDO = 0x20000A -- keyboard input to perform undo
STB_TEXTEDIT_K_REDO = 0x20000B -- keyboard input to perform redo
STB_TEXTEDIT_K_WORDLEFT = 0x20000C -- keyboard input to move cursor left one word -- e.g. ctrl-LEFT
STB_TEXTEDIT_K_WORDRIGHT = 0x20000D -- keyboard input to move cursor right one word -- e.g. ctrl-RIGHT
STB_TEXTEDIT_K_PGUP = 0x20000E -- keyboard input to move cursor up a page
STB_TEXTEDIT_K_PGDOWN = 0x20000F -- keyboard input to move cursor down a page
STB_TEXTEDIT_K_SHIFT = 0x400000 -- a power of two that is or'd in to a keyboard input to represent the shift key
IMSTB_TEXTEDIT_UNDOSTATECOUNT = 99 -- the number of undo states to allow
local IMSTB_TEXTEDIT_UNDOCHARCOUNT = 999 -- the number of characters to store in the undo buffer
local IMSTB_TEXTEDIT_memmove = ImStb.TEXTEDIT_memmove
-- forward declarations
local stb_text_undo
local stb_text_redo
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
---
--- STB_TexteditState
---
--- Definition of STB_TexteditState which you should store
--- per-textfield; it includes cursor position, selection state,
--- and undo state.
---
--- @alias IMSTB_TEXTEDIT_POSITIONTYPE int
--- @alias IMSTB_TEXTEDIT_CHARTYPE char
--- @alias STB_TEXTEDIT_KEYTYPE int
--- @class StbUndoRecord
--- @field where IMSTB_TEXTEDIT_POSITIONTYPE
--- @field insert_length IMSTB_TEXTEDIT_POSITIONTYPE
--- @field delete_length IMSTB_TEXTEDIT_POSITIONTYPE
--- @field char_storage int
--- @return StbUndoRecord
--- @nodiscard
local function StbUndoRecord()
return {
-- private data
where = 1,
insert_length = 0,
delete_length = 0,
char_storage = 1
}
end
--- @class StbUndoState
--- @field undo_rec StbUndoRecord[] # size = IMSTB_TEXTEDIT_UNDOSTATECOUNT
--- @field undo_char IMSTB_TEXTEDIT_CHARTYPE[] # size = IMSTB_TEXTEDIT_UNDOCHARCOUNT
--- @field undo_point short # next available slot
--- @field redo_point short # next available slot
--- @field undo_char_point int # next available slot
--- @field redo_char_point int # next available slot
--- @return StbUndoState
--- @nodiscard
local function StbUndoState()
local undo_rec = {}
for i = 1, 99 --[[IMSTB_TEXTEDIT_UNDOSTATECOUNT]] do
undo_rec[i] = StbUndoRecord()
end
local undo_char = {}
for i = 1, IMSTB_TEXTEDIT_UNDOCHARCOUNT do
undo_char[i] = 0
end
return {
-- private data
undo_rec = undo_rec,
undo_char = undo_char,
undo_point = 1,
redo_point = 1,
undo_char_point = 1,
redo_char_point = 1
}
end
--- @class STB_TexteditState
--- @field cursor int # position of the text cursor within the string
--- @field select_start int # selection start point
--- @field select_end int
--- @field insert_mode bool
--- @field row_count_per_page int
--- @field cursor_at_end_of_line bool # not implemented yet
--- @field initialized bool
--- @field has_preferred_x bool
--- @field single_line bool
--- @field padding1 unsigned_char
--- @field padding2 unsigned_char
--- @field padding3 unsigned_char
--- @field preferred_x float
--- @field undostate StbUndoState
--- @return STB_TexteditState
--- @nodiscard
function STB_TexteditState()
return {
---------------------
--
-- public data
--
cursor = 0,
-- position of the text cursor within the string
select_start = 0, -- selection start point
select_end = 0,
-- selection start and end point in characters; if equal, no selection.
-- note that start may be less than or greater than end (e.g. when
-- dragging the mouse, start is where the initial click was, and you
-- can drag in either direction)
insert_mode = false,
-- each textfield keeps its own insert mode state. to keep an app-wide
-- insert mode, copy this value in/out of the app state
row_count_per_page = 0,
-- page size in number of row.
-- this value MUST be set to >0 for pageup or pagedown in multilines documents.
---------------------
--
-- private data
--
cursor_at_end_of_line = false, -- not implemented yet
initialized = false,
has_preferred_x = false,
single_line = false,
padding1 = 0,
padding2 = 0,
padding3 = 0,
preferred_x = 0.0, -- this determines where the cursor up/down tries to seek to along x
undostate = StbUndoState()
}
end
--- @param s STB_TexteditState
local function STB_TEXT_HAS_SELECTION(s)
return s.select_start ~= s.select_end
end
-------------------------------------------------------------------------------
---
--- StbTexteditRow
---
--- Result of layout query, used by stb_textedit to determine where
--- the text in each row is.
-- result of layout query
--- @class StbTexteditRow
--- @field x0 float # starting x location, end x location (allows for align=right, etc)
--- @field x1 float # end x location
--- @field baseline_y_delta float # position of baseline relative to previous row's baseline
--- @field ymin float # height of row above and below baseline
--- @field ymax float # height of row above and below baseline
--- @field num_chars int
--- @return StbTexteditRow
--- @nodiscard
local function StbTexteditRow()
return {
x0 = 0.0,
x1 = 0.0,
baseline_y_delta = 0.0,
ymin = 0.0,
ymax = 0.0,
num_chars = 0
}
end
--- @param r StbTexteditRow
local function StbTexteditRow_Reset(r)
r.x0 = 0.0
r.x1 = 0.0
r.baseline_y_delta = 0.0
r.ymin = 0.0
r.ymax = 0.0
r.num_chars = 0
end
----------------------------------------
----------------------------------------
---
--- Implementation mode
---
---
-------------------------------------------------------------------------------
---
--- Mouse input handling
---
local stb_text_locate_coord do
-- only create once, reuse later
local r = StbTexteditRow()
--- traverse the layout to locate the nearest character to a display position
--- @param str IMSTB_TEXTEDIT_STRING
--- @param x float
--- @param y float
--- @return int idx
--- @return bool side_on_line
function stb_text_locate_coord(str, x, y)
local n = STB_TEXTEDIT_STRINGLEN(str)
local base_y = 0
local prev_x
StbTexteditRow_Reset(r)
local out_side_on_line = false
-- search rows to find one that straddles 'y'
local i = 1
while i <= n do
STB_TEXTEDIT_LAYOUTROW(r, str, i)
if r.num_chars <= 0 then
return n + 1, out_side_on_line
end
if i == 1 and y < base_y + r.ymin then
return 1, out_side_on_line
end
if y < base_y + r.ymax then
break
end
i = i + r.num_chars
base_y = base_y + r.baseline_y_delta
end
-- below all text, return 'after' last character
if i > n then
out_side_on_line = true
return n + 1, out_side_on_line
end
-- check if it's before the beginning of the line
if x < r.x0 then
return i, out_side_on_line
end
-- check if it's before the end of the line
if x < r.x1 then
-- search characters in row for one that straddles 'x'
prev_x = r.x0
local k = 1
while k <= r.num_chars do
local w = STB_TEXTEDIT_GETWIDTH(str, i, k)
if x < prev_x + w then
out_side_on_line = (k == 1) and false or true
if x < prev_x + w / 2 then
return k + i - 1, out_side_on_line
else
return IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k - 1), out_side_on_line
end
end
prev_x = prev_x + w
k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k - 1) - i + 1
end
-- shouldn't happen, but if it does, fall through to end-of-line case
end
-- if the last character is a newline, return that. otherwise return 'after' the last character
out_side_on_line = true
if STB_TEXTEDIT_GETCHAR(str, i + r.num_chars - 1) == 10 --[[STB_TEXTEDIT_NEWLINE]] then
return i + r.num_chars - 1, out_side_on_line
else
return i + r.num_chars, out_side_on_line
end
end
end
local stb_textedit_click do
local r = StbTexteditRow()
-- API click: on mouse down, move the cursor to the clicked location, and reset the selection
--- @param str IMSTB_TEXTEDIT_STRING
--- @param state STB_TexteditState
--- @param x float
--- @param y float
function stb_textedit_click(str, state, x, y)
-- In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
-- goes off the top or bottom of the text
local side_on_line
if state.single_line then
StbTexteditRow_Reset(r)
STB_TEXTEDIT_LAYOUTROW(r, str, 1)
y = r.ymin
end
state.cursor, side_on_line = stb_text_locate_coord(str, x, y)
state.select_start = state.cursor
state.select_end = state.cursor
state.has_preferred_x = false
str.LastMoveDirectionLR = side_on_line and 1 --[[ImGuiDir.Right]] or 0 --[[ImGuiDir.Left]]
end
end
local stb_textedit_drag do
local r = StbTexteditRow()
-- API drag: on mouse drag, move the cursor and selection endpoint to the clicked location
--- @param str IMSTB_TEXTEDIT_STRING
--- @param state STB_TexteditState
--- @param x float
--- @param y float
function stb_textedit_drag(str, state, x, y)
local p = 1
local side_on_line
-- In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
-- goes off the top or bottom of the text
if state.single_line then
StbTexteditRow_Reset(r)
STB_TEXTEDIT_LAYOUTROW(r, str, 1)
y = r.ymin
end
if state.select_start == state.select_end then
state.select_start = state.cursor
end
p, side_on_line = stb_text_locate_coord(str, x, y)
state.cursor = p
state.select_end = p
str.LastMoveDirectionLR = (side_on_line ~= false) and 1 --[[ImGuiDir.Right]] or 0 --[[ImGuiDir.Left]]
end
end
---------------------------
---------------------------
---
--- Keyboard input handling
---
---
-- forward declarations
local stb_text_makeundo_insert
local stb_text_makeundo_delete
local stb_text_makeundo_replace
--- @class StbFindState
--- @field x float # position of n'th character
--- @field y float
--- @field height float # height of line
--- @field first_char int # first char of row, and length
--- @field length int
--- @field prev_first int # first char of previous row
--- @return StbFindState
local function StbFindState()
return {
x = 0.0, y = 0.0,
height = 0.0,
first_char = 0, length = 0,
prev_first = 0
}
end
--- @param f StbFindState
local function StbFindState_Reset(f)
f.x = 0.0
f.y = 0.0
f.height = 0.0
f.first_char = 0
f.length = 0
f.prev_first = 0
end
local stb_textedit_find_charpos do
local r = StbTexteditRow()
-- find the x/y location of a character, and remember info about the previous row in
-- case we get a move-up event (for page up, we'll have to rescan)
--- @param find StbFindState
--- @param str IMSTB_TEXTEDIT_STRING
--- @param n int
--- @param single_line bool
function stb_textedit_find_charpos(find, str, n, single_line)
StbTexteditRow_Reset(r)
local prev_start = 1
local z = STB_TEXTEDIT_STRINGLEN(str)
local first
if n == z + 1 and single_line then
-- special case if it's at the end (may not be needed?)
STB_TEXTEDIT_LAYOUTROW(r, str, 1)
find.y = 0
find.first_char = 1
find.length = z
find.height = r.ymax - r.ymin
find.x = r.x1
return
end
-- search rows to find the one that straddles character n
find.y = 0
local i = 1
while true do
STB_TEXTEDIT_LAYOUTROW(r, str, i)
if n < i + r.num_chars - 1 then
break
end
if str.LastMoveDirectionLR == 1 --[[ImGuiDir.Right]] and str.Stb.cursor > 1 and str.Stb.cursor == i + r.num_chars and STB_TEXTEDIT_GETCHAR(str, i + r.num_chars - 1) ~= 10 --[[STB_TEXTEDIT_NEWLINE]] then -- [DEAR IMGUI] Wrapping point handling
break
end
if (i - 1) + r.num_chars == z and z > 0 and STB_TEXTEDIT_GETCHAR(str, z) ~= 10 --[[STB_TEXTEDIT_NEWLINE]] then -- [DEAR IMGUI] special handling for last line
break -- [DEAR IMGUI]
end
prev_start = i
i = i + r.num_chars
find.y = find.y + r.baseline_y_delta
if i == z + 1 then -- [DEAR IMGUI]
r.num_chars = 0 -- [DEAR IMGUI]
break -- [DEAR IMGUI]
end
end
find.first_char = i
first = i
find.length = r.num_chars
find.height = r.ymax - r.ymin
find.prev_first = prev_start
-- now scan to find xpos
find.x = r.x0
i = 1
while first + i <= n + 1 do
find.x = find.x + STB_TEXTEDIT_GETWIDTH(str, first, i)
i = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, first + i - 1) - first + 1
end
end
end
-- make the selection/cursor state valid if client altered the string
--- @param str IMSTB_TEXTEDIT_STRING
--- @param state STB_TexteditState
local function stb_textedit_clamp(str, state)
local n = STB_TEXTEDIT_STRINGLEN(str)
if STB_TEXT_HAS_SELECTION(state) then
if state.select_start > n + 1 then state.select_start = n + 1 end
if state.select_end > n + 1 then state.select_end = n + 1 end
-- if clamping forced them to be equal, move the cursor to match
if state.select_start == state.select_end then
state.cursor = state.select_start
end
end
if state.cursor > n + 1 then state.cursor = n + 1 end
end
-- delete characters while updating undo
--- @param str IMSTB_TEXTEDIT_STRING
--- @param state STB_TexteditState
--- @param where int
--- @param len int
local function stb_textedit_delete(str, state, where, len)
stb_text_makeundo_delete(str, state, where, len)
STB_TEXTEDIT_DELETECHARS(str, where, len)
state.has_preferred_x = false
end
-- delete the section
--- @param str IMSTB_TEXTEDIT_STRING
--- @param state STB_TexteditState
local function stb_textedit_delete_selection(str, state)
stb_textedit_clamp(str, state)
if STB_TEXT_HAS_SELECTION(state) then
if state.select_start < state.select_end then
stb_textedit_delete(str, state, state.select_start, state.select_end - state.select_start)
state.cursor = state.select_start
state.select_end = state.cursor
else
stb_textedit_delete(str, state, state.select_end, state.select_start - state.select_end)
state.cursor = state.select_end
state.select_start = state.cursor
end
state.has_preferred_x = false
end
end
-- canoncialize the selection so start <= end
--- @param state STB_TexteditState
local function stb_textedit_sortselection(state)
if state.select_end < state.select_start then
local temp = state.select_end
state.select_end = state.select_start
state.select_start = temp
end
end
-- move cursor to first character of selection
--- @param state STB_TexteditState
local function stb_textedit_move_to_first(state)
if STB_TEXT_HAS_SELECTION(state) then
stb_textedit_sortselection(state)
state.cursor = state.select_start
state.select_end = state.select_start
state.has_preferred_x = false
end
end
-- move cursor to last character of selection
--- @param str IMSTB_TEXTEDIT_STRING
--- @param state STB_TexteditState
local function stb_textedit_move_to_last(str, state)
if STB_TEXT_HAS_SELECTION(state) then
stb_textedit_sortselection(state)
stb_textedit_clamp(str, state)
state.cursor = state.select_end
state.select_start = state.select_end
state.has_preferred_x = false
end
end
-- update selection and cursor to match each other
--- @param state STB_TexteditState
local function stb_textedit_prep_selection_at_cursor(state)
if not STB_TEXT_HAS_SELECTION(state) then
state.select_start = state.cursor
state.select_end = state.cursor
else
state.cursor = state.select_end
end
end
-- API cut: delete selection
--- @param str IMSTB_TEXTEDIT_STRING
--- @param state STB_TexteditState
--- @return bool
local function stb_textedit_cut(str, state)
if STB_TEXT_HAS_SELECTION(state) then
stb_textedit_delete_selection(str, state) -- implicitly clamps
state.has_preferred_x = false
return true
end
return false
end
-- API paste: replace existing selection with passed-in text
--- @param str IMSTB_TEXTEDIT_STRING
--- @param state STB_TexteditState
--- @param text IMSTB_TEXTEDIT_CHARTYPE[]
--- @param len int
--- @return bool
local function stb_textedit_paste_internal(str, state, text, len)
-- if there's a selection, the paste should delete it
stb_textedit_clamp(str, state)
stb_textedit_delete_selection(str, state)
-- try to insert the characters
len = STB_TEXTEDIT_INSERTCHARS(str, state.cursor, text, 1, len)
if len then
stb_text_makeundo_insert(state, state.cursor, len)
state.cursor = state.cursor + len
state.has_preferred_x = false
return true
end
-- note: paste failure will leave deleted selection, may be restored with an undo (see https://github.com/nothings/stb/issues/734 for details)
return false
end
-- API key: process text input
-- [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
--- @param str IMSTB_TEXTEDIT_STRING
--- @param state STB_TexteditState
--- @param text IMSTB_TEXTEDIT_CHARTYPE[]
--- @param text_len int
local function stb_textedit_text(str, state, text, text_len)
-- can't add newline in single-line mode
if text[1] == 10 --[[STB_TEXTEDIT_NEWLINE]] and state.single_line then
return
end
if state.insert_mode and not STB_TEXT_HAS_SELECTION(state) and state.cursor < STB_TEXTEDIT_STRINGLEN(str) then
stb_text_makeundo_replace(str, state, state.cursor, 1, 1)
STB_TEXTEDIT_DELETECHARS(str, state.cursor, 1)
text_len = STB_TEXTEDIT_INSERTCHARS(str, state.cursor, text, 1, text_len)
if text_len then
state.cursor = state.cursor + text_len
state.has_preferred_x = false
end
else
stb_textedit_delete_selection(str, state) -- implicitly clamps
text_len = STB_TEXTEDIT_INSERTCHARS(str, state.cursor, text, 1, text_len)
if text_len then
stb_text_makeundo_insert(state, state.cursor, text_len)
state.cursor = state.cursor + text_len
state.has_preferred_x = false
end
end
end
local stb_textedit_key do
local find = StbFindState()
local row = StbTexteditRow()
-- API key: process a keyboard input
--- @param str IMSTB_TEXTEDIT_STRING
--- @param state STB_TexteditState
--- @param key STB_TEXTEDIT_KEYTYPE
function stb_textedit_key(str, state, key)
if key == 2097162 --[[STB_TEXTEDIT_K_UNDO]] then
stb_text_undo(str, state)
state.has_preferred_x = false
elseif key == 2097163 --[[STB_TEXTEDIT_K_REDO]] then
stb_text_redo(str, state)
state.has_preferred_x = false
elseif key == 2097152 --[[STB_TEXTEDIT_K_LEFT]] then
-- if currently there's a selection, move cursor to start of selection
if STB_TEXT_HAS_SELECTION(state) then
stb_textedit_move_to_first(state)
else
if state.cursor > 1 then
state.cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state.cursor)
end
end
state.has_preferred_x = false
elseif key == 2097153 --[[STB_TEXTEDIT_K_RIGHT]] then
-- if currently there's a selection, move cursor to end of selection
if STB_TEXT_HAS_SELECTION(state) then
stb_textedit_move_to_last(str, state)
else
state.cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state.cursor)
end
stb_textedit_clamp(str, state)
state.has_preferred_x = false
elseif key == bit32.bor(2097152 --[[STB_TEXTEDIT_K_LEFT]], 4194304 --[[STB_TEXTEDIT_K_SHIFT]]) then
stb_textedit_clamp(str, state)
stb_textedit_prep_selection_at_cursor(state)
-- move selection left
if state.select_end > 1 then
state.select_end = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state.select_end)
end
state.cursor = state.select_end
state.has_preferred_x = false
elseif key == 2097164 --[[STB_TEXTEDIT_K_WORDLEFT]] then
-- if currently there's a selection, move cursor to start of selection
if STB_TEXT_HAS_SELECTION(state) then
stb_textedit_move_to_first(state)
else
state.cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state.cursor)
stb_textedit_clamp(str, state)
end
elseif key == bit32.bor(2097164 --[[STB_TEXTEDIT_K_WORDLEFT]], 4194304 --[[STB_TEXTEDIT_K_SHIFT]]) then
if not STB_TEXT_HAS_SELECTION(state) then
stb_textedit_prep_selection_at_cursor(state)
end
state.cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state.cursor)
state.select_end = state.cursor
stb_textedit_clamp(str, state)
elseif key == 2097165 --[[STB_TEXTEDIT_K_WORDRIGHT]] then
-- if currently there's a selection, move cursor to end of selection
if STB_TEXT_HAS_SELECTION(state) then
stb_textedit_move_to_last(str, state)
else
state.cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state.cursor)
stb_textedit_clamp(str, state)
end
elseif key == bit32.bor(2097165 --[[STB_TEXTEDIT_K_WORDRIGHT]], 4194304 --[[STB_TEXTEDIT_K_SHIFT]]) then
if not STB_TEXT_HAS_SELECTION(state) then
stb_textedit_prep_selection_at_cursor(state)
end
state.cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state.cursor)
state.select_end = state.cursor
stb_textedit_clamp(str, state)
elseif key == bit32.bor(2097153 --[[STB_TEXTEDIT_K_RIGHT]], 4194304 --[[STB_TEXTEDIT_K_SHIFT]]) then
stb_textedit_prep_selection_at_cursor(state)
-- move selection right
state.select_end = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state.select_end)
stb_textedit_clamp(str, state)
state.cursor = state.select_end
state.has_preferred_x = false
elseif key == 2097155 --[[STB_TEXTEDIT_K_DOWN]]
or key == bit32.bor(2097155 --[[STB_TEXTEDIT_K_DOWN]], 4194304 --[[STB_TEXTEDIT_K_SHIFT]])
or key == 2097167 --[[STB_TEXTEDIT_K_PGDOWN]]
or key == bit32.bor(2097167 --[[STB_TEXTEDIT_K_PGDOWN]], 4194304 --[[STB_TEXTEDIT_K_SHIFT]]) then
StbFindState_Reset(find)
StbTexteditRow_Reset(row)
local sel = (bit32.band(key, 4194304 --[[STB_TEXTEDIT_K_SHIFT]]) ~= 0)
local is_page = (bit32.band(key, bit32.bnot(4194304 --[[STB_TEXTEDIT_K_SHIFT]])) == 2097167 --[[STB_TEXTEDIT_K_PGDOWN]])
local row_count = is_page and state.row_count_per_page or 1
if not is_page and state.single_line then
-- on windows, up&down in single-line behave like left&right
key = bit32.bor(2097153 --[[STB_TEXTEDIT_K_RIGHT]], bit32.band(key, 4194304 --[[STB_TEXTEDIT_K_SHIFT]]))
return stb_textedit_key(str, state, key) -- was `goto retry`
end
if sel then
stb_textedit_prep_selection_at_cursor(state)
elseif STB_TEXT_HAS_SELECTION(state) then
stb_textedit_move_to_last(str, state)
end
-- compute current position of cursor point
stb_textedit_clamp(str, state)
stb_textedit_find_charpos(find, str, state.cursor, state.single_line)
for j = 1, row_count do
local goal_x = state.has_preferred_x and state.preferred_x or find.x
local x
local start = find.first_char + find.length
if find.length == 0 then
break
end
-- [DEAR IMGUI]
-- going down while being on the last line shouldn't bring us to that line end
--if STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) ~= STB_TEXTEDIT_NEWLINE then
-- break
--end
-- now find character position down a row
state.cursor = start
STB_TEXTEDIT_LAYOUTROW(row, str, state.cursor)
x = row.x0
local i = 1
while i <= row.num_chars do
local dx = STB_TEXTEDIT_GETWIDTH(str, start, i)
local next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state.cursor)
x = x + dx
if x > goal_x then
break
end
i = i + (next - state.cursor)
state.cursor = next
end
stb_textedit_clamp(str, state)
if state.cursor == find.first_char + find.length then
str.LastMoveDirectionLR = 0 --[[ImGuiDir.Left]]
end
state.has_preferred_x = true
state.preferred_x = goal_x
if sel then
state.select_end = state.cursor
end
-- go to next line
find.first_char = find.first_char + find.length
find.length = row.num_chars
end
elseif key == 2097154 --[[STB_TEXTEDIT_K_UP]]
or key == bit32.bor(2097154 --[[STB_TEXTEDIT_K_UP]], 4194304 --[[STB_TEXTEDIT_K_SHIFT]])
or key == 2097166 --[[STB_TEXTEDIT_K_PGUP]]
or key == bit32.bor(2097166 --[[STB_TEXTEDIT_K_PGUP]], 4194304 --[[STB_TEXTEDIT_K_SHIFT]]) then
StbFindState_Reset(find)
StbTexteditRow_Reset(row)
local sel = (bit32.band(key, 4194304 --[[STB_TEXTEDIT_K_SHIFT]]) ~= 0)
local is_page = (bit32.band(key, bit32.bnot(4194304 --[[STB_TEXTEDIT_K_SHIFT]])) == 2097166 --[[STB_TEXTEDIT_K_PGUP]])
local row_count = is_page and state.row_count_per_page or 1
if not is_page and state.single_line then
-- on windows, up&down become left&right
key = bit32.bor(2097152 --[[STB_TEXTEDIT_K_LEFT]], bit32.band(key, 4194304 --[[STB_TEXTEDIT_K_SHIFT]]))
return stb_textedit_key(str, state, key) -- was `goto retry`
end
if sel then
stb_textedit_prep_selection_at_cursor(state)
elseif STB_TEXT_HAS_SELECTION(state) then
stb_textedit_move_to_first(state)
end
-- compute current position of cursor point
stb_textedit_clamp(str, state)
stb_textedit_find_charpos(find, str, state.cursor, state.single_line)
for j = 1, row_count do
local goal_x
if state.has_preferred_x then
goal_x = state.preferred_x
else
goal_x = find.x
end
local x
-- can only go up if there's a previous row
if find.prev_first == find.first_char then
break
end
-- now find character position up a row
state.cursor = find.prev_first
STB_TEXTEDIT_LAYOUTROW(row, str, state.cursor)
x = row.x0
local i = 1
while i <= row.num_chars do
local dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i)
local next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state.cursor)
x = x + dx
if x > goal_x then
break
end
i = i + (next - state.cursor)
state.cursor = next
end
stb_textedit_clamp(str, state)
if state.cursor == find.first_char then
str.LastMoveDirectionLR = 1 --[[ImGuiDir.Right]]
elseif state.cursor == find.prev_first then
str.LastMoveDirectionLR = 0 --[[ImGuiDir.Left]]
end
state.has_preferred_x = true
state.preferred_x = goal_x
if sel then
state.select_end = state.cursor
end
-- go to previous line
-- (we need to scan previous line the hard way. maybe we could expose this as a new API function?)
local prev_scan = find.prev_first > 1 and find.prev_first - 1 or 1
while prev_scan > 1 do
local prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, prev_scan)
if STB_TEXTEDIT_GETCHAR(str, prev) == 10 --[[STB_TEXTEDIT_NEWLINE]] then
break
end
prev_scan = prev
end
find.first_char = find.prev_first
find.prev_first = STB_TEXTEDIT_MOVELINESTART(str, state, prev_scan)
end
elseif key == 2097160 --[[STB_TEXTEDIT_K_DELETE]]
or key == bit32.bor(2097160 --[[STB_TEXTEDIT_K_DELETE]], 4194304 --[[STB_TEXTEDIT_K_SHIFT]]) then
if STB_TEXT_HAS_SELECTION(state) then
stb_textedit_delete_selection(str, state)
else
local n = STB_TEXTEDIT_STRINGLEN(str)
if state.cursor <= n then
stb_textedit_delete(str, state, state.cursor, IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state.cursor) - state.cursor)
end
end
state.has_preferred_x = false
elseif key == 2097161 --[[STB_TEXTEDIT_K_BACKSPACE]]
or key == bit32.bor(2097161 --[[STB_TEXTEDIT_K_BACKSPACE]], 4194304 --[[STB_TEXTEDIT_K_SHIFT]]) then
if STB_TEXT_HAS_SELECTION(state) then
stb_textedit_delete_selection(str, state)
else
stb_textedit_clamp(str, state)
if state.cursor > 1 then
local prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state.cursor)
stb_textedit_delete(str, state, prev, state.cursor - prev)
state.cursor = prev
end
end
state.has_preferred_x = false
elseif key == 2097158 --[[STB_TEXTEDIT_K_TEXTSTART]] then
state.cursor = 1
state.select_start = 1
state.select_end = 1
state.has_preferred_x = false
elseif key == 2097159 --[[STB_TEXTEDIT_K_TEXTEND]] then
state.cursor = STB_TEXTEDIT_STRINGLEN(str) + 1
state.select_start = 1
state.select_end = 1
state.has_preferred_x = false
elseif key == bit32.bor(2097158 --[[STB_TEXTEDIT_K_TEXTSTART]], 4194304 --[[STB_TEXTEDIT_K_SHIFT]]) then
stb_textedit_prep_selection_at_cursor(state)
state.cursor = 1
state.select_end = 1
state.has_preferred_x = false
elseif key == bit32.bor(2097159 --[[STB_TEXTEDIT_K_TEXTEND]], 4194304 --[[STB_TEXTEDIT_K_SHIFT]]) then
stb_textedit_prep_selection_at_cursor(state)
state.cursor = STB_TEXTEDIT_STRINGLEN(str) + 1
state.select_end = state.cursor
state.has_preferred_x = false
elseif key == 2097156 --[[STB_TEXTEDIT_K_LINESTART]] then
stb_textedit_clamp(str, state)
stb_textedit_move_to_first(state)
state.cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state.cursor)
state.has_preferred_x = false
elseif key == 2097157 --[[STB_TEXTEDIT_K_LINEEND]] then
stb_textedit_clamp(str, state)
stb_textedit_move_to_last(str, state)
state.cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state.cursor)
state.has_preferred_x = false
elseif key == bit32.bor(2097156 --[[STB_TEXTEDIT_K_LINESTART]], 4194304 --[[STB_TEXTEDIT_K_SHIFT]]) then
stb_textedit_clamp(str, state)
stb_textedit_prep_selection_at_cursor(state)
state.cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state.cursor)
state.select_end = state.cursor
state.has_preferred_x = false
elseif key == bit32.bor(2097157 --[[STB_TEXTEDIT_K_LINEEND]], 4194304 --[[STB_TEXTEDIT_K_SHIFT]]) then
stb_textedit_clamp(str, state)
stb_textedit_prep_selection_at_cursor(state)
state.cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state.cursor)
state.select_end = state.cursor
state.has_preferred_x = false
end
end
end
-----------------------------------------------------
-----------------------------------------------------
---
--- Undo processing
---
--- @OPTIMIZE: the undo/redo buffer should be circular
--- @param state StbUndoState
local function stb_textedit_flush_redo(state)
state.redo_point = 99 --[[IMSTB_TEXTEDIT_UNDOSTATECOUNT]] + 1
state.redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT + 1
end
-- discard the oldest entry in the undo list
--- @param state StbUndoState
local function stb_textedit_discard_undo(state)
if state.undo_point > 0 then
-- if the 1th undo state has characters, clean those up
if state.undo_rec[1].char_storage >= 0 then
local n = state.undo_rec[1].insert_length
-- delete n characters from all other records
state.undo_char_point = state.undo_char_point - n
IMSTB_TEXTEDIT_memmove(state.undo_char, 1, state.undo_char, n + 1, state.undo_char_point - 1)
for i = 1, state.undo_point do
if state.undo_rec[i].char_storage >= 0 then
state.undo_rec[i].char_storage = state.undo_rec[i].char_storage - n -- @OPTIMIZE: get rid of char_storage and infer it
end
end
end
state.undo_point = state.undo_point - 1
IMSTB_TEXTEDIT_memmove(state.undo_rec, 1, state.undo_rec, 2, state.undo_point - 1)
end
end
-- discard the oldest entry in the redo list--it's bad if this
-- ever happens, but because undo & redo have to store the actual
-- characters in different cases, the redo character buffer can
-- fill up even though the undo buffer didn't
--- @param state StbUndoState
local function stb_textedit_discard_redo(state)
local k = 99 --[[IMSTB_TEXTEDIT_UNDOSTATECOUNT]]
if state.redo_point <= k then
-- if the k'th undo state has characters, clean those up
if state.undo_rec[k].char_storage >= 1 then
local n = state.undo_rec[k].insert_length
-- move the remaining redo character data to the end of the buffer
state.redo_char_point = state.redo_char_point + n
IMSTB_TEXTEDIT_memmove(state.undo_char, state.redo_char_point, state.undo_char, state.redo_char_point - n, IMSTB_TEXTEDIT_UNDOCHARCOUNT - state.redo_char_point + 1)
-- adjust the position of all the other records to account for above memmove
for i = state.redo_point, k - 1 do
if state.undo_rec[i].char_storage >= 1 then
state.undo_rec[i].char_storage = state.undo_rec[i].char_storage + n
end
end
end
-- now move all the redo records towards the end of the buffer; the first one is at 'redo_point'
-- [DEAR IMGUI]
local move_size = 99 --[[IMSTB_TEXTEDIT_UNDOSTATECOUNT]] - state.redo_point - 1
IMSTB_TEXTEDIT_memmove(state.undo_rec, state.redo_point + 1, state.undo_rec, state.redo_point, move_size)
-- now move redo_point to point to the new one
state.redo_point = state.redo_point + 1
end
end
--- @param state StbUndoState
--- @param numchars int
local function stb_text_create_undo_record(state, numchars)
-- any time we create a new undo record, we discard redo
stb_textedit_flush_redo(state)
-- if we have no free records, we have to make room, by sliding the
-- existing records down
if state.undo_point == 99 --[[IMSTB_TEXTEDIT_UNDOSTATECOUNT]] then
stb_textedit_discard_undo(state)
end
-- if the characters to store won't possibly fit in the buffer, we can't undo
if numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT then
state.undo_point = 1
state.undo_char_point = 1
return nil
end
-- if we don't have enough free characters in the buffer, we have to make room
while state.undo_char_point + numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT do
stb_textedit_discard_undo(state)
end
local ret = state.undo_rec[state.undo_point]
state.undo_point = state.undo_point + 1
return ret
end
--- @param state StbUndoState
--- @param pos int
--- @param insert_len int
--- @param delete_len int
--- @return int? # index into undostate.undo_char[]
local function stb_text_createundo(state, pos, insert_len, delete_len)
local r = stb_text_create_undo_record(state, insert_len)
if r == nil then
return nil
end
r.where = pos
r.insert_length = insert_len
r.delete_length = delete_len
if insert_len == 0 then
r.char_storage = -1
return nil
else
r.char_storage = state.undo_char_point
state.undo_char_point = state.undo_char_point + insert_len
return r.char_storage
end
end
--- @param str IMSTB_TEXTEDIT_STRING
--- @param state STB_TexteditState
function stb_text_undo(str, state)
local s = state.undostate
if s.undo_point == 1 then
return
end
-- we need to do two things: apply the undo record, and create a redo record
local u = s.undo_rec[s.undo_point - 1]
local r = s.undo_rec[s.redo_point - 1]
r.char_storage = -1
r.insert_length = u.delete_length
r.delete_length = u.insert_length
r.where = u.where
if u.delete_length > 0 then
-- if the undo record says to delete characters, then the redo record will
-- need to re-insert the characters that get deleted, so we need to store
-- them.
-- there are three cases:
-- there's enough room to store the characters
-- characters stored for *redoing* don't leave room for redo
-- characters stored for *undoing* don't leave room for redo
-- if the last is true, we have to bail
if s.undo_char_point + u.delete_length > IMSTB_TEXTEDIT_UNDOCHARCOUNT then
-- the undo records take up too much character space; there's no space to store the redo characters
r.insert_length = 0
else
-- there's definitely room to store the characters eventually
while s.undo_char_point + u.delete_length > s.redo_char_point do
-- should never happen:
if s.redo_point == 99 --[[IMSTB_TEXTEDIT_UNDOSTATECOUNT]] + 1 then
return
end
-- there's currently not enough room, so discard a redo record
stb_textedit_discard_redo(s)
end
r = s.undo_rec[s.redo_point - 1]
r.char_storage = s.redo_char_point - u.delete_length
s.redo_char_point = s.redo_char_point - u.delete_length
-- now save the characters
for i = 0, u.delete_length - 1 do
s.undo_char[r.char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u.where + i)
end
end
-- now we can carry out the deletion
STB_TEXTEDIT_DELETECHARS(str, u.where, u.delete_length)
end
-- check type of recorded action:
if u.insert_length > 0 then
-- easy case: was a deletion, so we need to insert n characters
u.insert_length = STB_TEXTEDIT_INSERTCHARS(str, u.where, s.undo_char, u.char_storage, u.insert_length)
s.undo_char_point = s.undo_char_point - u.insert_length
end
state.cursor = u.where + u.insert_length
s.undo_point = s.undo_point - 1
s.redo_point = s.redo_point - 1
end
--- @param str IMSTB_TEXTEDIT_STRING
--- @param state STB_TexteditState
function stb_text_redo(str, state)
local s = state.undostate
if s.undo_point == 99 --[[IMSTB_TEXTEDIT_UNDOSTATECOUNT]] + 1 then
return
end
-- we need to do two things: apply the redo record, and create an undo record
local u = s.undo_rec[s.undo_point]
local r = s.undo_rec[s.redo_point]
-- we KNOW there must be room for the undo record, because the redo record
-- was derived from an undo record
u.delete_length = r.insert_length
u.insert_length = r.delete_length
u.where = r.where
u.char_storage = -1
if r.delete_length > 0 then
-- the redo record requires us to delete characters, so the undo record
-- needs to store the characters
if s.undo_char_point + u.insert_length > s.redo_char_point then
u.insert_length = 0
u.delete_length = 0
else
u.char_storage = s.undo_char_point
s.undo_char_point = s.undo_char_point + u.insert_length
-- now save the characters
for i = 0, u.insert_length - 1 do
s.undo_char[u.char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u.where + i)
end
end
STB_TEXTEDIT_DELETECHARS(str, r.where, r.delete_length)
end
if r.insert_length ~= 0 then
-- easy case: need to insert n characters
r.insert_length = STB_TEXTEDIT_INSERTCHARS(str, r.where, s.undo_char, r.char_storage, r.insert_length)
s.redo_char_point = s.redo_char_point + r.insert_length
end
state.cursor = r.where + r.insert_length
s.undo_point = s.undo_point + 1
s.redo_point = s.redo_point + 1
end
--- @param state STB_TexteditState
--- @param where int
--- @param length int
function stb_text_makeundo_insert(state, where, length)
stb_text_createundo(state.undostate, where, 0, length)
end
--- @param str IMSTB_TEXTEDIT_STRING
--- @param state STB_TexteditState
--- @param where int
--- @param length int
function stb_text_makeundo_delete(str, state, where, length)
local s = state.undostate
local p = stb_text_createundo(s, where, length, 0)
if p then
for i = 0, length - 1 do
s.undo_char[p + i] = STB_TEXTEDIT_GETCHAR(str, where + i)
end
end
end
--- @param str IMSTB_TEXTEDIT_STRING
--- @param state STB_TexteditState
--- @param where int
--- @param old_length int
--- @param new_length int
function stb_text_makeundo_replace(str, state, where, old_length, new_length)
local s = state.undostate
local p = stb_text_createundo(s, where, old_length, new_length)
if p then
for i = 0, old_length - 1 do
s.undo_char[p + i] = STB_TEXTEDIT_GETCHAR(str, where + i)
end
end
end
-- reset the state to default
--- @param state STB_TexteditState
--- @param is_single_line bool
local function stb_textedit_clear_state(state, is_single_line)
state.undostate.undo_point = 1
state.undostate.undo_char_point = 1
state.undostate.redo_point = 99 --[[IMSTB_TEXTEDIT_UNDOSTATECOUNT]] + 1
state.undostate.redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT + 1
state.select_end = 1
state.select_start = 1
state.cursor = 1
state.has_preferred_x = false
state.preferred_x = 0
state.cursor_at_end_of_line = false
state.initialized = true
state.single_line = is_single_line
state.insert_mode = false
state.row_count_per_page = 0
end
return {
click = stb_textedit_click,
drag = stb_textedit_drag,
createundo = stb_text_createundo,
initialize_state = stb_textedit_clear_state, -- API initialize
text = stb_textedit_text,
key = stb_textedit_key,
makeundo_replace = stb_text_makeundo_replace,
clamp = stb_textedit_clamp,
prep_selection_at_cursor = stb_textedit_prep_selection_at_cursor,
cut = stb_textedit_cut,
paste = stb_textedit_paste_internal,
HAS_SELECTION = STB_TEXT_HAS_SELECTION
}
-------------------------------------------------------------------------------
-- This software is available under 2 licenses -- choose whichever you prefer.
-------------------------------------------------------------------------------
-- ALTERNATIVE A - MIT License
-- Copyright (c) 2017 Sean Barrett
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
-- this software and associated documentation files (the "Software"), to deal in
-- the Software without restriction, including without limitation the rights to
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-- of the Software, and to permit persons to whom the Software is furnished to do
-- so, subject to the following conditions:
-- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software.
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
-------------------------------------------------------------------------------
-- ALTERNATIVE B - Public Domain (www.unlicense.org)
-- This is free and unencumbered software released into the public domain.
-- Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
-- software, either in source code form or as a compiled binary, for any purpose,
-- commercial or non-commercial, and by any means.
-- In jurisdictions that recognize copyright laws, the author or authors of this
-- software dedicate any and all copyright interest in the software to the public
-- domain. We make this dedication for the benefit of the public at large and to
-- the detriment of our heirs and successors. We intend this dedication to be an
-- overt act of relinquishment in perpetuity of all present and future rights to
-- this software under copyright law.
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-- ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------------
end
__FILES['imstb_truetype.lua'] = function()
--- Dear ImGui
-- This is a Lua port of original `imstb_truetype.h`
-- [DEAR IMGUI]
-- This is a slightly modified version of stb_truetype.h 1.26.
-- Mostly fixing for compiler and static analyzer warnings.
-- Grep for [DEAR IMGUI] to find the changes.
-- stb_truetype.h - v1.26 - public domain
-- authored from 2009-2021 by Sean Barrett / RAD Game Tools
--
-- =======================================================================
--
-- NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES
--
-- This library does no range checking of the offsets found in the file,
-- meaning an attacker can use it to read arbitrary memory.
--
-- =======================================================================
--
-- This library processes TrueType files:
-- parse files
-- extract glyph metrics
-- extract glyph shapes
-- render glyphs to one-channel bitmaps with antialiasing (box filter)
--
-- Todo:
-- non-MS cmaps
-- crashproof on bad data
-- hinting? (no longer patented)
-- cleartype-style AA?
-- optimize: build edge-list directly from curves
-- optimize: rasterize directly from curves?
--
-- ADDITIONAL CONTRIBUTORS
--
-- Mikko Mononen: compound shape support, more cmap formats
-- Tor Andersson: kerning, subpixel rendering
-- Dougall Johnson: OpenType / Type 2 font handling
-- Daniel Ribeiro Maciel: basic GPOS-based kerning
--
-- Misc other:
-- Ryan Gordon
-- Simon Glass
-- github:IntellectualKitty
-- Imanol Celaya
-- Daniel Ribeiro Maciel
--
-- Bug/warning reports/fixes:
-- "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe
-- Cass Everitt Martins Mozeiko github:aloucks
-- stoiko (Haemimont Games) Cap Petschulat github:oyvindjam
-- Brian Hook Omar Cornut github:vassvik
-- Walter van Niftrik Ryan Griege
-- David Gow Peter LaValle
-- David Given Sergey Popov
-- Ivan-Assen Ivanov Giumo X. Clanjor
-- Anthony Pesch Higor Euripedes
-- Johan Duparc Thomas Fields
-- Hou Qiming Derek Vinyard
-- Rob Loach Cort Stratton
-- Kenney Phillis Jr. Brian Costabile
-- Ken Voskuil (kaesve)
--
-- USAGE
--
-- "Load" a font file from a memory buffer (you have to keep the buffer loaded)
-- stbtt_InitFont()
-- stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections
--
-- Character advance/positioning
-- stbtt_GetFontVMetrics()
--
-- Starting with version 1.06, the rasterizer was replaced with a new,
-- faster and generally-more-precise rasterizer. The new rasterizer more
-- accurately measures pixel coverage for anti-aliasing, except in the case
-- where multiple shapes overlap, in which case it overestimates the AA pixel
-- coverage. Thus, anti-aliasing of intersecting shapes may look wrong.
-- (This port only implements the new rasterizer.)
--
-- ADDITIONAL DOCUMENTATION
--
-- Some important concepts to understand to use this library:
--
-- Codepoint
-- Characters are defined by unicode codepoints, e.g. 65 is
-- uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is
-- the hiragana for "ma".
--
-- Glyph
-- A visual character shape (every codepoint is rendered as
-- some glyph)
--
-- Glyph index
-- A font-specific integer ID representing a glyph
--
-- Baseline
-- Glyph shapes are defined relative to a baseline, which is the
-- bottom of uppercase characters. Characters extend both above
-- and below the baseline.
--
-- Current Point
-- As you draw text to the screen, you keep track of a "current point"
-- which is the origin of each character. The current point's vertical
-- position is the baseline. Even "baked fonts" use this model.
--
-- Vertical Font Metrics
-- The vertical qualities of the font, used to vertically position
-- and space the characters. See docs for stbtt_GetFontVMetrics.
--
-- Font Size in Pixels or Points
-- The preferred interface for specifying font sizes in stb_truetype
-- is to specify how tall the font's vertical extent should be in pixels.
--
-- DETAILED USAGE:
--
-- Scale:
-- Select how high you want the font to be, in pixels.
-- Call ScaleForPixelHeight to compute a scale factor SF that will be
-- used by all other functions.
--
-- Current point:
-- Set the current point where the first character will appear. The
-- first character could extend left of the current point; this is font
-- dependent. You can either choose a current point that is the leftmost
-- point and hope, or add some padding, or check the bounding box or
-- left-side-bearing of the first character to be displayed and set
-- the current point based on that.
--
-- Displaying a character:
-- Compute the bounding box of the character. It will contain signed values
-- relative to <current_point, baseline>. I.e. if it returns x0,y0,x1,y1,
-- then the character should be displayed in the rectangle from
-- <current_point+SF*x0, baseline+SF*y0> to <current_point+SF*x1,baseline+SF*y1).
--
-- Advancing for the next character:
-- Call GlyphHMetrics, and compute 'current_point += SF * advance'.
--
--
-- ADVANCED USAGE
--
-- Quality:
--
-- - Use the functions with Subpixel at the end to allow your characters
-- to have subpixel positioning. Since the font is anti-aliased, not
-- hinted, this is very import for quality. (This is not possible with
-- baked fonts.)
--
-- Performance:
--
-- - Convert Unicode codepoints to glyph indexes and operate on the glyphs;
-- if you don't do this, stb_truetype is forced to do the conversion on
-- every call.
--
-- NOTES
--
-- The system uses the raw data found in the .ttf file without changing it
-- and without building auxiliary data structures. This is a bit inefficient
-- on little-endian systems (the data is big-endian), but assuming you're
-- caching the bitmaps or glyph shapes this shouldn't be a big deal.
--- STBTT_RASTERIZER_VERSION == 2
local stbtt_fontinfo
local stbtt_InitFont
local stbtt_MakeGlyphBitmapSubpixelPrefilter
local stbtt_GetFontVMetrics
local stbtt__GetGlyphInfoT2
local stbtt__GetGlyphShapeT2
local stbtt_GetGlyphShape
local stbtt_GetGlyphBitmapBox
local stbtt_GetGlyphBitmapBoxSubpixel
local stbtt_GetGlyphHMetrics
local stbtt_GetFontOffsetForIndex
local stbtt_ScaleForPixelHeight
local stbtt_FindGlyphIndex
local STBTT_MAX_OVERSAMPLE = 8
--- @enum STBTT_PLATFORM_ID
local STBTT_PLATFORM_ID = { -- platformID
UNICODE = 0,
MAC = 1,
ISO = 2,
MICROSOFT = 3,
}
--- @enum STBTT_MS_EID
local STBTT_MS_EID = { -- encodingID for STBTT_PLATFORM_ID_MICROSOFT
SYMBOL = 0,
UNICODE_BMP = 1,
SHIFTJIS = 2,
UNICODE_FULL = 10
}
-- original enum doesn't have a name, so we name it
--- @enum STBTT_VTX
local STBTT_VTX = {
MOVE = 1,
LINE = 2,
CURVE = 3,
CUBIC = 4
}
local bit32 = bit32
local math = math
local STBTT_assert = assert
local STBTT_sort = table.sort
local STBTT_sqrt = math.sqrt
local STBTT_fabs = math.abs
local STBTT_ifloor = math.floor
local STBTT_iceil = math.ceil
local trunc = function(x)
if x >= 0 then
return math.floor(x)
else
return math.ceil(x)
end
end
local function STBTT__NOTUSED(_) return end
--- @alias stbtt_int32 int
local stbtt_int32 = ImS32
local stbtt_uint32 = ImU32
local stbtt_int16 = ImS16
local stbtt_uint16 = ImU16
local stbtt_int8 = ImS8
local stbtt_uint8 = ImU8
local unsigned_char = stbtt_uint8
-- private structure
--- @class stbtt__buf
--- @field data? table # 1-based byte table
--- @field offset int # Every access to `data` will be offset by this
--- @field cursor int # >= 0
--- @field size int
--- @param initial_offset? int
--- @return stbtt__buf
--- @nodiscard
local function stbtt__buf(initial_offset)
return {
data = nil,
offset = initial_offset or 0,
cursor = 0,
size = nil
}
end
-- The following structure is defined publicly so you can declare one on
-- the stack or as a global or etc, but you should treat it as opaque.
--- @class stbtt_fontinfo
--- @field userdata any
--- @field data table # pointer to .ttf file
--- @field fontstart int # offset of start of font
--- @field numGlyphs int # number of glyphs, needed for range checking
--- @field loca int # table location as offset from start of .ttf
--- @field head int # table location as offset from start of .ttf
--- @field glyf int # table location as offset from start of .ttf
--- @field hhea int # table location as offset from start of .ttf
--- @field hmtx int # table location as offset from start of .ttf
--- @field kern int # table location as offset from start of .ttf
--- @field gpos int # table location as offset from start of .ttf
--- @field svg int # table location as offset from start of .ttf
--- @field index_map int # a cmap mapping for our chosen character encoding
--- @field indexToLocFormat int # format needed to map from glyph index to glyph
--- @field cff stbtt__buf # cff font data
--- @field charstrings stbtt__buf # the charstring index
--- @field gsubrs stbtt__buf # global charstring subroutines index
--- @field subrs stbtt__buf # private charstring subroutines index
--- @field fontdicts stbtt__buf # array of font dicts
--- @field fdselect stbtt__buf # map from glyph to fontdict
--- @return stbtt_fontinfo
function stbtt_fontinfo()
return {
data = nil,
fontstart = nil,
numGlyphs = nil,
loca = nil,
head = nil,
glyf = nil,
hhea = nil,
hmtx = nil,
kern = nil,
gpos = nil,
svg = nil,
index_map = nil,
indexToLocFormat = nil,
cff = nil,
charstrings = nil,
gsubrs = nil,
subrs = nil,
fontdicts = nil,
fdselect = nil
}
end
--- @class stbtt_vertex
--- @field x int
--- @field y int
--- @field cx int
--- @field cy int
--- @field cx1 int
--- @field cy1 int
--- @field type int
--- @field padding int
--- @return stbtt_vertex
--- @nodiscard
local function stbtt_vertex()
return {
x = nil,
y = nil,
cx = nil,
cy = nil,
cx1 = nil,
cy1 = nil,
type = nil,
padding = nil
}
end
--- @class stbtt__csctx
--- @field bounds bool
--- @field started bool
--- @field first_x float
--- @field first_y float
--- @field x float
--- @field y float
--- @field min_x int
--- @field max_x int
--- @field min_y int
--- @field max_y int
--- @field pvertices stbtt_vertex[]
--- @field num_vertices int
--- @return stbtt__csctx
--- @nodiscard
local function stbtt__csctx()
return {
bounds = false,
started = false,
first_x = nil,
first_y = nil,
x = nil,
y = nil,
min_x = nil,
max_x = nil,
min_y = nil,
max_y = nil,
pvertices = nil,
num_vertices = nil
}
end
--- @param bounds bool
--- @return stbtt__csctx
--- @nodiscard
local function STBTT__CSCTX_INIT(bounds)
local this = stbtt__csctx()
this.bounds = bounds
this.started = false
this.first_x = 0
this.first_y = 0
this.x = 0
this.y = 0
this.min_x = 0
this.max_x = 0
this.min_y = 0
this.max_y = 0
this.pvertices = nil
this.num_vertices = 0
return this
end
--- stbtt_kerningentry
--- stbtt_pack_range
--- stbtt_packedchar
--- @class stbtt__edge
--- @field x0 float
--- @field y0 float
--- @field x1 float
--- @field y1 float
--- @field invert bool
--- @return stbtt__edge
--- @nodiscard
local function stbtt__edge()
return {
x0 = nil,
y0 = nil,
x1 = nil,
y1 = nil,
invert = false
}
end
--- @class stbtt__active_edge
--- @return stbtt__active_edge
--- @nodiscard
local function stbtt__active_edge()
return {
next = nil,
fx = nil,
fdx = nil,
fdy = nil,
direction = nil,
sy = nil,
ey = nil
}
end
--- @return stbtt__active_edge
--- @nodiscard
local function stbtt__new_active(e, off_x, start_point)
local z = stbtt__active_edge()
local dxdy = (e.x1 - e.x0) / (e.y1 - e.y0)
z.fdx = dxdy
z.fdy = (dxdy ~= 0.0) and (1.0 / dxdy) or 0.0
z.fx = e.x0 + dxdy * (start_point - e.y0)
z.fx = z.fx - off_x
z.direction = (e.invert) and 1.0 or -1.0
z.sy = e.y0
z.ey = e.y1
z.next = nil
return z
end
-- @TODO: don't expose this structure
--- @class stbtt__bitmap
--- @field w int
--- @field h int
--- @field stride int
--- @field pixels unsigned_char[]
--- @return stbtt__bitmap
--- @nodiscard
local function stbtt__bitmap()
return {
w = nil,
h = nil,
stride = nil,
pixels = nil
}
end
--- @class stbtt__point
--- @field x number
--- @field y number
--- @return stbtt__point
--- @nodiscard
local function stbtt__point() return { x = nil, y = nil } end
----------------------------------------------
----------------------------------------------
---
--- stbtt__buf helpers to parse data from file
---
---
--- @param b stbtt__buf
local function stbtt__buf_get8(b)
if b.cursor >= b.size then return 0 end
local result = b.data[b.offset + b.cursor + 1]
b.cursor = b.cursor + 1
return result
end
--- @param b stbtt__buf
local function stbtt__buf_peek8(b)
if b.cursor >= b.size then return 0 end
return b.data[b.offset + b.cursor + 1]
end
--- @param b stbtt__buf
--- @param o int
local function stbtt__buf_seek(b, o)
STBTT_assert(not (o > b.size or o < 0))
if o > b.size or o < 0 then
b.cursor = b.size
else
b.cursor = o
end
end
--- @param b stbtt__buf
--- @param o int
local function stbtt__buf_skip(b, o)
stbtt__buf_seek(b, b.cursor + o)
end
local function stbtt__buf_get(b, n)
local v = 0
STBTT_assert(n >= 1 and n <= 4)
for _ = 1, n do
v = bit32.bor(bit32.lshift(v, 8), stbtt__buf_get8(b))
end
return v
end
--- @param p? table
--- @param size int
--- @param offset? int
--- @return stbtt__buf
--- @nodiscard
local function stbtt__new_buf(p, size, offset)
local r = stbtt__buf(offset)
STBTT_assert(size < 0x40000000)
r.data = p
r.size = trunc(size)
r.cursor = 0
return r
end
--- @param b stbtt__buf
local function stbtt__buf_get16(b) return stbtt__buf_get(b, 2) end
--- @param b stbtt__buf
local function stbtt__buf_get32(b) return stbtt__buf_get(b, 4) end
--- @param b stbtt__buf
--- @param o int
--- @param s int
--- @return stbtt__buf
--- @nodiscard
local function stbtt__buf_range(b, o, s)
local r = stbtt__new_buf(nil, 0)
if (o < 0 or s < 0 or o > b.size or s > (b.size - o)) then
return r
end
r.data = b.data
r.offset = b.offset + o
r.size = s
return r
end
--- @param b stbtt__buf
local function stbtt__cff_get_index(b)
local count, start, offsize
start = b.cursor
count = stbtt__buf_get16(b)
if count > 0 then
offsize = stbtt__buf_get8(b)
STBTT_assert(offsize >= 1 and offsize <= 4)
stbtt__buf_skip(b, count * offsize)
stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1)
end
return stbtt__buf_range(b, start, b.cursor - start)
end
--- @param b stbtt__buf
local function stbtt__cff_int(b)
local b0 = stbtt__buf_get8(b)
if b0 >= 32 and b0 <= 246 then return b0 - 139
elseif b0 >= 247 and b0 <= 250 then return (b0 - 247) * 256 + stbtt__buf_get8(b) + 108
elseif b0 >= 251 and b0 <= 254 then return -(b0 - 251) * 256 - stbtt__buf_get8(b) - 108
elseif b0 == 28 then return stbtt__buf_get16(b)
elseif b0 == 29 then return stbtt__buf_get32(b)
end
STBTT_assert(false)
return 0
end
--- @param b stbtt__buf
local function stbtt__cff_skip_operand(b)
local v
local b0 = stbtt__buf_peek8(b)
STBTT_assert(b0 >= 28)
if b0 == 30 then
stbtt__buf_skip(b, 1)
while b.cursor < b.size do
v = stbtt__buf_get8(b)
if (bit32.band(v, 0xF) == 0xF or bit32.rshift(v, 4) == 0xF) then
break
end
end
else
stbtt__cff_int(b)
end
end
--- @param b stbtt__buf
--- @param key int
--- @return stbtt__buf
--- @nodiscard
local function stbtt__dict_get(b, key)
stbtt__buf_seek(b, 0)
while b.cursor < b.size do
local start = b.cursor
local _end, op
while stbtt__buf_peek8(b) >= 28 do
stbtt__cff_skip_operand(b)
end
_end = b.cursor
op = stbtt__buf_get8(b)
if op == 12 then op = bit32.bor(stbtt__buf_get8(b), 0x100) end
if op == key then return stbtt__buf_range(b, start, _end - start) end
end
return stbtt__buf_range(b, 0, 0)
end
--- @param b stbtt__buf
--- @param key int
--- @param outcount int
--- @return int[]
--- @nodiscard
local function stbtt__dict_get_ints(b, key, outcount)
local operands = stbtt__dict_get(b, key)
local out = {}
for i = 1, outcount do
if operands.cursor >= operands.size then break end
out[i] = stbtt__cff_int(operands)
end
return out
end
--- @param b stbtt__buf
local function stbtt__cff_index_count(b)
stbtt__buf_seek(b, 0)
return stbtt__buf_get16(b)
end
--- @param b stbtt__buf
--- @param i int
--- @return stbtt__buf
--- @nodiscard
local function stbtt__cff_index_get(b, i)
local count, offsize, start, _end
stbtt__buf_seek(b, 0)
count = stbtt__buf_get16(b)
offsize = stbtt__buf_get8(b)
STBTT_assert(i >= 0 and i < count)
STBTT_assert(offsize >= 1 and offsize <= 4)
stbtt__buf_skip(b, i * offsize)
start = stbtt__buf_get(b, offsize)
_end = stbtt__buf_get(b, offsize)
return stbtt__buf_range(b, 2 + (count + 1) * offsize + start, _end - start)
end
-------------------------------------
-------------------------------------
---
--- accessors to parse data from file
---
---
--- UNIMPLEMENTED / UNUSED:
--- stbtt_IsGlyphEmpty
--- stbtt_GetKerningTableLength
--- stbtt_GetKerningTable
--- stbtt__GetGlyphKernInfoAdvance
--- stbtt__GetCoverageIndex
--- stbtt__GetGlyphClass
--- stbtt__GetGlyphGPOSInfoAdvance
--- stbtt_GetGlyphKernAdvance
--- stbtt_FindSVGDoc
--- stbtt_GetGlyphSVG
--- stbtt_GetCodepointSVG
--- stbtt_GetCodepointKernAdvance
--- stbtt_GetFontVMetricsOS2
--- stbtt_GetCodepointHMetrics
--- stbtt_ScaleForMappingEmToPixels
--- stbtt_GetFontBoundingBox
--- stbtt_GetCodepointBitmapBox
--- stbtt_GetCodepointBitmapBoxSubpixel
--- stbtt_GetCodepointBox
--- stbtt_GetNumberOfFonts_internal
--- stbtt__get_svg
local function ttUSHORT(p, offset) return stbtt_uint16(p[offset + 1] * 256 + p[offset + 2]) end
local function ttSHORT(p, offset) return stbtt_int16(p[offset + 1] * 256 + p[offset + 2]) end
local function ttULONG(p, offset) return stbtt_uint32(bit32.lshift(p[offset + 1], 24) + bit32.lshift(p[offset + 2], 16) + bit32.lshift(p[offset + 3], 8) + p[offset + 4]) end
local function ttLONG(p, offset) return stbtt_int32(bit32.lshift(p[offset + 1], 24) + bit32.lshift(p[offset + 2], 16) + bit32.lshift(p[offset + 3], 8) + p[offset + 4]) end
local function ttBYTE(p, offset) return stbtt_uint8(p[offset + 1]) end
local function ttCHAR(p, offset) return stbtt_int8(p[offset + 1]) end
local function stbtt_tag4(p, c0, c1, c2, c3, offset) offset = offset or 0; return p[offset + 1] == c0 and p[offset + 2] == c1 and p[offset + 3] == c2 and p[offset + 4] == c3 end
local function stbtt_tag(p, str, offset) local c0, c1, c2, c3 = string.byte(str, 1, 4); return stbtt_tag4(p, c0, c1, c2, c3, offset) end
local function stbtt__isfont(font)
-- check the version number
if stbtt_tag4(font, 49, 0, 0, 0) then return true end -- TrueType 1
if stbtt_tag(font, "typ1") then return true end -- TrueType with type 1 font -- we don't support this!
if stbtt_tag(font, "OTTO") then return true end -- OpenType with CFF
if stbtt_tag4(font, 0, 1, 0, 0) then return true end -- OpenType 1.0
if stbtt_tag(font, "true") then return true end -- Apple specification for TrueType fonts
return false
end
-- @OPTIMIZE: binary search
local function stbtt__find_table(data, font_start, tag)
local num_tables = ttUSHORT(data, font_start + 4)
local tabledir = font_start + 12
for i = 0, num_tables - 1 do
local loc = tabledir + i * 16
if stbtt_tag(data, tag, loc + 0) then
return ttULONG(data, loc + 8)
end
end
return 0
end
local function stbtt_GetFontOffsetForIndex_internal(font_collection, index)
-- if it's just a font, there's only one valid index
if stbtt__isfont(font_collection) then
if index == 0 then return 0 else return -1 end
end
-- check if it's a TTC
if stbtt_tag(font_collection, "ttcf") then
-- version 1?
if ttULONG(font_collection, 4) == 0x00010000 or ttULONG(font_collection, 4) == 0x00020000 then
local n = ttLONG(font_collection, 8)
if index >= n then
return -1
end
return ttULONG(font_collection, 12 + index * 4)
end
end
return -1
end
--- @param cff stbtt__buf
--- @param fontdict stbtt__buf
local function stbtt__get_subrs(cff, fontdict)
local private_loc = stbtt__dict_get_ints(fontdict, 18, 2)
if (private_loc[2] == 0 or private_loc[1] == 0) then
return stbtt__new_buf(nil, 0)
end
local pdict = stbtt__buf_range(cff, private_loc[2], private_loc[1])
local subrsoff = stbtt__dict_get_ints(pdict, 19, 1)
if subrsoff[1] == 0 then
return stbtt__new_buf(nil, 0)
end
stbtt__buf_seek(cff, private_loc[2] + subrsoff[1])
return stbtt__cff_get_index(cff)
end
--- @return bool
local function stbtt_InitFont_internal(info, data, fontstart)
local cmap
local numTables
info.data = data
info.fontstart = fontstart
info.cff = stbtt__new_buf(nil, 0)
cmap = stbtt__find_table(data, fontstart, "cmap") -- required
info.loca = stbtt__find_table(data, fontstart, "loca") -- required
info.head = stbtt__find_table(data, fontstart, "head") -- required
info.glyf = stbtt__find_table(data, fontstart, "glyf") -- required
info.hhea = stbtt__find_table(data, fontstart, "hhea") -- required
info.hmtx = stbtt__find_table(data, fontstart, "hmtx") -- required
info.kern = stbtt__find_table(data, fontstart, "kern") -- not required
info.gpos = stbtt__find_table(data, fontstart, "GPOS") -- not required
if (cmap == 0 or info.head == 0 or info.hhea == 0 or info.hmtx == 0) then
return false
end
if info.glyf ~= 0 then
-- required for truetype
if info.loca == 0 then
return false
end
else
-- initialization for CFF / Type2 fonts (OTF)
local cff = stbtt__find_table(data, fontstart, "CFF ")
if cff == 0 then
return false
end
info.fontdicts = stbtt__new_buf(nil, 0)
info.fdselect = stbtt__new_buf(nil, 0)
-- @TODO this should use size from table (not 512MB)
info.cff = stbtt__new_buf(data, 64 * 1024 * 1024, cff) -- PORT: 64MB here instead of 512MB
local b = info.cff
-- read the header
stbtt__buf_skip(b, 2)
stbtt__buf_seek(b, stbtt__buf_get8(b)) -- hdrsize
-- @TODO the name INDEX could list multiple fonts,
-- but we just use the first one.
stbtt__cff_get_index(b) -- name INDEX
local topdictidx = stbtt__cff_get_index(b)
local topdict = stbtt__cff_index_get(topdictidx, 0)
stbtt__cff_get_index(b) -- string INDEX
info.gsubrs = stbtt__cff_get_index(b)
local charstrings = stbtt__dict_get_ints(topdict, 17, 1)
local cstype = stbtt__dict_get_ints(topdict, bit32.bor(0x100, 6), 1)
local fdarrayoff = stbtt__dict_get_ints(topdict, bit32.bor(0x100, 36), 1)
local fdselectoff = stbtt__dict_get_ints(topdict, bit32.bor(0x100, 37), 1)
info.subrs = stbtt__get_subrs(b, topdict)
-- we only support Type 2 charstrings
if cstype[1] ~= 2 then
return false
end
if charstrings[1] == 0 then
return false
end
if fdarrayoff[1] ~= 0 then
-- looks like a CID font
if fdselectoff[1] == 0 then
return false
end
stbtt__buf_seek(b, fdarrayoff[1])
info.fontdicts = stbtt__cff_get_index(b)
info.fdselect = stbtt__buf_range(b, fdselectoff[1], b.size - fdselectoff[1])
end
stbtt__buf_seek(b, charstrings[1])
info.charstrings = stbtt__cff_get_index(b)
end
local t = stbtt__find_table(data, fontstart, "maxp")
if t ~= 0 then
info.numGlyphs = ttUSHORT(data, t + 4)
else
info.numGlyphs = 0xffff
end
info.svg = -1
-- find a cmap encoding table we understand *now* to avoid searching
-- later. (todo: could make this installable)
-- the same regardless of glyph.
numTables = ttUSHORT(data, cmap + 2)
info.index_map = 0
for i = 0, numTables - 1 do
local encoding_record = cmap + 4 + 8 * i
-- find an encoding we understand:
local platform_id = ttUSHORT(data, encoding_record)
if platform_id == STBTT_PLATFORM_ID.MICROSOFT then
local ms_eid = ttUSHORT(data, encoding_record + 2)
if ms_eid == STBTT_MS_EID.UNICODE_BMP or ms_eid == STBTT_MS_EID.UNICODE_FULL then
-- MS/Unicode
info.index_map = cmap + ttULONG(data, encoding_record + 4)
end
elseif platform_id == STBTT_PLATFORM_ID.UNICODE then
-- Mac/iOS has these
-- all the encodingIDs are unicode, so we don't bother to check it
info.index_map = cmap + ttULONG(data, encoding_record + 4)
end
end
if info.index_map == 0 then
return false
end
info.indexToLocFormat = ttUSHORT(data, info.head + 50)
return true
end
-- If you're going to perform multiple operations on the same character
-- and you want a speed-up, call this function with the character you're
-- going to process, then use glyph-based functions instead of the
-- codepoint-based functions.
-- Returns 0 if the character codepoint is not defined in the font.
--- @param info stbtt_fontinfo
--- @param unicode_codepoint int
function stbtt_FindGlyphIndex(info, unicode_codepoint)
local data = info.data
local index_map = info.index_map
local format = ttUSHORT(data, index_map + 0)
if format == 0 then -- apple byte encoding
local bytes = ttUSHORT(data, index_map + 2)
if unicode_codepoint < bytes - 6 then
return ttBYTE(data, index_map + 6 + unicode_codepoint)
end
return 0
elseif format == 6 then
local first = ttUSHORT(data, index_map + 6)
local count = ttUSHORT(data, index_map + 8)
if unicode_codepoint >= first and unicode_codepoint < first + count then
return ttUSHORT(data, index_map + 10 + (unicode_codepoint - first) * 2)
end
return 0
elseif format == 2 then
STBTT_assert(false) -- @TODO: high-byte mapping for japanese/chinese/korean
return 0
elseif format == 4 then -- standard mapping for windows fonts: binary search collection of ranges
local segcount = bit32.rshift(ttUSHORT(data, index_map + 6), 1)
local searchRange = bit32.rshift(ttUSHORT(data, index_map + 8), 1)
local entrySelector = ttUSHORT(data, index_map + 10)
local rangeShift = bit32.rshift(ttUSHORT(data, index_map + 12), 1)
-- do a binary search of the segments
local endCount = index_map + 14
local search = endCount
if unicode_codepoint > 0xffff then
return 0
end
-- they lie from endCount .. endCount + segCount
-- but searchRange is the nearest power of two, so...
if unicode_codepoint >= ttUSHORT(data, search + rangeShift * 2) then
search = search + rangeShift * 2
end
-- now decrement to bias correctly to find smallest
search = search - 2
while entrySelector ~= 0 do
searchRange = bit32.rshift(searchRange, 1)
local _end = ttUSHORT(data, search + searchRange * 2)
if unicode_codepoint > _end then
search = search + searchRange * 2
end
entrySelector = entrySelector - 1
end
search = search + 2
do
local item = stbtt_uint16(bit32.rshift(search - endCount, 1))
local start = ttUSHORT(data, index_map + 14 + segcount * 2 + 2 + 2 * item)
local last = ttUSHORT(data, endCount + 2 * item)
if unicode_codepoint < start or unicode_codepoint > last then
return 0
end
local offset = ttUSHORT(data, index_map + 14 + segcount * 6 + 2 + 2 * item)
if offset == 0 then
return stbtt_uint16(unicode_codepoint + ttSHORT(data, index_map + 14 + segcount * 4 + 2 + 2 * item))
end
return ttUSHORT(data, offset + (unicode_codepoint - start) * 2 + index_map + 14 + segcount * 6 + 2 + 2 * item)
end
elseif format == 12 or format == 13 then
local ngroups = ttULONG(data, index_map + 12)
local low = 0
local high = ngroups
-- Binary search the right group.
while low < high do
local mid = low + bit32.rshift(high - low, 1) -- rounds down, so low <= mid < high
local start_char = ttULONG(data, index_map + 16 + mid * 12)
local end_char = ttULONG(data, index_map + 16 + mid * 12 + 4)
if unicode_codepoint < start_char then
high = mid
elseif unicode_codepoint > end_char then
low = mid + 1
else
local start_glyph = ttULONG(data, index_map + 16 + mid * 12 + 8)
if format == 12 then
return start_glyph + unicode_codepoint - start_char
else -- format == 13
return start_glyph
end
end
end
return 0 -- not found
end
-- @TODO
STBTT_assert(false)
return 0
end
--- @param v stbtt_vertex
--- @param vtx_type STBTT_VTX
--- @param x stbtt_int32
--- @param y stbtt_int32
--- @param cx stbtt_int32
--- @param cy stbtt_int32
local function stbtt_setvertex(v, vtx_type, x, y, cx, cy)
v.type = vtx_type
v.x = (stbtt_int16)(x)
v.y = (stbtt_int16)(y)
v.cx = (stbtt_int16)(cx)
v.cy = (stbtt_int16)(cy)
end
local function stbtt__GetGlyfOffset(info, glyph_index)
STBTT_assert(info.cff.size == 0)
if glyph_index >= info.numGlyphs then return -1 end -- glyph index out of range
if info.indexToLocFormat >= 2 then return -1 end -- unknown index->glyph map format
local g1, g2
if info.indexToLocFormat == 0 then
g1 = info.glyf + ttUSHORT(info.data, info.loca + glyph_index * 2) * 2
g2 = info.glyf + ttUSHORT(info.data, info.loca + glyph_index * 2 + 2) * 2
else
g1 = info.glyf + ttULONG(info.data, info.loca + glyph_index * 4)
g2 = info.glyf + ttULONG(info.data, info.loca + glyph_index * 4 + 4)
end
if g1 == g2 then return -1 else return g1 end -- if length is 0, return -1
end
-- Gets the bounding box of the visible part of the glyph, in unscaled coordinates
--- @return integer, integer?, integer?, integer?, integer?
local function stbtt_GetGlyphBox(info, glyph_index)
local n, x0, y0, x1, y1
if info.cff.size ~= 0 then
n, x0, y0, x1, y1 = stbtt__GetGlyphInfoT2(info, glyph_index)
else
local g = stbtt__GetGlyfOffset(info, glyph_index)
if g < 0 then return 0 end
x0 = ttSHORT(info.data, g + 2)
y0 = ttSHORT(info.data, g + 4)
x1 = ttSHORT(info.data, g + 6)
y1 = ttSHORT(info.data, g + 8)
end
return 1, x0, y0, x1, y1
end
--- @param vertices stbtt_vertex[]
--- @param num_vertices int
--- @param was_off bool
--- @param start_off bool
--- @param sx stbtt_int32
--- @param sy stbtt_int32
--- @param scx stbtt_int32
--- @param scy stbtt_int32
--- @param cx stbtt_int32
--- @param cy stbtt_int32
local function stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx, sy, scx, scy, cx, cy)
if start_off then
if was_off then
stbtt_setvertex(vertices[num_vertices + 1], STBTT_VTX.CURVE, bit32.rshift(cx + scx, 1), bit32.rshift(cy + scy, 1), cx, cy)
num_vertices = num_vertices + 1
end
stbtt_setvertex(vertices[num_vertices + 1], STBTT_VTX.CURVE, sx, sy, scx, scy)
num_vertices = num_vertices + 1
else
if was_off then
stbtt_setvertex(vertices[num_vertices + 1], STBTT_VTX.CURVE, sx, sy, cx, cy)
num_vertices = num_vertices + 1
else
stbtt_setvertex(vertices[num_vertices + 1], STBTT_VTX.LINE, sx, sy, 0, 0)
num_vertices = num_vertices + 1
end
end
return num_vertices
end
--- @param info stbtt_fontinfo
--- @param glyph_index int
--- @return int num_vertices
--- @return stbtt_vertex[]? vertices
local function stbtt__GetGlyphShapeTT(info, glyph_index)
local data = info.data
local num_vertices
local vertices = nil
local g = stbtt__GetGlyfOffset(info, glyph_index)
if g < 0 then return 0 end
local numberOfContours = ttSHORT(data, g)
if numberOfContours > 0 then
local endPtsOfContours = g + 10
local ins = ttUSHORT(data, endPtsOfContours + numberOfContours * 2)
local points = (endPtsOfContours + numberOfContours * 2 + 2 + ins) + 1 -- 1-based idx
local n = 1 + ttUSHORT(data, endPtsOfContours + numberOfContours * 2 - 2)
local m = n + 2 * numberOfContours -- a loose bound on how many vertices we might need
vertices = {}
for i = 1, m do vertices[i] = stbtt_vertex() end
local j = 0
local was_off = false
local start_off = false
local next_move = 0
local flagcount = 0
-- in first pass, we load uninterpreted data into the allocated array
-- above, shifted to the end of the array so we won't overwrite it when
-- we create our final data starting from the front
local off = m - n -- starting offset for uninterpreted data, regardless of how m ends up being calculated
-- first load flags
local flags = 0
for i = 1, n do
if flagcount == 0 then
flags = data[points]
points = points + 1
if bit32.band(flags, 8) ~= 0 then
flagcount = data[points]
points = points + 1
end
else
flagcount = flagcount - 1
end
vertices[off + i].type = flags
end
-- now load x coordinates
local x = 0
for i = 1, n do
flags = vertices[off + i].type
if bit32.band(flags, 2) ~= 0 then
local dx = data[points]
points = points + 1
if bit32.band(flags, 16) ~= 0 then -- ???
x = x + dx
else
x = x - dx
end
else
if bit32.band(flags, 16) == 0 then
x = x + stbtt_int16(data[points] * 256 + data[points + 1])
points = points + 2
end
end
vertices[off + i].x = stbtt_int16(x)
end
-- now load y coordinates
local y = 0
for i = 1, n do
flags = vertices[off + i].type
if bit32.band(flags, 4) ~= 0 then
local dy = data[points]
points = points + 1
if bit32.band(flags, 32) ~= 0 then -- ???
y = y + dy
else
y = y - dy
end
else
if bit32.band(flags, 32) == 0 then
y = y + stbtt_int16(data[points] * 256 + data[points + 1])
points = points + 2
end
end
vertices[off + i].y = stbtt_int16(y)
end
-- now convert them to our format
num_vertices = 0
local sx, sy, cx, cy, scx, scy = 0, 0, 0, 0, 0, 0
for i = 1, n do
flags = vertices[off + i].type
x = stbtt_int16(vertices[off + i].x)
y = stbtt_int16(vertices[off + i].y)
if next_move == i - 1 then
if i ~= 1 then
num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx, sy, scx, scy, cx, cy)
end
-- now start the new one
start_off = (bit32.band(flags, 1) == 0)
if start_off then
-- if we start off with an off-curve point, then when we need to find a point on the curve
-- where we can start, and we need to save some state for when we wraparound.
scx = x
scy = y
if bit32.band(vertices[off + i + 1].type, 1) == 0 then
-- next point is also a curve point, so interpolate an on-point curve
sx = bit32.rshift(x + stbtt_int32(vertices[off + i + 1].x), 1)
sy = bit32.rshift(y + stbtt_int32(vertices[off + i + 1].y), 1)
else
-- otherwise just use the next point as our start point
sx = stbtt_int32(vertices[off + i + 1].x)
sy = stbtt_int32(vertices[off + i + 1].y)
i = i + 1 -- we're using point i+1 as the starting point, so skip it
end
else
sx = x
sy = y
end
stbtt_setvertex(vertices[num_vertices + 1], STBTT_VTX.MOVE, sx, sy, 0, 0)
num_vertices = num_vertices + 1
was_off = false
next_move = 1 + ttUSHORT(data, endPtsOfContours + j * 2)
j = j + 1
else
if bit32.band(flags, 1) == 0 then -- if it's a curve
if was_off then -- two off-curve control points in a row means interpolate an on-curve midpoint
stbtt_setvertex(vertices[num_vertices + 1], STBTT_VTX.CURVE, bit32.rshift(cx + x, 1), bit32.rshift(cy + y, 1), cx, cy)
num_vertices = num_vertices + 1
end
cx = x
cy = y
was_off = true
else
if was_off then
stbtt_setvertex(vertices[num_vertices + 1], STBTT_VTX.CURVE, x, y, cx, cy)
num_vertices = num_vertices + 1
else
stbtt_setvertex(vertices[num_vertices + 1], STBTT_VTX.LINE, x, y, 0, 0)
num_vertices = num_vertices + 1
end
was_off = false
end
end
end
num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx, sy, scx, scy, cx, cy)
elseif numberOfContours < 0 then
-- Compound shapes.
local more = 1
local comp = g + 10
num_vertices = 0
while more ~= 0 do
local flags, gidx
local comp_num_vertices = 0
local comp_verts
local mtx = {1, 0, 0, 1, 0, 0}
local m, n
flags = ttSHORT(data, comp) comp = comp + 2
gidx = ttSHORT(data, comp) comp = comp + 2
if bit32.band(flags, 2) ~= 0 then -- XY values
if bit32.band(flags, 1) ~= 0 then -- shorts
mtx[5] = ttSHORT(data, comp) comp = comp + 2
mtx[6] = ttSHORT(data, comp) comp = comp + 2
else
mtx[5] = ttCHAR(data, comp) comp = comp + 1
mtx[6] = ttCHAR(data, comp) comp = comp + 1
end
else
-- @TODO handle matching point
STBTT_assert(false)
end
if bit32.band(flags, bit32.lshift(1, 3)) ~= 0 then -- WE_HAVE_A_SCALE
mtx[4] = ttSHORT(data, comp) / 16384.0
mtx[1] = mtx[4]
comp = comp + 2
mtx[3] = 0
mtx[2] = mtx[3]
elseif bit32.band(flags, bit32.lshift(1, 6)) ~= 0 then -- WE_HAVE_AN_X_AND_YSCALE
mtx[1] = ttSHORT(data, comp) / 16384.0
comp = comp + 2
mtx[3] = 0
mtx[2] = mtx[3]
mtx[4] = ttSHORT(data, comp) / 16384.0
comp = comp + 2
elseif bit32.band(flags, bit32.lshift(1, 7)) ~= 0 then -- WE_HAVE_A_TWO_BY_TWO
mtx[1] = ttSHORT(data, comp) / 16384.0 comp = comp + 2
mtx[2] = ttSHORT(data, comp) / 16384.0 comp = comp + 2
mtx[3] = ttSHORT(data, comp) / 16384.0 comp = comp + 2
mtx[4] = ttSHORT(data, comp) / 16384.0 comp = comp + 2
end
-- Find transformation scales.
m = STBTT_sqrt(mtx[1] * mtx[1] + mtx[2] * mtx[2])
n = STBTT_sqrt(mtx[3] * mtx[3] + mtx[4] * mtx[4])
-- Get indexed glyph.
comp_num_vertices, comp_verts = stbtt_GetGlyphShape(info, gidx)
if comp_num_vertices > 0 then
-- Transform vertices.
for i = 1, comp_num_vertices do
local v = comp_verts[i]
local x = v.x
local y = v.y
v.x = stbtt_int16(m * (mtx[1] * x + mtx[3] * y + mtx[5]))
v.y = stbtt_int16(n * (mtx[2] * x + mtx[4] * y + mtx[6]))
x = v.cx
y = v.cy
v.cx = stbtt_int16(m * (mtx[1] * x + mtx[3] * y + mtx[5]))
v.cy = stbtt_int16(n * (mtx[2] * x + mtx[4] * y + mtx[6]))
end
-- Append vertices.
if num_vertices == 0 then -- LUA: No malloc
vertices = comp_verts
else
for i = 1, comp_num_vertices do
vertices[num_vertices + i] = comp_verts[i]
end
end
num_vertices = num_vertices + comp_num_vertices
end
-- More components ?
more = bit32.band(flags, bit32.lshift(1, 5))
end
end
return num_vertices, vertices
end
--- @param c stbtt__csctx
--- @param x stbtt_int32
--- @param y stbtt_int32
local function stbtt__track_vertex(c, x, y)
if x > c.max_x or not c.started then c.max_x = x end
if y > c.max_y or not c.started then c.max_y = y end
if x < c.min_x or not c.started then c.min_x = x end
if y < c.min_y or not c.started then c.min_y = y end
c.started = true
end
--- @param c stbtt__csctx
--- @param vtx_type STBTT_VTX
--- @param x stbtt_int32
--- @param y stbtt_int32
--- @param cx stbtt_int32
--- @param cy stbtt_int32
--- @param cx1 stbtt_int32
--- @param cy1 stbtt_int32
local function stbtt__csctx_v(c, vtx_type, x, y, cx, cy, cx1, cy1)
if c.bounds then
stbtt__track_vertex(c, x, y)
if vtx_type == STBTT_VTX.CUBIC then
stbtt__track_vertex(c, cx, cy)
stbtt__track_vertex(c, cx1, cy1)
end
else
stbtt_setvertex(c.pvertices[c.num_vertices + 1], vtx_type, x, y, cx, cy)
c.pvertices[c.num_vertices + 1].cx1 = stbtt_int16(cx1)
c.pvertices[c.num_vertices + 1].cy1 = stbtt_int16(cy1)
end
end
--- @param ctx stbtt__csctx
local function stbtt__csctx_close_shape(ctx)
if ctx.first_x ~= ctx.x or ctx.first_y ~= ctx.y then
stbtt__csctx_v(ctx, STBTT_VTX.LINE, trunc(ctx.first_x), trunc(ctx.first_y), 0, 0, 0, 0)
end
end
--- @param ctx stbtt__csctx
--- @param dx float
--- @param dy float
local function stbtt__csctx_rmove_to(ctx, dx, dy)
stbtt__csctx_close_shape(ctx)
ctx.x = ctx.x + dx
ctx.first_x = ctx.x
ctx.y = ctx.y + dy
ctx.first_y = ctx.y
stbtt__csctx_v(ctx, STBTT_VTX.MOVE, trunc(ctx.x), trunc(ctx.y), 0, 0, 0, 0)
end
--- @param ctx stbtt__csctx
--- @param dx float
--- @param dy float
local function stbtt__csctx_rline_to(ctx, dx, dy)
ctx.x = ctx.x + dx
ctx.y = ctx.y + dy
stbtt__csctx_v(ctx, STBTT_VTX.LINE, trunc(ctx.x), trunc(ctx.y), 0, 0, 0, 0)
end
--- @param ctx stbtt__csctx
--- @param dx1 float
--- @param dy1 float
--- @param dx2 float
--- @param dy2 float
--- @param dx3 float
--- @param dy3 float
local function stbtt__csctx_rccurve_to(ctx, dx1, dy1, dx2, dy2, dx3, dy3)
local cx1 = ctx.x + dx1
local cy1 = ctx.y + dy1
local cx2 = cx1 + dx2
local cy2 = cy1 + dy2
ctx.x = cx2 + dx3
ctx.y = cy2 + dy3
stbtt__csctx_v(ctx, STBTT_VTX.CUBIC, trunc(ctx.x), trunc(ctx.y), trunc(cx1), trunc(cy1), trunc(cx2), trunc(cy2))
end
local function stbtt__get_subr(idx, n)
local count = stbtt__cff_index_count(idx)
local bias = 107
if count >= 33900 then
bias = 32768
elseif count >= 1240 then
bias = 1131
end
n = n + bias
if n < 0 or n >= count then
return stbtt__new_buf(nil, 0)
end
return stbtt__cff_index_get(idx, n)
end
local function stbtt__cid_get_glyph_subrs(info, glyph_index)
local fdselect = info.fdselect
local fdselector = -1
stbtt__buf_seek(fdselect, 0)
local fmt = stbtt__buf_get8(fdselect)
if fmt == 0 then
-- untested
stbtt__buf_skip(fdselect, glyph_index)
fdselector = stbtt__buf_get8(fdselect)
elseif fmt == 3 then
local nranges = stbtt__buf_get16(fdselect)
local start = stbtt__buf_get16(fdselect)
for i = 0, nranges - 1 do
local v = stbtt__buf_get8(fdselect)
local _end = stbtt__buf_get16(fdselect)
if glyph_index >= start and glyph_index < _end then
fdselector = v
break
end
start = _end
end
end
if fdselector == -1 then return stbtt__new_buf(nil, 0) end -- [DEAR IMGUI] fixed, see #6007 and nothings/stb#1422
return stbtt__get_subrs(info.cff, stbtt__cff_index_get(info.fontdicts, fdselector))
end
--- @param info stbtt_fontinfo
--- @param glyph_index int
--- @param c stbtt__csctx
local function stbtt__run_charstring(info, glyph_index, c)
local in_header = true
local maskbits = 0
local subr_stack_height = 0
local sp = 0
local has_subrs = 0
local clear_stack
local s = {} -- size = 48, 0-based
local subr_stack = {} for i = 1, 10 do subr_stack[i] = stbtt__buf() end
local subrs = info.subrs
local b, f
local function STBTT__CSERR(_s) return 0 end
-- this currently ignores the initial width value, which isn't needed if we have hmtx
b = stbtt__cff_index_get(info.charstrings, glyph_index)
local i, v, b0
while b.cursor < b.size do
i = 0
clear_stack = 1
b0 = stbtt__buf_get8(b)
-- @TODO implement hinting
if b0 == 0x13 or b0 == 0x14 then -- hintmask or cntrmask
if in_header then
maskbits = maskbits + trunc(sp / 2) -- implicit "vstem"
end
in_header = false
stbtt__buf_skip(b, trunc((maskbits + 7) / 8))
elseif b0 == 0x01 or b0 == 0x03 or b0 == 0x12 or b0 == 0x17 then -- hstem, vstem, hstemhm, vstemhm
maskbits = maskbits + trunc(sp / 2)
elseif b0 == 0x15 then -- rmoveto
in_header = false
if sp < 2 then return STBTT__CSERR("rmoveto stack") end
stbtt__csctx_rmove_to(c, s[sp - 2], s[sp - 1])
elseif b0 == 0x04 then -- vmoveto
in_header = false
if sp < 1 then return STBTT__CSERR("vmoveto stack") end
stbtt__csctx_rmove_to(c, 0, s[sp - 1])
elseif b0 == 0x16 then -- hmoveto
in_header = false
if sp < 1 then return STBTT__CSERR("hmoveto stack") end
stbtt__csctx_rmove_to(c, s[sp - 1], 0)
elseif b0 == 0x05 then -- rlineto
if sp < 2 then return STBTT__CSERR("rlineto stack") end
while i + 1 < sp do
stbtt__csctx_rline_to(c, s[i], s[i + 1])
i = i + 2
end
-- hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical
-- starting from a different place.
elseif b0 == 0x07 then -- vlineto
if sp < 1 then return STBTT__CSERR("vlineto stack") end
while true do
if i >= sp then break end
stbtt__csctx_rline_to(c, 0, s[i])
i = i + 1
if i >= sp then break end
stbtt__csctx_rline_to(c, s[i], 0)
i = i + 1
end
elseif b0 == 0x06 then -- hlineto
if sp < 1 then return STBTT__CSERR("hlineto stack") end
while true do
if i >= sp then break end
stbtt__csctx_rline_to(c, s[i], 0)
i = i + 1
-- vlineto
if i >= sp then break end
stbtt__csctx_rline_to(c, 0, s[i])
i = i + 1
end
elseif b0 == 0x1F then -- hvcurveto
if sp < 4 then return STBTT__CSERR("hvcurveto stack") end
while true do
if i + 3 >= sp then break end
stbtt__csctx_rccurve_to(c, s[i], 0, s[i + 1], s[i + 2], ((sp - i) == 5) and s[i + 4] or 0.0, s[i + 3])
i = i + 4
if i + 3 >= sp then break end
stbtt__csctx_rccurve_to(c, 0, s[i], s[i + 1], s[i + 2], s[i + 3], ((sp - i) == 5) and s[i + 4] or 0.0)
i = i + 4
end
elseif b0 == 0x1E then -- vhcurveto
if sp < 4 then return STBTT__CSERR("vhcurveto stack") end
while true do
if i + 3 >= sp then break end
stbtt__csctx_rccurve_to(c, 0, s[i], s[i + 1], s[i + 2], s[i + 3], ((sp - i) == 5) and s[i + 4] or 0.0)
i = i + 4
-- hvcurveto
if i + 3 >= sp then break end
stbtt__csctx_rccurve_to(c, s[i], 0, s[i + 1], s[i + 2], ((sp - i) == 5) and s[i + 4] or 0.0, s[i + 3])
i = i + 4
end
elseif b0 == 0x08 then -- rrcurveto
if sp < 6 then return STBTT__CSERR("rcurveline stack") end
while i + 5 < sp do
stbtt__csctx_rccurve_to(c, s[i], s[i + 1], s[i + 2], s[i + 3], s[i + 4], s[i + 5])
i = i + 6
end
elseif b0 == 0x18 then -- rcurveline
if sp < 8 then return STBTT__CSERR("rcurveline stack") end
while i + 5 < sp - 2 do
stbtt__csctx_rccurve_to(c, s[i], s[i + 1], s[i + 2], s[i + 3], s[i + 4], s[i + 5])
i = i + 6
end
if i + 1 >= sp then return STBTT__CSERR("rcurveline stack") end
stbtt__csctx_rline_to(c, s[i], s[i + 1])
elseif b0 == 0x19 then -- rlinecurve
if sp < 8 then return STBTT__CSERR("rlinecurve stack") end
while i + 1 < sp - 6 do
stbtt__csctx_rline_to(c, s[i], s[i + 1])
i = i + 2
end
if i + 5 >= sp then return STBTT__CSERR("rlinecurve stack") end
stbtt__csctx_rccurve_to(c, s[i], s[i + 1], s[i + 2], s[i + 3], s[i + 4], s[i + 5])
elseif b0 == 0x1A or b0 == 0x1B then -- vvcurveto or hhcurveto
if sp < 4 then return STBTT__CSERR("(vv|hh)curveto stack") end
f = 0.0
if bit32.band(sp, 1) ~= 0 then
f = s[i]
i = i + 1
end
while i + 3 < sp do
if b0 == 0x1B then
stbtt__csctx_rccurve_to(c, s[i], f, s[i + 1], s[i + 2], s[i + 3], 0.0)
else
stbtt__csctx_rccurve_to(c, f, s[i], s[i + 1], s[i + 2], 0.0, s[i + 3])
end
f = 0.0
i = i + 4
end
elseif b0 == 0x0A -- callsubr
or b0 == 0x1D then
if b0 == 0x0A and has_subrs == 0 then
if info.fdselect.size ~= 0 then
subrs = stbtt__cid_get_glyph_subrs(info, glyph_index)
end
has_subrs = 1
end
-- callgsubr
if sp < 1 then return STBTT__CSERR("call(g|)subr stack") end
sp = sp - 1
v = trunc(s[sp])
if subr_stack_height >= 10 then return STBTT__CSERR("recursion limit") end
subr_stack[subr_stack_height + 1] = b
subr_stack_height = subr_stack_height + 1
b = stbtt__get_subr((b0 == 0x0A) and subrs or info.gsubrs, v)
if b.size == 0 then return STBTT__CSERR("subr not found") end
b.cursor = 0
clear_stack = 0
elseif b0 == 0x0B then -- return
if subr_stack_height <= 0 then return STBTT__CSERR("return outside subr") end
subr_stack_height = subr_stack_height - 1
b = subr_stack[subr_stack_height + 1]
clear_stack = 0
elseif b0 == 0x0E then -- endchar
stbtt__csctx_close_shape(c)
return 1
elseif b0 == 0x0C then -- two-byte escape
local b1 = stbtt__buf_get8(b)
-- @TODO These "flex" implementations ignore the flex-depth and resolution,
-- and always draw beziers.
if b1 == 0x22 then -- hflex
if sp < 7 then return STBTT__CSERR("hflex stack") end
local dx1 = s[0]
local dx2 = s[1]
local dy2 = s[2]
local dx3 = s[3]
local dx4 = s[4]
local dx5 = s[5]
local dx6 = s[6]
stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0)
stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0)
elseif b1 == 0x23 then -- flex
if sp < 13 then return STBTT__CSERR("flex stack") end
local dx1 = s[0]
local dy1 = s[1]
local dx2 = s[2]
local dy2 = s[3]
local dx3 = s[4]
local dy3 = s[5]
local dx4 = s[6]
local dy4 = s[7]
local dx5 = s[8]
local dy5 = s[9]
local dx6 = s[10]
local dy6 = s[11]
-- fd is s[12]
stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3)
stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6)
elseif b1 == 0x24 then -- hflex1
if sp < 9 then return STBTT__CSERR("hflex1 stack") end
local dx1 = s[0]
local dy1 = s[1]
local dx2 = s[2]
local dy2 = s[3]
local dx3 = s[4]
local dx4 = s[5]
local dx5 = s[6]
local dy5 = s[7]
local dx6 = s[8]
stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0)
stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1 + dy2 + dy5))
elseif b1 == 0x25 then -- flex1
if sp < 11 then return STBTT__CSERR("flex1 stack") end
local dx1 = s[0]
local dy1 = s[1]
local dx2 = s[2]
local dy2 = s[3]
local dx3 = s[4]
local dy3 = s[5]
local dx4 = s[6]
local dy4 = s[7]
local dx5 = s[8]
local dy5 = s[9]
local dx6 = s[10]
local dy6 = s[10]
local dx = dx1 + dx2 + dx3 + dx4 + dx5
local dy = dy1 + dy2 + dy3 + dy4 + dy5
if STBTT_fabs(dx) > STBTT_fabs(dy) then
dy6 = -dy
else
dx6 = -dx
end
stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3)
stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6)
else
return STBTT__CSERR("unimplemented")
end
else
if b0 ~= 255 and b0 ~= 28 and b0 < 32 then
return STBTT__CSERR("reserved operator")
end
-- push immediate
if b0 == 255 then
f = stbtt__buf_get32(b) / 0x10000
else
stbtt__buf_skip(b, -1)
f = stbtt_int16(stbtt__cff_int(b))
end
if sp >= 48 then return STBTT__CSERR("push stack overflow") end
s[sp] = f
sp = sp + 1
clear_stack = 0
end
if clear_stack ~= 0 then
sp = 0
end
end
return STBTT__CSERR("no endchar")
end
--- @param info stbtt_fontinfo
--- @param glyph_index int
--- @return int num_vertices
--- @return stbtt_vertex[]? vertices
function stbtt__GetGlyphShapeT2(info, glyph_index)
-- runs the charstring twice, once to count and once to output (to avoid realloc)
local vertices = nil
local count_ctx = STBTT__CSCTX_INIT(true)
local output_ctx = STBTT__CSCTX_INIT(false)
if stbtt__run_charstring(info, glyph_index, count_ctx) ~= 0 then
vertices = {} for i = 1, count_ctx.num_vertices do vertices[i] = stbtt_vertex() end
output_ctx.pvertices = vertices
if stbtt__run_charstring(info, glyph_index, output_ctx) ~= 0 then
STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices)
return output_ctx.num_vertices, vertices
end
end
return 0, vertices
end
--- @param info stbtt_fontinfo
--- @param glyph_index int
--- @return int
--- @return int x0
--- @return int y0
--- @return int x1
--- @return int y1
function stbtt__GetGlyphInfoT2(info, glyph_index)
local c = STBTT__CSCTX_INIT(true)
local r = stbtt__run_charstring(info, glyph_index, c)
local x0 = (r ~= 0) and c.min_x or 0
local y0 = (r ~= 0) and c.min_y or 0
local x1 = (r ~= 0) and c.max_x or 0
local y1 = (r ~= 0) and c.max_y or 0
return ((r ~= 0) and c.num_vertices or 0), x0, y0, x1, y1
end
-- returns # of vertices and fills *vertices with the pointer to them
-- these are expressed in "unscaled" coordinates
--
-- The shape is a series of contours. Each one starts with
-- a STBTT_moveto, then consists of a series of mixed
-- STBTT_lineto and STBTT_curveto segments. A lineto
-- draws a line from previous endpoint to its x,y; a curveto
-- draws a quadratic bezier from previous endpoint to
-- its x,y, using cx,cy as the bezier control point.
--- @param info stbtt_fontinfo
--- @param glyph_index int
function stbtt_GetGlyphShape(info, glyph_index)
if info.cff.size == 0 then
return stbtt__GetGlyphShapeTT(info, glyph_index)
else
return stbtt__GetGlyphShapeT2(info, glyph_index)
end
end
-- leftSideBearing is the offset from the current horizontal position to the left edge of the character
-- advanceWidth is the offset from the current horizontal position to the next horizontal position
-- these are expressed in unscaled coordinates
--- @param info stbtt_fontinfo
--- @param glyph_index int
function stbtt_GetGlyphHMetrics(info, glyph_index)
local numOfLongHorMetrics = ttUSHORT(info.data, info.hhea + 34)
local advanceWidth, leftSideBearing
if glyph_index < numOfLongHorMetrics then
advanceWidth = ttSHORT(info.data, info.hmtx + 4 * glyph_index)
leftSideBearing = ttSHORT(info.data, info.hmtx + 4 * glyph_index + 2)
else
advanceWidth = ttSHORT(info.data, info.hmtx + 4 * (numOfLongHorMetrics - 1))
leftSideBearing = ttSHORT(info.data, info.hmtx + 4 * numOfLongHorMetrics + 2 * (glyph_index - numOfLongHorMetrics))
end
return advanceWidth, leftSideBearing
end
-- ascent is the coordinate above the baseline the font extends; descent
-- is the coordinate below the baseline the font extends (i.e. it is typically negative)
-- lineGap is the spacing between one row's descent and the next row's ascent...
-- so you should advance the vertical position by "ascent - descent + lineGap"
-- these are expressed in unscaled coordinates, so you must multiply by
-- the scale factor for a given size
--- @param info stbtt_fontinfo
function stbtt_GetFontVMetrics(info)
local ascent = ttSHORT(info.data, info.hhea + 4)
local descent = ttSHORT(info.data, info.hhea + 6)
local lineGap = ttSHORT(info.data, info.hhea + 8)
return ascent, descent, lineGap
end
-- computes a scale factor to produce a font whose "height" is 'pixels' tall.
-- Height is measured as the distance from the highest ascender to the lowest
-- descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics
-- and computing:
-- scale = pixels / (ascent - descent)
-- so if you prefer to measure height by the ascent only, use a similar calculation.
--- @param info stbtt_fontinfo
--- @param height float
function stbtt_ScaleForPixelHeight(info, height)
local fheight = ttSHORT(info.data, info.hhea + 4) - ttSHORT(info.data, info.hhea + 6)
return height / fheight
end
------------------------------------
------------------------------------
---
--- antialiasing software rasterizer
---
---
-- same as stbtt_GetGlyphBitmapBox, but you can specify a subpixel
-- shift for the character
--- @param font stbtt_fontinfo
--- @param glyph int
--- @param scale_x float
--- @param scale_y float
--- @param shift_x float
--- @param shift_y float
function stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y, shift_x, shift_y)
local n, x0, y0, x1, y1 = stbtt_GetGlyphBox(font, glyph)
local ix0, iy0, ix1, iy1
if n == 0 then
-- e.g. space character
ix0 = 0
iy0 = 0
ix1 = 0
iy1 = 0
else
-- move to integral bboxes (treating pixels as little squares, what pixels get touched)?
ix0 = STBTT_ifloor( x0 * scale_x + shift_x)
iy0 = STBTT_ifloor(-y1 * scale_y + shift_y)
ix1 = STBTT_iceil( x1 * scale_x + shift_x)
iy1 = STBTT_iceil(-y0 * scale_y + shift_y)
end
return ix0, iy0, ix1, iy1
end
-- get the bbox of the bitmap centered around the glyph origin; so the
-- bitmap width is ix1-ix0, height is iy1-iy0, and location to place
-- the bitmap top left is (leftSideBearing*scale,iy0).
-- (Note that the bitmap uses y-increases-down, but the shape uses
-- y-increases-up, so GlyphBitmapBox and GlyphBox are inverted.)
--- @param font stbtt_fontinfo
--- @param glyph int
--- @param scale_x float
--- @param scale_y float
function stbtt_GetGlyphBitmapBox(font, glyph, scale_x, scale_y)
return stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y, 0.0, 0.0)
end
--------------
--------------
---
--- Rasterizer
---
---
--- UNIMPLEMENTED / UNUSED:
--- stbtt_MakeCodepointBitmap
--- stbtt_MakeCodepointBitmapSubpixelPrefilter
--- stbtt_GetGlyphBitmap
--- stbtt_MakeGlyphBitmap
--- stbtt_GetGlyphBitmapSubpixel
-- the edge passed in here does not cross the vertical line at x or the vertical line at x+1
-- (i.e. it has already been clipped to those)
--- @param scanline float[]
--- @param x int
--- @param e stbtt__active_edge
--- @param x0 float
--- @param y0 float
--- @param x1 float
--- @param y1 float
local function stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x1, y1)
if y0 == y1 then return end
STBTT_assert(y0 < y1)
STBTT_assert(e.sy <= e.ey)
if y0 > e.ey then return end
if y1 < e.sy then return end
if y0 < e.sy then
x0 = x0 + (x1 - x0) * (e.sy - y0) / (y1 - y0)
y0 = e.sy
end
if y1 > e.ey then
x1 = x1 + (x1 - x0) * (e.ey - y1) / (y1 - y0)
y1 = e.ey
end
if x0 == x then
STBTT_assert(x1 <= x + 1)
elseif x0 == x + 1 then
STBTT_assert(x1 >= x)
elseif x0 <= x then
STBTT_assert(x1 <= x)
elseif x0 >= x + 1 then
STBTT_assert(x1 >= x + 1)
else
STBTT_assert(x1 >= x and x1 <= x + 1)
end
if x0 <= x and x1 <= x then
scanline[x] = scanline[x] + e.direction * (y1 - y0)
elseif x0 >= x + 1 and x1 >= x + 1 then
-- do nothing
else
STBTT_assert(x0 >= x and x0 <= x + 1 and x1 >= x and x1 <= x + 1)
scanline[x] = scanline[x] + e.direction * (y1 - y0) * (1 - ((x0 - x) + (x1 - x)) / 2) -- coverage = 1 - average x position
end
end
--- @param height float
--- @param top_width float
--- @param bottom_width float
local function stbtt__sized_trapezoid_area(height, top_width, bottom_width)
STBTT_assert(top_width >= 0)
STBTT_assert(bottom_width >= 0)
return (top_width + bottom_width) / 2.0 * height
end
--- @param height float
--- @param tx0 float
--- @param tx1 float
--- @param bx0 float
--- @param bx1 float
local function stbtt__position_trapezoid_area(height, tx0, tx1, bx0, bx1)
return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0)
end
--- @param height float
--- @param width float
local function stbtt__sized_triangle_area(height, width)
return height * width / 2
end
--- @param scanline float[]
--- @param scanline_fill float[]
--- @param len int
--- @param e stbtt__active_edge
--- @param y_top float
local function stbtt__fill_active_edges_new(scanline, scanline_fill, len, e, y_top)
local y_bottom = y_top + 1
while e do
-- brute force every pixel
-- compute intersection points with top & bottom
STBTT_assert(e.ey >= y_top)
if e.fdx == 0 then
local x0 = e.fx
if x0 < len then
if x0 >= 0 then
stbtt__handle_clipped_edge(scanline, trunc(x0), e, x0, y_top, x0, y_bottom)
stbtt__handle_clipped_edge(scanline_fill, trunc(x0) + 1, e, x0, y_top, x0, y_bottom)
else
stbtt__handle_clipped_edge(scanline_fill, 0, e, x0, y_top, x0, y_bottom)
end
end
else
local x0 = e.fx
local dx = e.fdx
local xb = x0 + dx
local x_top, x_bottom
local sy0, sy1
local dy = e.fdy
STBTT_assert(e.sy <= y_bottom and e.ey >= y_top)
-- compute endpoints of line segment clipped to this scanline (if the
-- line segment starts on this scanline. x0 is the intersection of the
-- line with y_top, but that may be off the line segment.
if e.sy > y_top then
x_top = x0 + dx * (e.sy - y_top)
sy0 = e.sy
else
x_top = x0
sy0 = y_top
end
if e.ey < y_bottom then
x_bottom = x0 + dx * (e.ey - y_top)
sy1 = e.ey
else
x_bottom = xb
sy1 = y_bottom
end
if x_top >= 0 and x_bottom >= 0 and x_top < len and x_bottom < len then
-- from here on, we don't have to range check x values
if trunc(x_top) == trunc(x_bottom) then
local height
-- simple case, only spans one pixel
local x = trunc(x_top)
height = (sy1 - sy0) * e.direction
STBTT_assert(x >= 0 and x < len)
scanline[x] = scanline[x] + stbtt__position_trapezoid_area(height, x_top, x + 1.0, x_bottom, x + 1.0)
scanline_fill[x + 1] = scanline_fill[x + 1] + height -- everything right of this pixel is filled
else
local x1, x2
local y_crossing, y_final, step, sign, area
-- covers 2+ pixels
if x_top > x_bottom then
-- flip scanline vertically; signed area is the same
sy0 = y_bottom - (sy0 - y_top)
sy1 = y_bottom - (sy1 - y_top)
local t
t = sy0; sy0 = sy1; sy1 = t
t = x_bottom; x_bottom = x_top; x_top = t
dx = -dx
dy = -dy
t = x0; x0 = xb; xb = t
end
STBTT_assert(dy >= 0)
STBTT_assert(dx >= 0)
x1 = trunc(x_top)
x2 = trunc(x_bottom)
-- compute intersection with y axis at x1+1
y_crossing = y_top + dy * (x1 + 1 - x0)
-- compute intersection with y axis at x2
y_final = y_top + dy * (x2 - x0)
-- x1 x_top x2 x_bottom
-- y_top +------|-----+------------+------------+--------|---+------------+
-- | | | | | |
-- | | | | | |
-- sy0 | Txxxxx|............|............|............|............|
-- y_crossing | *xxxxx.......|............|............|............|
-- | | xxxxx..|............|............|............|
-- | | /- xx*xxxx........|............|............|
-- | | dy < | xxxxxx..|............|............|
-- y_final | | \- | xx*xxx.........|............|
-- sy1 | | | | xxxxxB...|............|
-- | | | | | |
-- | | | | | |
-- y_bottom +------------+------------+------------+------------+------------+
--
-- goal is to measure the area covered by '.' in each pixel
-- if x2 is right at the right edge of x1, y_crossing can blow up, github #1057
-- @TODO: maybe test against sy1 rather than y_bottom?
if y_crossing > y_bottom then
y_crossing = y_bottom
end
sign = e.direction
-- area of the rectangle covered from sy0..y_crossing
area = sign * (y_crossing - sy0)
-- area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing)
scanline[x1] = scanline[x1] + stbtt__sized_triangle_area(area, x1 + 1 - x_top)
-- check if final y_crossing is blown up; no test case for this
if y_final > y_bottom then
local denom = (x2 - (x1 + 1))
y_final = y_bottom
if denom ~= 0 then -- [DEAR IMGUI] Avoid div by zero (https://github.com/nothings/stb/issues/1316)
dy = (y_final - y_crossing) / denom -- if denom=0, y_final = y_crossing, so y_final <= y_bottom
end
end
-- in second pixel, area covered by line segment found in first pixel
-- is always a rectangle 1 wide * the height of that line segment; this
-- is exactly what the variable 'area' stores. it also gets a contribution
-- from the line segment within it. the THIRD pixel will get the first
-- pixel's rectangle contribution, the second pixel's rectangle contribution,
-- and its own contribution. the 'own contribution' is the same in every pixel except
-- the leftmost and rightmost, a trapezoid that slides down in each pixel.
-- the second pixel's contribution to the third pixel will be the
-- rectangle 1 wide times the height change in the second pixel, which is dy.
step = sign * dy * 1 -- dy is dy/dx, change in y for every 1 change in x,
-- which multiplied by 1-pixel-width is how much pixel area changes for each step in x
-- so the area advances by 'step' every time
for x = x1 + 1, x2 - 1 do
scanline[x] = scanline[x] + area + step / 2 -- area of trapezoid is 1*step/2
area = area + step
end
STBTT_assert(STBTT_fabs(area) <= 1.01) -- accumulated error from area += step unless we round step down
STBTT_assert(sy1 > y_final - 0.01)
-- area covered in the last pixel is the rectangle from all the pixels to the left,
-- plus the trapezoid filled by the line segment in this pixel all the way to the right edge
scanline[x2] = scanline[x2] + area + sign * stbtt__position_trapezoid_area(sy1 - y_final, x2, x2 + 1.0, x_bottom, x2 + 1.0)
-- the rest of the line is filled based on the total height of the line segment in this pixel
scanline_fill[x2 + 1] = scanline_fill[x2 + 1] + sign * (sy1 - sy0)
end
else
-- if edge goes outside of box we're drawing, we require
-- clipping logic. since this does not match the intended use
-- of this library, we use a different, very slow brute
-- force implementation
-- note though that this does happen some of the time because
-- x_top and x_bottom can be extrapolated at the top & bottom of
-- the shape and actually lie outside the bounding box
for x = 0, len - 1 do
-- cases:
--
-- there can be up to two intersections with the pixel. any intersection
-- with left or right edges can be handled by splitting into two (or three)
-- regions. intersections with top & bottom do not necessitate case-wise logic.
--
-- the old way of doing this found the intersections with the left & right edges,
-- then used some simple logic to produce up to three segments in sorted order
-- from top-to-bottom. however, this had a problem: if an x edge was epsilon
-- across the x border, then the corresponding y position might not be distinct
-- from the other y segment, and it might ignored as an empty segment. to avoid
-- that, we need to explicitly produce segments based on x positions.
-- rename variables to clearly-defined pairs
local y0 = y_top
local x1 = x
local x2 = x + 1
local x3 = xb
local y3 = y_bottom
-- x = e->x + e->dx * (y-y_top)
-- (y-y_top) = (x - e->x) / e->dx
-- y = (x - e->x) / e->dx + y_top
local y1 = (x - x0) / dx + y_top
local y2 = (x + 1 - x0) / dx + y_top
if x0 < x1 and x3 > x2 then -- three segments descending down-right
stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x1, y1)
stbtt__handle_clipped_edge(scanline, x, e, x1, y1, x2, y2)
stbtt__handle_clipped_edge(scanline, x, e, x2, y2, x3, y3)
elseif x3 < x1 and x0 > x2 then -- three segments descending down-left
stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x2, y2)
stbtt__handle_clipped_edge(scanline, x, e, x2, y2, x1, y1)
stbtt__handle_clipped_edge(scanline, x, e, x1, y1, x3, y3)
elseif x0 < x1 and x3 > x1 then -- two segments across x, down-right
stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x1, y1)
stbtt__handle_clipped_edge(scanline, x, e, x1, y1, x3, y3)
elseif x3 < x1 and x0 > x1 then -- two segments across x, down-left
stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x1, y1)
stbtt__handle_clipped_edge(scanline, x, e, x1, y1, x3, y3)
elseif x0 < x2 and x3 > x2 then -- two segments across x+1, down-right
stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x2, y2)
stbtt__handle_clipped_edge(scanline, x, e, x2, y2, x3, y3)
elseif x3 < x2 and x0 > x2 then -- two segments across x+1, down-left
stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x2, y2)
stbtt__handle_clipped_edge(scanline, x, e, x2, y2, x3, y3)
else -- one segment
stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x3, y3)
end
end
end
end
e = e.next
end
end
-- directly AA rasterize edges w/o supersampling
--- @param result stbtt__bitmap
--- @param e stbtt__edge
--- @param n int
--- @param vsubsample int
--- @param off_x int
--- @param off_y int
local function stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y)
local active = nil
local scanline = {} -- size = result.w, 0-based
local scanline2 = {} -- size = result.w + 1, 0-based
STBTT__NOTUSED(vsubsample)
local y = off_y
e[n + 1].y0 = off_y + result.h + 1
local j = 0
local p = 1
while j < result.h do
-- find center of pixel for this scanline
local scan_y_top = y + 0.0
local scan_y_bottom = y + 1.0
for i = 0, result.w - 1 do scanline[i] = 0 end
for i = 0, result.w do scanline2[i] = 0 end
-- update all active edges;
-- remove all active edges that terminate before the top of this scanline
local prev = nil
local curr = active
while curr do
local next = curr.next
if curr.ey <= scan_y_top then
if prev then prev.next = next -- delete from list
else active = next end
curr.direction = 0
else
prev = curr -- advance through list
end
curr = next
end
-- insert all edges that start before the bottom of this scanline
local edge = e[p]
while edge.y0 <= scan_y_bottom do
if edge.y0 ~= edge.y1 then
local z = stbtt__new_active(edge, off_x, scan_y_top)
if j == 0 and off_y ~= 0 and z.ey < scan_y_top then
-- this can happen due to subpixel positioning and some kind of fp rounding error i think
z.ey = scan_y_top
end
STBTT_assert(z.ey >= scan_y_top) -- if we get really unlucky a tiny bit of an edge can be out of bounds
-- insert at front
z.next = active
active = z
end
p = p + 1
edge = e[p]
end
-- now process all active edges
if active then
stbtt__fill_active_edges_new(scanline, scanline2, result.w, active, scan_y_top)
end
do
local sum = 0
for i = 0, result.w - 1 do
local k
local m
sum = sum + scanline2[i]
k = scanline[i] + sum
k = STBTT_fabs(k) * 255 + 0.5
m = trunc(k)
if m > 255 then m = 255 end
result.pixels[j * result.stride + i + 1] = unsigned_char(m)
end
end
-- advance all the edges
step = active
while step do
step.fx = step.fx + step.fdx -- advance to position for current scanline
step = step.next -- advance through list
end
y = y + 1
j = j + 1
end
end
--- @param a stbtt__edge
--- @param b stbtt__edge
local STBTT__COMPARE = function(a, b)
if a.y0 == nil then return false end -- sentinels on the back
if b.y0 == nil then return true end
return a.y0 < b.y0
end
--- @param p stbtt__edge[]
local function stbtt__sort_edges(p)
STBTT_sort(p, STBTT__COMPARE)
end
--- @param result stbtt__bitmap
--- @param pts stbtt__point[]
--- @param wcount int[]
--- @param windings int
--- @param scale_x float
--- @param scale_y float
--- @param shift_x float
--- @param shift_y float
--- @param off_x int
--- @param off_y int
--- @param invert bool
local function stbtt__rasterize(result, pts, wcount, windings, scale_x, scale_y, shift_x, shift_y, off_x, off_y, invert)
local y_scale_inv = (invert) and -scale_y or scale_y
local vsubsample = 1 -- STBTT_RASTERIZER_VERSION == 2
-- vsubsample should divide 255 evenly; otherwise we won't reach full opacity
-- now we have to blow out the windings into explicit edge lists
local n = 0
for i = 1, windings do
n = n + wcount[i]
end
local e = {} for i = 1, n + 1 do e[i] = stbtt__edge() end -- add an extra one as a sentinel
n = 0
local m = 0
local j
for i = 1, windings do
local p = m
m = m + wcount[i]
j = wcount[i]
for k = 1, wcount[i] do
repeat
local a = k
local b = j
-- skip the edge if horizontal
if pts[p + j].y == pts[p + k].y then
break
end
-- add edge from j to k to the list
n = n + 1
e[n].invert = false
if invert then
if pts[p + j].y > pts[p + k].y then
e[n].invert = true
a = j
b = k
end
else
if pts[p + j].y < pts[p + k].y then
e[n].invert = true
a = j
b = k
end
end
e[n].x0 = pts[p + a].x * scale_x + shift_x
e[n].y0 = (pts[p + a].y * y_scale_inv + shift_y) * vsubsample
e[n].x1 = pts[p + b].x * scale_x + shift_x
e[n].y1 = (pts[p + b].y * y_scale_inv + shift_y) * vsubsample
until true
j = k
end
end
-- now sort the edges by their highest point (should snap to integer, and then by x)
stbtt__sort_edges(e)
-- now, traverse the scanlines and find the intersections on each scanline, use xor winding rule
stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y)
end
--- @param points? stbtt__point[]
--- @param n int
--- @param x float
--- @param y float
local function stbtt__add_point(points, n, x, y)
if points == nil then return end -- during first pass, it's unallocated
points[n + 1].x = x
points[n + 1].y = y
end
-- tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching
--- @param points? stbtt__point[]
--- @param num_points int
--- @param x0 float
--- @param y0 float
--- @param x1 float
--- @param y1 float
--- @param x2 float
--- @param y2 float
--- @param objspace_flatness_squared float
--- @param n int
--- @return int num_points # updated num_points passed in
local function stbtt__tesselate_curve(points, num_points, x0, y0, x1, y1, x2, y2, objspace_flatness_squared, n)
-- midpoint
local mx = (x0 + 2 * x1 + x2) / 4
local my = (y0 + 2 * y1 + y2) / 4
-- versus directly drawn line
local dx = (x0 + x2) / 2 - mx
local dy = (y0 + y2) / 2 - my
if n > 16 then -- 65536 segments on one curve better be enough!
return num_points
end
if dx * dx + dy * dy > objspace_flatness_squared then -- half-pixel error allowed... need to be smaller if AA
num_points = stbtt__tesselate_curve(points, num_points, x0, y0, (x0 + x1) / 2.0, (y0 + y1) / 2.0, mx, my, objspace_flatness_squared, n + 1)
num_points = stbtt__tesselate_curve(points, num_points, mx, my, (x1 + x2) / 2.0, (y1 + y2) / 2.0, x2, y2, objspace_flatness_squared, n + 1)
else
stbtt__add_point(points, num_points, x2, y2)
num_points = num_points + 1
end
return num_points
end
--- @param points? stbtt__point[]
--- @param num_points int
--- @param x0 float
--- @param y0 float
--- @param x1 float
--- @param y1 float
--- @param x2 float
--- @param y2 float
--- @param x3 float
--- @param y3 float
--- @param objspace_flatness_squared float
--- @param n int
--- @return int num_points # updated num_points passed in
local function stbtt__tesselate_cubic(points, num_points, x0, y0, x1, y1, x2, y2, x3, y3, objspace_flatness_squared, n)
-- @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough
local dx0 = x1 - x0
local dy0 = y1 - y0
local dx1 = x2 - x1
local dy1 = y2 - y1
local dx2 = x3 - x2
local dy2 = y3 - y2
local dx = x3 - x0
local dy = y3 - y0
local longlen = STBTT_sqrt(dx0 * dx0 + dy0 * dy0) + STBTT_sqrt(dx1 * dx1 + dy1 * dy1) + STBTT_sqrt(dx2 * dx2 + dy2 * dy2)
local shortlen = STBTT_sqrt(dx * dx + dy * dy)
local flatness_squared = longlen * longlen - shortlen * shortlen
if n > 16 then -- 65536 segments on one curve better be enough!
return num_points
end
if flatness_squared > objspace_flatness_squared then
local x01 = (x0 + x1) / 2
local y01 = (y0 + y1) / 2
local x12 = (x1 + x2) / 2
local y12 = (y1 + y2) / 2
local x23 = (x2 + x3) / 2
local y23 = (y2 + y3) / 2
local xa = (x01 + x12) / 2
local ya = (y01 + y12) / 2
local xb = (x12 + x23) / 2
local yb = (y12 + y23) / 2
local mx = (xa + xb) / 2
local my = (ya + yb) / 2
num_points = stbtt__tesselate_cubic(points, num_points, x0, y0, x01, y01, xa, ya, mx, my, objspace_flatness_squared, n + 1)
num_points = stbtt__tesselate_cubic(points, num_points, mx, my, xb, yb, x23, y23, x3, y3, objspace_flatness_squared, n + 1)
else
stbtt__add_point(points, num_points, x3, y3)
num_points = num_points + 1
end
return num_points
end
-- returns number of contours
--- @param vertices stbtt_vertex[]
--- @param num_verts int
--- @param objspace_flatness float
--- @return stbtt__point[]? points
--- @return int? num_contours
--- @return int[]? contour_lengths
local function stbtt_FlattenCurves(vertices, num_verts, objspace_flatness)
local points = nil
local num_points = 0
local objspace_flatness_squared = objspace_flatness * objspace_flatness
local n = 0
local start = 0
-- count how many "moves" there are to get the contour count
for i = 1, num_verts do
if vertices[i].type == STBTT_VTX.MOVE then
n = n + 1
end
end
local num_contours = n
if n == 0 then return nil end
local contour_lengths = {} -- size = n
-- make two passes through the points so we don't need to realloc
for pass = 0, 1 do
local x, y = 0, 0
if pass == 1 then
points = {}
for i = 1, num_points do points[i] = stbtt__point() end
end
num_points = 0
n = -1
for i = 1, num_verts do
if vertices[i].type == STBTT_VTX.MOVE then
-- start the next contour
if n >= 0 then
contour_lengths[n + 1] = num_points - start
end
n = n + 1
start = num_points
x = vertices[i].x
y = vertices[i].y
stbtt__add_point(points, num_points, x, y)
num_points = num_points + 1
elseif vertices[i].type == STBTT_VTX.LINE then
x = vertices[i].x
y = vertices[i].y
stbtt__add_point(points, num_points, x, y)
num_points = num_points + 1
elseif vertices[i].type == STBTT_VTX.CURVE then
num_points = stbtt__tesselate_curve(points, num_points, x, y,
vertices[i].cx, vertices[i].cy,
vertices[i].x, vertices[i].y,
objspace_flatness_squared, 0)
x = vertices[i].x
y = vertices[i].y
elseif vertices[i].type == STBTT_VTX.CUBIC then
num_points = stbtt__tesselate_cubic(points, num_points, x, y,
vertices[i].cx, vertices[i].cy,
vertices[i].cx1, vertices[i].cy1,
vertices[i].x, vertices[i].y,
objspace_flatness_squared, 0)
x = vertices[i].x
y = vertices[i].y
end
end
contour_lengths[n + 1] = num_points - start
end
return points, num_contours, contour_lengths
end
-- rasterize a shape with quadratic beziers into a bitmap
--- @param result stbtt__bitmap
--- @param flatness_in_pixels float
--- @param vertices stbtt_vertex[]
--- @param num_verts int
--- @param scale_x float
--- @param scale_y float
--- @param shift_x float
--- @param shift_y float
--- @param x_off int
--- @param y_off int
--- @param invert bool
local function stbtt_Rasterize(result, flatness_in_pixels, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert)
local scale = (scale_x > scale_y) and scale_y or scale_x
local windings, winding_count, winding_lengths = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale)
if windings then
--- @cast winding_lengths int[]
--- @cast winding_count int
stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert)
end
end
-- renders the glyph into storage you pass in for the bitmap,
-- in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap
-- is clipped to out_w/out_h bytes. Call stbtt_GetGlyphBitmapBox to get the
-- width and height and positioning info for it first. You can also specify
-- a subpixel shift for the character.
--- @param info stbtt_fontinfo
--- @param output unsigned_char[]
--- @param out_w int
--- @param out_h int
--- @param out_stride int
--- @param scale_x float
--- @param scale_y float
--- @param shift_x float
--- @param shift_y float
--- @param glyph int
local function stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, glyph)
local num_verts, vertices = stbtt_GetGlyphShape(info, glyph)
local gbm = stbtt__bitmap()
local ix0, iy0 = stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y)
gbm.pixels = output
gbm.w = out_w
gbm.h = out_h
gbm.stride = out_stride
if gbm.w ~= 0 and gbm.h ~= 0 then
stbtt_Rasterize(gbm, 0.35, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, true)
end
end
-----------------
-----------------
---
--- bitmap baking
---
---
--- UNIMPLEMENTED / UNUSED:
--- stbtt_BakeFontBitmap_internal
--- stbtt_BakeFontBitmap
--- stbtt_GetBakedQuad
---------------------------
---------------------------
---
--- bitmap baking / packing
---
---
--- UNIMPLEMENTED / UNUSED:
--- stbtt_PackBegin
--- stbtt_PackSetOversampling
--- stbtt_PackSetSkipMissingCodepoints
--- stbtt_PackFontRangesGatherRects
--- stbtt_PackFontRangesRenderIntoRects
--- stbtt_PackFontRangesPackRects
--- stbtt_PackFontRanges
--- stbtt_PackFontRange
--- stbtt_GetPackedQuad
--- stbtt_GetScaledFontVMetrics
local STBTT__OVER_MASK = STBTT_MAX_OVERSAMPLE - 1
--- @param pixels table
--- @param w int
--- @param h int
--- @param stride_in_bytes int
--- @param kernel_width unsigned_int
local function stbtt__h_prefilter(pixels, w, h, stride_in_bytes, kernel_width)
local buffer = {}
local safe_w = w - kernel_width
for j = 0, h - 1 do
local total = 0
local row_offset = j * stride_in_bytes
for i = 1, kernel_width do buffer[i] = 0 end
for i = 0, safe_w do
total = total + pixels[row_offset + i + 1] - buffer[bit32.band(i, STBTT__OVER_MASK) + 1]
buffer[bit32.band(i + kernel_width, STBTT__OVER_MASK) + 1] = pixels[row_offset + i + 1]
pixels[row_offset + i + 1] = unsigned_char(total / kernel_width)
end
for i = safe_w + 1, w - 1 do
STBTT_assert(pixels[row_offset + i + 1] == 0)
total = total - buffer[bit32.band(i, STBTT__OVER_MASK) + 1]
pixels[row_offset + i + 1] = unsigned_char(total / kernel_width)
end
end
end
--- @param pixels table
--- @param w int
--- @param h int
--- @param stride_in_bytes int
--- @param kernel_width unsigned_int
local function stbtt__v_prefilter(pixels, w, h, stride_in_bytes, kernel_width)
local buffer = {}
local safe_h = h - kernel_width
for j = 0, w - 1 do
local total = 0
local col_offset = j
for i = 1, kernel_width do buffer[i] = 0 end
for i = 0, safe_h do
total = total + pixels[col_offset + i * stride_in_bytes + 1] - buffer[bit32.band(i, STBTT__OVER_MASK) + 1]
buffer[bit32.band(i + kernel_width, STBTT__OVER_MASK) + 1] = pixels[col_offset + i * stride_in_bytes + 1]
pixels[col_offset + i * stride_in_bytes + 1] = unsigned_char(total / kernel_width)
end
for i = safe_h + 1, h - 1 do
STBTT_assert(pixels[col_offset + i * stride_in_bytes + 1] == 0)
total = total - buffer[bit32.band(i, STBTT__OVER_MASK) + 1]
pixels[col_offset + i * stride_in_bytes + 1] = unsigned_char(total / kernel_width)
end
end
end
--- @param oversample int
--- @return float
local function stbtt__oversample_shift(oversample)
if oversample == 0 then
return 0.0
end
-- The prefilter is a box filter of width "oversample",
-- which shifts phase by (oversample - 1)/2 pixels in
-- oversampled space. We want to shift in the opposite
-- direction to counter this.
return -(oversample - 1) / (2.0 * oversample)
end
-- same as stbtt_MakeGlyphBitmapSubpixel, but prefiltering
-- is performed. Returns (sub_x, sub_y).
--- @param info stbtt_fontinfo
--- @param output unsigned_char[]
--- @param out_w int
--- @param out_h int
--- @param out_stride int
--- @param scale_x float
--- @param scale_y float
--- @param shift_x float
--- @param shift_y float
--- @param prefilter_x int
--- @param prefilter_y int
--- @param glyph int
function stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, prefilter_x, prefilter_y, glyph)
stbtt_MakeGlyphBitmapSubpixel(info, output, out_w - (prefilter_x - 1), out_h - (prefilter_y - 1), out_stride, scale_x, scale_y, shift_x, shift_y, glyph)
if prefilter_x > 1 then
stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x)
end
if prefilter_y > 1 then
stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y)
end
local sub_x, sub_y
sub_x = stbtt__oversample_shift(prefilter_x)
sub_y = stbtt__oversample_shift(prefilter_y)
return sub_x, sub_y
end
-------------------
-------------------
---
--- sdf computation
---
---
--- UNIMPLEMENTED / UNUSED:
--- stbtt__ray_intersect_bezier
--- stbtt__compute_crossings_x
--- stbtt__cuberoot
--- stbtt__solve_cubic
--- stbtt_GetGlyphSDF
--- stbtt_GetCodepointSDF
-----------------------------------------------------
-----------------------------------------------------
---
--- Font name matching -- recommended not to use this
---
---
--- UNIMPLEMENTED / UNUSED:
--- stbtt__CompareUTF8toUTF16_bigendian_prefix
--- stbtt_CompareUTF8toUTF16_bigendian_internal
--- stbtt_GetFontNameString
--- stbtt__matchpair
--- stbtt__matches
-- Each .ttf/.ttc file may have more than one font. Each font has a sequential
-- index number starting from 0. Call this function to get the font offset for
-- a given index; it returns -1 if the index is out of range. A regular .ttf
-- file will only define one font and it always be at offset 0, so it will
-- return '0' for index 0, and -1 for all other indices.
--- @param data table
--- @param index int
function stbtt_GetFontOffsetForIndex(data, index) return stbtt_GetFontOffsetForIndex_internal(data, index) end
-- Given an offset into the file that defines a font, this function builds
-- the necessary cached info for the rest of the system. You must allocate
-- the stbtt_fontinfo yourself (see stbtt_fontinfo()), and stbtt_InitFont will fill it out. You don't
-- need to do anything special to free it, because the contents are pure
-- value data with no additional data structures. Returns false on failure.
--- @param info stbtt_fontinfo
--- @param data table
--- @param offset int
function stbtt_InitFont(info, data, offset) return stbtt_InitFont_internal(info, data, offset) end
return {
fontinfo = stbtt_fontinfo,
InitFont = stbtt_InitFont,
FindGlyphIndex = stbtt_FindGlyphIndex,
MakeGlyphBitmapSubpixelPrefilter = stbtt_MakeGlyphBitmapSubpixelPrefilter,
GetFontVMetrics = stbtt_GetFontVMetrics,
GetGlyphBitmapBox = stbtt_GetGlyphBitmapBox,
GetGlyphBitmapBoxSubpixel = stbtt_GetGlyphBitmapBoxSubpixel,
GetGlyphHMetrics = stbtt_GetGlyphHMetrics,
GetFontOffsetForIndex = stbtt_GetFontOffsetForIndex,
ScaleForPixelHeight = stbtt_ScaleForPixelHeight
}
end
__FILES['imstd_minstdio.lua'] = function()
--- Dear ImGui
-- This is a minimal Lua impl of C-like sscanf, sprintf sub-set
-- Supports:
-- - %x, %X, %nx, %nX (where n is a number)
-- - %d, %nd (where n is a number)
-- - %f, %lf (very limited)
--
-- Returns items_matched instead of EOF on error
local _byte = string.byte
local CHAR_PERCENT = _byte'%'
local CHAR_DOT = _byte'.'
local CHAR_PLUS, CHAR_MINUS = _byte'+', _byte'-'
local CHAR_0, CHAR_9 = _byte'0', _byte'9'
local CHAR_a, CHAR_A = _byte'a', _byte'A'
local CHAR_d = _byte'd'
local CHAR_l = _byte'l'
local CHAR_f, CHAR_F = _byte'f', _byte'F'
local CHAR_x, CHAR_X = _byte'x', _byte'X'
local function isspace(c) return c == 32 or c == 9 end
local function isdigit(c) return c >= CHAR_0 and c <= CHAR_9 end
local function isxdigit(c) return isdigit(c) or (c >= CHAR_a and c <= CHAR_f) or (c >= CHAR_A and c <= CHAR_F) end
local function isdigit_under_base(c, base)
if base == 10 then return isdigit(c)
elseif base == 16 then return isxdigit(c)
end
end
local function hex_digit_to_int(c)
if c >= CHAR_0 and c <= CHAR_9 then return c - CHAR_0 end
if c >= CHAR_a and c <= CHAR_f then return c - CHAR_a + 10 end
if c >= CHAR_A and c <= CHAR_F then return c - CHAR_A + 10 end
end
-- we force this behavior by default
local function clamp_if_overflow(spec, val)
if spec == CHAR_d or spec == CHAR_x or spec == CHAR_X then
if val > 2147483647 --[[INT_MAX]] then return 2147483647 --[[INT_MAX]] end
if val < (-2147483648) --[[INT_MIN]] then return (-2147483648) --[[INT_MIN]] end
end
return val
end
--- @param buffer char[]
--- @param buffer_begin int
--- @param format string
--- @param result table # table to store parsed values
local function sscanf(buffer, buffer_begin, format, result)
local p = buffer_begin
local f = 1 -- format_begin
local format_len = #format
local assigned = 0
local items_matched = 0
while f <= format_len do
repeat
if isspace(_byte(format, f, f)) then
while isspace(buffer[p]) do
p = p + 1
end
f = f + 1
break
end
if _byte(format, f, f) ~= CHAR_PERCENT then
if buffer[p] == _byte(format, f, f) then
p = p + 1
f = f + 1
else
return items_matched
end
break
end
-- encountered % in fmt
f = f + 1
-- %%
if _byte(format, f, f) == CHAR_PERCENT then
if buffer[p] == CHAR_PERCENT then
p = p + 1
f = f + 1
else
return items_matched
end
break
end
-- handle optional width
local width = 0
while isdigit(_byte(format, f, f)) do
width = width * 10 + _byte(format, f, f) - CHAR_0
f = f + 1
end
if width == 0 then
width = math.huge
end
local spec = _byte(format, f, f)
local base
local is_float = false
if spec == CHAR_x or spec == CHAR_X then
base = 16
elseif spec == CHAR_d then
base = 10
elseif spec == CHAR_f or spec == CHAR_F then
is_float = true
elseif spec == CHAR_l then
f = f + 1
if _byte(format, f, f) == CHAR_f then
is_float = true -- TODO: distinguish from float?
else
return items_matched
end
else
return items_matched
end
f = f + 1
while isspace(buffer[p]) do
p = p + 1
end
local val
if is_float then
local sign = 1
if buffer[p] == CHAR_PLUS then
p = p + 1
elseif buffer[p] == CHAR_MINUS then
sign = -1
p = p + 1
end
-- inf, nan are not handled
local p_start = p
local int_part = 0
while isdigit(buffer[p]) do
int_part = int_part * 10 + (buffer[p] - CHAR_0)
p = p + 1
end
local frac_part = 0
local frac_div = 1
if buffer[p] == CHAR_DOT then
p = p + 1
while isdigit(buffer[p]) do
frac_part = frac_part * 10 + (buffer[p] - CHAR_0)
frac_div = frac_div * 10
p = p + 1
end
end
if p == p_start then
return items_matched
end
val = sign * (int_part + frac_part / frac_div)
else
local sign = 1
-- optional sign
if base == 10 then
if buffer[p] == CHAR_PLUS then
p = p + 1
width = width - 1
elseif buffer[p] == CHAR_MINUS then
sign = -1
p = p + 1
width = width - 1
end
end
local p_start = p
while width > 0 and buffer[p] and isdigit_under_base(buffer[p], base) do
p = p + 1
width = width - 1
end
if p == p_start then
-- failed to match
return items_matched
end
val = 0
while p_start < p do
local c = buffer[p_start]
p_start = p_start + 1
val = val * base + sign * hex_digit_to_int(c)
local old_val = val
val = clamp_if_overflow(spec, val)
if old_val ~= val then
break
end
end
end
result[assigned + 1] = val
assigned = assigned + 1
items_matched = items_matched + 1
until true
end
return items_matched
end
local function sprintf()
end
return sscanf
end
IM_INCLUDE('imgui.lua')
IM_INCLUDE('imgui_impl_roblox.lua')
IM_INCLUDE('imgui_demo.lua')
return {
BuildLoadGlyphGetAdvanceOrFallback = BuildLoadGlyphGetAdvanceOrFallback,
CalcCornerArcStepAndCount = CalcCornerArcStepAndCount,
CalcNextScrollFromScrollTargetAndClamp = CalcNextScrollFromScrollTargetAndClamp,
CustomConstraints = CustomConstraints,
DBL_MAX = DBL_MAX,
DebugNodeTableGetSizingPolicyDesc = DebugNodeTableGetSizingPolicyDesc,
DemoWindowColumns = DemoWindowColumns,
DemoWindowInputs = DemoWindowInputs,
DemoWindowLayout = DemoWindowLayout,
DemoWindowMenuBar = DemoWindowMenuBar,
DemoWindowPopups = DemoWindowPopups,
DemoWindowTables = DemoWindowTables,
DemoWindowWidgets = DemoWindowWidgets,
DemoWindowWidgetsBasic = DemoWindowWidgetsBasic,
DemoWindowWidgetsBullets = DemoWindowWidgetsBullets,
DemoWindowWidgetsCollapsingHeaders = DemoWindowWidgetsCollapsingHeaders,
DemoWindowWidgetsColorAndPickers = DemoWindowWidgetsColorAndPickers,
DemoWindowWidgetsComboBoxes = DemoWindowWidgetsComboBoxes,
DemoWindowWidgetsDataTypes = DemoWindowWidgetsDataTypes,
DemoWindowWidgetsDisableBlocks = DemoWindowWidgetsDisableBlocks,
DemoWindowWidgetsDragAndDrop = DemoWindowWidgetsDragAndDrop,
DemoWindowWidgetsDragsAndSliders = DemoWindowWidgetsDragsAndSliders,
DemoWindowWidgetsFonts = DemoWindowWidgetsFonts,
DemoWindowWidgetsImages = DemoWindowWidgetsImages,
DemoWindowWidgetsListBoxes = DemoWindowWidgetsListBoxes,
DemoWindowWidgetsLiveEdit = DemoWindowWidgetsLiveEdit,
DemoWindowWidgetsMixedValues = DemoWindowWidgetsMixedValues,
DemoWindowWidgetsMultiComponents = DemoWindowWidgetsMultiComponents,
DemoWindowWidgetsPlotting = DemoWindowWidgetsPlotting,
DemoWindowWidgetsProgressBars = DemoWindowWidgetsProgressBars,
DemoWindowWidgetsQueryingStatuses = DemoWindowWidgetsQueryingStatuses,
DemoWindowWidgetsSelectables = DemoWindowWidgetsSelectables,
DemoWindowWidgetsSelectionAndMultiSelect = DemoWindowWidgetsSelectionAndMultiSelect,
DemoWindowWidgetsTabs = DemoWindowWidgetsTabs,
DemoWindowWidgetsText = DemoWindowWidgetsText,
DemoWindowWidgetsTextFilter = DemoWindowWidgetsTextFilter,
DemoWindowWidgetsTextInput = DemoWindowWidgetsTextInput,
DemoWindowWidgetsTooltips = DemoWindowWidgetsTooltips,
DemoWindowWidgetsTreeNodes = DemoWindowWidgetsTreeNodes,
DemoWindowWidgetsVerticalSliders = DemoWindowWidgetsVerticalSliders,
DockSettingsFindNodeSettings = DockSettingsFindNodeSettings,
DockSettingsHandler_ApplyAll = DockSettingsHandler_ApplyAll,
DockSettingsHandler_ClearAll = DockSettingsHandler_ClearAll,
DockSettingsHandler_ReadLine = DockSettingsHandler_ReadLine,
DockSettingsHandler_ReadOpen = DockSettingsHandler_ReadOpen,
DockSettingsHandler_WriteAll = DockSettingsHandler_WriteAll,
DockSettingsRemoveNodeReferences = DockSettingsRemoveNodeReferences,
DockSettingsRenameNodeReferences = DockSettingsRenameNodeReferences,
EditTabBarFittingPolicyFlags = EditTabBarFittingPolicyFlags,
EditTableColumnsFlags = EditTableColumnsFlags,
EditTableSizingFlags = EditTableSizingFlags,
ExampleAppConsole = ExampleAppConsole,
ExampleAppConsoleMT = ExampleAppConsoleMT,
ExampleAppDocuments = ExampleAppDocuments,
ExampleAppLog = ExampleAppLog,
ExampleAppLogMT = ExampleAppLogMT,
ExampleAppPropertyEditor = ExampleAppPropertyEditor,
ExampleAppPropertyEditorMT = ExampleAppPropertyEditorMT,
ExampleAsset = ExampleAsset,
ExampleAsset_CompareWithSortSpecs = ExampleAsset_CompareWithSortSpecs,
ExampleAsset_SortWithSortSpecs = ExampleAsset_SortWithSortSpecs,
ExampleAsset_s_current_sort_specs = ExampleAsset_s_current_sort_specs,
ExampleAssetsBrowser = ExampleAssetsBrowser,
ExampleDualListBox = ExampleDualListBox,
ExampleDualListBoxMT = ExampleDualListBoxMT,
ExampleImageViewerData = ExampleImageViewerData,
ExampleImageViewer_DrawCanvas = ExampleImageViewer_DrawCanvas,
ExampleImageViewer_DrawOptions = ExampleImageViewer_DrawOptions,
ExampleMemberInfo = ExampleMemberInfo,
ExampleNames = ExampleNames,
ExampleSelectionWithDeletion = ExampleSelectionWithDeletion,
ExampleSelectionWithDeletionMT = ExampleSelectionWithDeletionMT,
ExampleTreeFuncs = ExampleTreeFuncs,
ExampleTreeNode = ExampleTreeNode,
ExampleTreeNodeMemberInfos = ExampleTreeNodeMemberInfos,
ExampleTree_CreateDemoTree = ExampleTree_CreateDemoTree,
ExampleTree_CreateNode = ExampleTree_CreateNode,
ExampleTree_DestroyNode = ExampleTree_DestroyNode,
FLT_MAX = FLT_MAX,
FLT_MIN = FLT_MIN,
FONT_ATLAS_DEFAULT_TEX_DATA_H = FONT_ATLAS_DEFAULT_TEX_DATA_H,
FONT_ATLAS_DEFAULT_TEX_DATA_W = FONT_ATLAS_DEFAULT_TEX_DATA_W,
FormatCached = FormatCached,
GetDefaultFontDataProggyClean = GetDefaultFontDataProggyClean,
GetDefaultFontDataProggyForever = GetDefaultFontDataProggyForever,
GetMinimumStepAtDecimalPrecision = GetMinimumStepAtDecimalPrecision,
GetTreeLinesFlagsName = GetTreeLinesFlagsName,
HelpMarker = HelpMarker,
IMGUI_DEBUG_LOG_ACTIVEID = IMGUI_DEBUG_LOG_ACTIVEID,
IMGUI_DEBUG_LOG_CLIPPER = IMGUI_DEBUG_LOG_CLIPPER,
IMGUI_DEBUG_LOG_DOCKING = IMGUI_DEBUG_LOG_DOCKING,
IMGUI_DEBUG_LOG_ERROR = IMGUI_DEBUG_LOG_ERROR,
IMGUI_DEBUG_LOG_FOCUS = IMGUI_DEBUG_LOG_FOCUS,
IMGUI_DEBUG_LOG_FONT = IMGUI_DEBUG_LOG_FONT,
IMGUI_DEBUG_LOG_INPUTROUTING = IMGUI_DEBUG_LOG_INPUTROUTING,
IMGUI_DEBUG_LOG_IO = IMGUI_DEBUG_LOG_IO,
IMGUI_DEBUG_LOG_NAV = IMGUI_DEBUG_LOG_NAV,
IMGUI_DEBUG_LOG_POPUP = IMGUI_DEBUG_LOG_POPUP,
IMGUI_DEBUG_LOG_SELECTION = IMGUI_DEBUG_LOG_SELECTION,
IMGUI_DEBUG_LOG_TABLE = IMGUI_DEBUG_LOG_TABLE,
IMGUI_DEBUG_LOG_VIEWPORT = IMGUI_DEBUG_LOG_VIEWPORT,
IMGUI_DEMO_MARKER = IMGUI_DEMO_MARKER,
IMGUI_DISABLE_DEBUG_TOOLS = IMGUI_DISABLE_DEBUG_TOOLS,
IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS = IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS,
IMGUI_DISABLE_TIME_FUNCTIONS = IMGUI_DISABLE_TIME_FUNCTIONS,
IMGUI_FONT_SIZE_MAX = IMGUI_FONT_SIZE_MAX,
IMGUI_FONT_SIZE_THRESHOLD_FOR_LOADADVANCEXONLYMODE = IMGUI_FONT_SIZE_THRESHOLD_FOR_LOADADVANCEXONLYMODE,
IMGUI_PAYLOAD_TYPE_COLOR_3F = IMGUI_PAYLOAD_TYPE_COLOR_3F,
IMGUI_PAYLOAD_TYPE_COLOR_4F = IMGUI_PAYLOAD_TYPE_COLOR_4F,
IMGUI_PAYLOAD_TYPE_WINDOW = IMGUI_PAYLOAD_TYPE_WINDOW,
IMGUI_TABLE_MAX_COLUMNS = IMGUI_TABLE_MAX_COLUMNS,
IMGUI_VERSION = IMGUI_VERSION,
IMGUI_VERSION_NUM = IMGUI_VERSION_NUM,
IMGUI_WINDOW_HARD_MIN_SIZE = IMGUI_WINDOW_HARD_MIN_SIZE,
IMSTB_TEXTEDIT_UNDOSTATECOUNT = IMSTB_TEXTEDIT_UNDOSTATECOUNT,
IM_ALLOC = IM_ALLOC,
IM_ASSERT = IM_ASSERT,
IM_ASSERT_PARANOID = IM_ASSERT_PARANOID,
IM_ASSERT_USER_ERROR = IM_ASSERT_USER_ERROR,
IM_ASSERT_USER_ERROR_RET = IM_ASSERT_USER_ERROR_RET,
IM_BITARRAY_CLEARBIT = IM_BITARRAY_CLEARBIT,
IM_BITARRAY_TESTBIT = IM_BITARRAY_TESTBIT,
IM_COL32 = IM_COL32,
IM_COL32_A_MASK = IM_COL32_A_MASK,
IM_COL32_A_SHIFT = IM_COL32_A_SHIFT,
IM_COL32_BLACK = IM_COL32_BLACK,
IM_COL32_BLACK_TRANS = IM_COL32_BLACK_TRANS,
IM_COL32_B_SHIFT = IM_COL32_B_SHIFT,
IM_COL32_DISABLE = IM_COL32_DISABLE,
IM_COL32_G_SHIFT = IM_COL32_G_SHIFT,
IM_COL32_R_SHIFT = IM_COL32_R_SHIFT,
IM_COL32_WHITE = IM_COL32_WHITE,
IM_DELETE = IM_DELETE,
IM_DRAWLIST_ARCFAST_SAMPLE_MAX = IM_DRAWLIST_ARCFAST_SAMPLE_MAX,
IM_DRAWLIST_ARCFAST_TABLE_SIZE = IM_DRAWLIST_ARCFAST_TABLE_SIZE,
IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC,
IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX = IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX,
IM_DRAWLIST_TEX_CORNERS_THICKNESS_MAX = IM_DRAWLIST_TEX_CORNERS_THICKNESS_MAX,
IM_DRAWLIST_TEX_LINES_DETAILED_WIDTH = IM_DRAWLIST_TEX_LINES_DETAILED_WIDTH,
IM_DRAWLIST_TEX_LINES_DETAILED_WIDTH_COUNT = IM_DRAWLIST_TEX_LINES_DETAILED_WIDTH_COUNT,
IM_DRAWLIST_TEX_LINES_SAMPLE_COUNT = IM_DRAWLIST_TEX_LINES_SAMPLE_COUNT,
IM_DRAWLIST_TEX_LINES_WIDTH_MAX = IM_DRAWLIST_TEX_LINES_WIDTH_MAX,
IM_F32_TO_INT8_SAT = IM_F32_TO_INT8_SAT,
IM_F32_TO_INT8_UNBOUND = IM_F32_TO_INT8_UNBOUND,
IM_FILE_CLOSE = IM_FILE_CLOSE,
IM_FILE_READ = IM_FILE_READ,
IM_FILE_SIZE = IM_FILE_SIZE,
IM_FILE_WRITE = IM_FILE_WRITE,
IM_FREE = IM_FREE,
IM_PI = IM_PI,
IM_ROUND = IM_ROUND,
IM_ROUNDUP_TO_EVEN = IM_ROUNDUP_TO_EVEN,
IM_TABSIZE = IM_TABSIZE,
IM_TRUNC = IM_TRUNC,
IM_UNICODE_CODEPOINT_INVALID = IM_UNICODE_CODEPOINT_INVALID,
IM_UNICODE_CODEPOINT_MAX = IM_UNICODE_CODEPOINT_MAX,
INT_MAX = INT_MAX,
INT_MIN = INT_MIN,
ImAbs = ImAbs,
ImAcos = ImAcos,
ImAddClampOverflow = ImAddClampOverflow,
ImAtan2 = ImAtan2,
ImAtof = ImAtof,
ImBezierCubicCalc = ImBezierCubicCalc,
ImBezierCubicClosestPoint = ImBezierCubicClosestPoint,
ImBezierCubicClosestPointCasteljau = ImBezierCubicClosestPointCasteljau,
ImBezierQuadraticCalc = ImBezierQuadraticCalc,
ImBitArray = ImBitArray,
ImBitArrayClearAllBits = ImBitArrayClearAllBits,
ImBitArrayClearBit = ImBitArrayClearBit,
ImBitArrayGetStorageSizeInBytes = ImBitArrayGetStorageSizeInBytes,
ImBitArraySetBit = ImBitArraySetBit,
ImBitArraySetBitRange = ImBitArraySetBitRange,
ImBitArrayTestBit = ImBitArrayTestBit,
ImBitVector = ImBitVector,
ImCeil = ImCeil,
ImCeilFast = ImCeilFast,
ImCharIsBlankA = ImCharIsBlankA,
ImCharIsBlankW = ImCharIsBlankW,
ImCharIsSeparatorW = ImCharIsSeparatorW,
ImCharIsXdigitA = ImCharIsXdigitA,
ImClamp = ImClamp,
ImColor = ImColor,
ImCos = ImCos,
ImCountSetBits = ImCountSetBits,
ImDot = ImDot,
ImDrawChannel = ImDrawChannel,
ImDrawCmd = ImDrawCmd,
ImDrawCmdHeader = ImDrawCmdHeader,
ImDrawData = ImDrawData,
ImDrawFlags = ImDrawFlags,
ImDrawList = ImDrawList,
ImDrawListFlags = ImDrawListFlags,
ImDrawListSharedData = ImDrawListSharedData,
ImDrawListSplitter = ImDrawListSplitter,
ImDrawTextFlags = ImDrawTextFlags,
ImDrawVert = ImDrawVert,
ImDrawVtxBuffer = ImDrawVtxBuffer,
ImDrawVtx_Col = ImDrawVtx_Col,
ImDrawVtx_Pos = ImDrawVtx_Pos,
ImFabs = ImFabs,
ImFloor = ImFloor,
ImFmod = ImFmod,
ImFont = ImFont,
ImFontAtlas = ImFontAtlas,
ImFontAtlasAddDrawListSharedData = ImFontAtlasAddDrawListSharedData,
ImFontAtlasBakedAdd = ImFontAtlasBakedAdd,
ImFontAtlasBakedAddFontGlyph = ImFontAtlasBakedAddFontGlyph,
ImFontAtlasBakedAddFontGlyphAdvancedX = ImFontAtlasBakedAddFontGlyphAdvancedX,
ImFontAtlasBakedDiscard = ImFontAtlasBakedDiscard,
ImFontAtlasBakedDiscardFontGlyph = ImFontAtlasBakedDiscardFontGlyph,
ImFontAtlasBakedGetClosestMatch = ImFontAtlasBakedGetClosestMatch,
ImFontAtlasBakedGetId = ImFontAtlasBakedGetId,
ImFontAtlasBakedGetOrAdd = ImFontAtlasBakedGetOrAdd,
ImFontAtlasBuildAcceptCodepointForSource = ImFontAtlasBuildAcceptCodepointForSource,
ImFontAtlasBuildClear = ImFontAtlasBuildClear,
ImFontAtlasBuildDestroy = ImFontAtlasBuildDestroy,
ImFontAtlasBuildInit = ImFontAtlasBuildInit,
ImFontAtlasBuildLegacyPreloadAllGlyphRanges = ImFontAtlasBuildLegacyPreloadAllGlyphRanges,
ImFontAtlasBuildMain = ImFontAtlasBuildMain,
ImFontAtlasBuildNotifySetFont = ImFontAtlasBuildNotifySetFont,
ImFontAtlasBuildRenderBitmapFromString = ImFontAtlasBuildRenderBitmapFromString,
ImFontAtlasBuildSetTexture = ImFontAtlasBuildSetTexture,
ImFontAtlasBuildSetupFontBakedBlanks = ImFontAtlasBuildSetupFontBakedBlanks,
ImFontAtlasBuildSetupFontBakedEllipsis = ImFontAtlasBuildSetupFontBakedEllipsis,
ImFontAtlasBuildSetupFontBakedFallback = ImFontAtlasBuildSetupFontBakedFallback,
ImFontAtlasBuildSetupFontLoader = ImFontAtlasBuildSetupFontLoader,
ImFontAtlasBuildSetupFontSpecialGlyphs = ImFontAtlasBuildSetupFontSpecialGlyphs,
ImFontAtlasBuildUpdateTexData = ImFontAtlasBuildUpdateTexData,
ImFontAtlasBuildUpdateTexDataBasic = ImFontAtlasBuildUpdateTexDataBasic,
ImFontAtlasBuildUpdateTexDataCorners = ImFontAtlasBuildUpdateTexDataCorners,
ImFontAtlasBuildUpdateTexDataLines = ImFontAtlasBuildUpdateTexDataLines,
ImFontAtlasBuilder = ImFontAtlasBuilder,
ImFontAtlasFlags = ImFontAtlasFlags,
ImFontAtlasFontDestroyOutput = ImFontAtlasFontDestroyOutput,
ImFontAtlasFontDestroySourceData = ImFontAtlasFontDestroySourceData,
ImFontAtlasFontDiscardBakes = ImFontAtlasFontDiscardBakes,
ImFontAtlasFontInitOutput = ImFontAtlasFontInitOutput,
ImFontAtlasFontRebuildOutput = ImFontAtlasFontRebuildOutput,
ImFontAtlasFontSourceAddToFont = ImFontAtlasFontSourceAddToFont,
ImFontAtlasFontSourceInit = ImFontAtlasFontSourceInit,
ImFontAtlasGetMouseCursorTexData = ImFontAtlasGetMouseCursorTexData,
ImFontAtlasPackAddRect = ImFontAtlasPackAddRect,
ImFontAtlasPackDiscardRect = ImFontAtlasPackDiscardRect,
ImFontAtlasPackGetRect = ImFontAtlasPackGetRect,
ImFontAtlasPackGetRectSafe = ImFontAtlasPackGetRectSafe,
ImFontAtlasPackInit = ImFontAtlasPackInit,
ImFontAtlasPostProcessData = ImFontAtlasPostProcessData,
ImFontAtlasRect = ImFontAtlasRect,
ImFontAtlasRectEntry = ImFontAtlasRectEntry,
ImFontAtlasRectId_GetGeneration = ImFontAtlasRectId_GetGeneration,
ImFontAtlasRectId_GetIndex = ImFontAtlasRectId_GetIndex,
ImFontAtlasRectId_Invalid = ImFontAtlasRectId_Invalid,
ImFontAtlasRectId_Make = ImFontAtlasRectId_Make,
ImFontAtlasRemoveDrawListSharedData = ImFontAtlasRemoveDrawListSharedData,
ImFontAtlasTextureAdd = ImFontAtlasTextureAdd,
ImFontAtlasTextureBlockConvert = ImFontAtlasTextureBlockConvert,
ImFontAtlasTextureBlockCopy = ImFontAtlasTextureBlockCopy,
ImFontAtlasTextureBlockFill = ImFontAtlasTextureBlockFill,
ImFontAtlasTextureBlockPostProcess = ImFontAtlasTextureBlockPostProcess,
ImFontAtlasTextureBlockPostProcessMultiply = ImFontAtlasTextureBlockPostProcessMultiply,
ImFontAtlasTextureBlockQueueUpload = ImFontAtlasTextureBlockQueueUpload,
ImFontAtlasTextureCompact = ImFontAtlasTextureCompact,
ImFontAtlasTextureGetSizeEstimate = ImFontAtlasTextureGetSizeEstimate,
ImFontAtlasTextureGrow = ImFontAtlasTextureGrow,
ImFontAtlasUpdateDrawListsSharedData = ImFontAtlasUpdateDrawListsSharedData,
ImFontAtlasUpdateNewFrame = ImFontAtlasUpdateNewFrame,
ImFontBaked = ImFontBaked,
ImFontBaked_BuildGrowIndex = ImFontBaked_BuildGrowIndex,
ImFontBaked_BuildLoadGlyph = ImFontBaked_BuildLoadGlyph,
ImFontBaked_BuildLoadGlyphAdvanceX = ImFontBaked_BuildLoadGlyphAdvanceX,
ImFontCalcTextSizeEx = ImFontCalcTextSizeEx,
ImFontCalcWordWrapPositionEx = ImFontCalcWordWrapPositionEx,
ImFontCalcWordWrapPositionEx_Uncached = ImFontCalcWordWrapPositionEx_Uncached,
ImFontConfig = ImFontConfig,
ImFontFlags = ImFontFlags,
ImFontGlyph = ImFontGlyph,
ImFontGlyphRangesBuilder = ImFontGlyphRangesBuilder,
ImFontLoader = ImFontLoader,
ImFontStackData = ImFontStackData,
ImFormatString = ImFormatString,
ImGui = ImGui,
ImGuiActivateFlags = ImGuiActivateFlags,
ImGuiAxis = ImGuiAxis,
ImGuiBackendFlags = ImGuiBackendFlags,
ImGuiBoxSelectState = ImGuiBoxSelectState,
ImGuiButtonFlags = ImGuiButtonFlags,
ImGuiChildFlags = ImGuiChildFlags,
ImGuiCol = ImGuiCol,
ImGuiColorEditFlags = ImGuiColorEditFlags,
ImGuiColorMod = ImGuiColorMod,
ImGuiComboFlags = ImGuiComboFlags,
ImGuiCond = ImGuiCond,
ImGuiConfigFlags = ImGuiConfigFlags,
ImGuiContext = ImGuiContext,
ImGuiContextHook = ImGuiContextHook,
ImGuiContextHookType = ImGuiContextHookType,
ImGuiDataAuthority = ImGuiDataAuthority,
ImGuiDataType = ImGuiDataType,
ImGuiDebugItemPathQuery = ImGuiDebugItemPathQuery,
ImGuiDebugLogFlags = ImGuiDebugLogFlags,
ImGuiDemoDockspaceArgs = ImGuiDemoDockspaceArgs,
ImGuiDemoWindowData = ImGuiDemoWindowData,
ImGuiDir = ImGuiDir,
ImGuiDockContext = ImGuiDockContext,
ImGuiDockNode = ImGuiDockNode,
ImGuiDockNodeFlags = ImGuiDockNodeFlags,
ImGuiDockNodeState = ImGuiDockNodeState,
ImGuiDragDropFlags = ImGuiDragDropFlags,
ImGuiErrorRecoveryState = ImGuiErrorRecoveryState,
ImGuiFocusRequestFlags = ImGuiFocusRequestFlags,
ImGuiFocusScopeData = ImGuiFocusScopeData,
ImGuiFocusedFlags = ImGuiFocusedFlags,
ImGuiGroupData = ImGuiGroupData,
ImGuiHoveredFlags = ImGuiHoveredFlags,
ImGuiIDStackTool = ImGuiIDStackTool,
ImGuiIO = ImGuiIO,
ImGuiInputEvent = ImGuiInputEvent,
ImGuiInputEventAppFocused = ImGuiInputEventAppFocused,
ImGuiInputEventKey = ImGuiInputEventKey,
ImGuiInputEventMouseButton = ImGuiInputEventMouseButton,
ImGuiInputEventMousePos = ImGuiInputEventMousePos,
ImGuiInputEventMouseViewport = ImGuiInputEventMouseViewport,
ImGuiInputEventMouseWheel = ImGuiInputEventMouseWheel,
ImGuiInputEventText = ImGuiInputEventText,
ImGuiInputEventType = ImGuiInputEventType,
ImGuiInputFlags = ImGuiInputFlags,
ImGuiInputSource = ImGuiInputSource,
ImGuiInputTextCallbackData = ImGuiInputTextCallbackData,
ImGuiInputTextFlags = ImGuiInputTextFlags,
ImGuiItemFlags = ImGuiItemFlags,
ImGuiItemStatusFlags = ImGuiItemStatusFlags,
ImGuiKey = ImGuiKey,
ImGuiKeyData = ImGuiKeyData,
ImGuiKeyOwnerData = ImGuiKeyOwnerData,
ImGuiKeyOwner_Any = ImGuiKeyOwner_Any,
ImGuiKeyOwner_NoOwner = ImGuiKeyOwner_NoOwner,
ImGuiKeyRoutingData = ImGuiKeyRoutingData,
ImGuiKey_Aliases_BEGIN = ImGuiKey_Aliases_BEGIN,
ImGuiKey_Aliases_END = ImGuiKey_Aliases_END,
ImGuiKey_Gamepad_BEGIN = ImGuiKey_Gamepad_BEGIN,
ImGuiKey_Gamepad_END = ImGuiKey_Gamepad_END,
ImGuiKey_Keyboard_BEGIN = ImGuiKey_Keyboard_BEGIN,
ImGuiKey_Keyboard_END = ImGuiKey_Keyboard_END,
ImGuiKey_LegacyNativeKey_BEGIN = ImGuiKey_LegacyNativeKey_BEGIN,
ImGuiKey_LegacyNativeKey_END = ImGuiKey_LegacyNativeKey_END,
ImGuiKey_Mouse_BEGIN = ImGuiKey_Mouse_BEGIN,
ImGuiKey_Mouse_END = ImGuiKey_Mouse_END,
ImGuiLastItemData = ImGuiLastItemData,
ImGuiLastItemData_Copy = ImGuiLastItemData_Copy,
ImGuiLayoutType = ImGuiLayoutType,
ImGuiListClipper = ImGuiListClipper,
ImGuiListClipperData = ImGuiListClipperData,
ImGuiListClipperFlags = ImGuiListClipperFlags,
ImGuiLocKey = ImGuiLocKey,
ImGuiLogFlags = ImGuiLogFlags,
ImGuiMod_Alt = ImGuiMod_Alt,
ImGuiMod_Ctrl = ImGuiMod_Ctrl,
ImGuiMod_Mask_ = ImGuiMod_Mask_,
ImGuiMod_None = ImGuiMod_None,
ImGuiMod_Shift = ImGuiMod_Shift,
ImGuiMod_Super = ImGuiMod_Super,
ImGuiMouseButton = ImGuiMouseButton,
ImGuiMouseCursor = ImGuiMouseCursor,
ImGuiMouseSource = ImGuiMouseSource,
ImGuiMultiSelectFlags = ImGuiMultiSelectFlags,
ImGuiMultiSelectIO = ImGuiMultiSelectIO,
ImGuiMultiSelectState = ImGuiMultiSelectState,
ImGuiMultiSelectTempData = ImGuiMultiSelectTempData,
ImGuiNavLayer = ImGuiNavLayer,
ImGuiNavMoveFlags = ImGuiNavMoveFlags,
ImGuiNavRenderCursorFlags = ImGuiNavRenderCursorFlags,
ImGuiNextItemData = ImGuiNextItemData,
ImGuiNextItemDataFlags = ImGuiNextItemDataFlags,
ImGuiNextWindowData = ImGuiNextWindowData,
ImGuiNextWindowDataFlags = ImGuiNextWindowDataFlags,
ImGuiOldColumnData = ImGuiOldColumnData,
ImGuiOldColumnFlags = ImGuiOldColumnFlags,
ImGuiOldColumns = ImGuiOldColumns,
ImGuiOnceUponAFrame = ImGuiOnceUponAFrame,
ImGuiPackedDate = ImGuiPackedDate,
ImGuiPayload = ImGuiPayload,
ImGuiPayload_Clear = ImGuiPayload_Clear,
ImGuiPayload_IsDataType = ImGuiPayload_IsDataType,
ImGuiPayload_IsDelivery = ImGuiPayload_IsDelivery,
ImGuiPayload_IsPreview = ImGuiPayload_IsPreview,
ImGuiPlatformIO = ImGuiPlatformIO,
ImGuiPlatformImeData = ImGuiPlatformImeData,
ImGuiPlatformImeData_Compare = ImGuiPlatformImeData_Compare,
ImGuiPlatformImeData_Copy = ImGuiPlatformImeData_Copy,
ImGuiPlatformMonitor = ImGuiPlatformMonitor,
ImGuiPlotArrayGetterData = ImGuiPlotArrayGetterData,
ImGuiPlotType = ImGuiPlotType,
ImGuiPopupData = ImGuiPopupData,
ImGuiPopupFlags = ImGuiPopupFlags,
ImGuiPopupPositionPolicy = ImGuiPopupPositionPolicy,
ImGuiPtrOrIndex = ImGuiPtrOrIndex,
ImGuiScrollFlags = ImGuiScrollFlags,
ImGuiSelectableFlags = ImGuiSelectableFlags,
ImGuiSelectionBasicStorage = ImGuiSelectionBasicStorage,
ImGuiSelectionExternalStorage = ImGuiSelectionExternalStorage,
ImGuiSelectionRequest = ImGuiSelectionRequest,
ImGuiSelectionRequestType = ImGuiSelectionRequestType,
ImGuiSelectionRequest_Copy = ImGuiSelectionRequest_Copy,
ImGuiSelectionUserData_Invalid = ImGuiSelectionUserData_Invalid,
ImGuiSeparatorFlags = ImGuiSeparatorFlags,
ImGuiSettingsCleanupArgs = ImGuiSettingsCleanupArgs,
ImGuiSettingsHandler = ImGuiSettingsHandler,
ImGuiShrinkWidthItem = ImGuiShrinkWidthItem,
ImGuiSliderFlags = ImGuiSliderFlags,
ImGuiSortDirection = ImGuiSortDirection,
ImGuiStackLevelInfo = ImGuiStackLevelInfo,
ImGuiStyle = ImGuiStyle,
ImGuiStyleMod = ImGuiStyleMod,
ImGuiStyleVar = ImGuiStyleVar,
ImGuiStyleVarInfo = ImGuiStyleVarInfo,
ImGuiStyle_Copy = ImGuiStyle_Copy,
ImGuiTabBar = ImGuiTabBar,
ImGuiTabBarFlags = ImGuiTabBarFlags,
ImGuiTabItem = ImGuiTabItem,
ImGuiTabItemFlags = ImGuiTabItemFlags,
ImGuiTable = ImGuiTable,
ImGuiTableBgTarget = ImGuiTableBgTarget,
ImGuiTableColumn = ImGuiTableColumn,
ImGuiTableColumnFlags = ImGuiTableColumnFlags,
ImGuiTableColumnSettings = ImGuiTableColumnSettings,
ImGuiTableColumnSortSpecs = ImGuiTableColumnSortSpecs,
ImGuiTableFlags = ImGuiTableFlags,
ImGuiTableInstanceData = ImGuiTableInstanceData,
ImGuiTableRowFlags = ImGuiTableRowFlags,
ImGuiTableSettings = ImGuiTableSettings,
ImGuiTableSortSpecs = ImGuiTableSortSpecs,
ImGuiTableTempData = ImGuiTableTempData,
ImGuiTextBuffer = ImGuiTextBuffer,
ImGuiTextFilter = ImGuiTextFilter,
ImGuiTextFilterItem = ImGuiTextFilterItem,
ImGuiTextFlags = ImGuiTextFlags,
ImGuiTooltipFlags = ImGuiTooltipFlags,
ImGuiTreeNodeFlags = ImGuiTreeNodeFlags,
ImGuiTypingSelectFlags = ImGuiTypingSelectFlags,
ImGuiTypingSelectState = ImGuiTypingSelectState,
ImGuiViewport = ImGuiViewport,
ImGuiViewportFlags = ImGuiViewportFlags,
ImGuiViewportP = ImGuiViewportP,
ImGuiWindow = ImGuiWindow,
ImGuiWindowBgClickFlags = ImGuiWindowBgClickFlags,
ImGuiWindowClass = ImGuiWindowClass,
ImGuiWindowDockStyleCol = ImGuiWindowDockStyleCol,
ImGuiWindowFlags = ImGuiWindowFlags,
ImGuiWindowRefreshFlags = ImGuiWindowRefreshFlags,
ImGuiWindowSettings = ImGuiWindowSettings,
ImGuiWindowStackData = ImGuiWindowStackData,
ImGui_ImplRoblox = ImGui_ImplRoblox,
ImGui_ImplRoblox_CreateTexture = ImGui_ImplRoblox_CreateTexture,
ImGui_ImplRoblox_DestroyTexture = ImGui_ImplRoblox_DestroyTexture,
ImGui_ImplRoblox_GetBackendData = ImGui_ImplRoblox_GetBackendData,
ImGui_ImplRoblox_Init = ImGui_ImplRoblox_Init,
ImGui_ImplRoblox_NewFrame = ImGui_ImplRoblox_NewFrame,
ImGui_ImplRoblox_RenderDrawData = ImGui_ImplRoblox_RenderDrawData,
ImGui_ImplRoblox_SetBusyRenderRate = ImGui_ImplRoblox_SetBusyRenderRate,
ImGui_ImplRoblox_SetOverlay = ImGui_ImplRoblox_SetOverlay,
ImGui_ImplRoblox_SetProfiling = ImGui_ImplRoblox_SetProfiling,
ImGui_ImplRoblox_SetRedrawBudget = ImGui_ImplRoblox_SetRedrawBudget,
ImGui_ImplRoblox_SetRenderRate = ImGui_ImplRoblox_SetRenderRate,
ImGui_ImplRoblox_SetRenderScale = ImGui_ImplRoblox_SetRenderScale,
ImGui_ImplRoblox_SetTextProxy = ImGui_ImplRoblox_SetTextProxy,
ImGui_ImplRoblox_Shutdown = ImGui_ImplRoblox_Shutdown,
ImGui_ImplRoblox_UpdateTexture = ImGui_ImplRoblox_UpdateTexture,
ImHashData = ImHashData,
ImHashStr = ImHashStr,
ImHashStrRaw = ImHashStrRaw,
ImInvLength = ImInvLength,
ImIsFloatAboveGuaranteedIntegerPrecision = ImIsFloatAboveGuaranteedIntegerPrecision,
ImIsPowerOfTwo = ImIsPowerOfTwo,
ImIsTruncated = ImIsTruncated,
ImIsTruncated4 = ImIsTruncated4,
ImLengthSqr = ImLengthSqr,
ImLerp = ImLerp,
ImLinearRemapClamp = ImLinearRemapClamp,
ImLog = ImLog,
ImMax = ImMax,
ImMemchr = ImMemchr,
ImMin = ImMin,
ImModPositive = ImModPositive,
ImMul = ImMul,
ImParseFormatFindStart = ImParseFormatFindStart,
ImParseFormatSanitizeForPrinting = ImParseFormatSanitizeForPrinting,
ImParseFormatSanitizeForScanning = ImParseFormatSanitizeForScanning,
ImPool = ImPool,
ImPow = ImPow,
ImRect = ImRect,
ImRect_Copy = ImRect_Copy,
ImRect_CopyFromV4 = ImRect_CopyFromV4,
ImRotate = ImRotate,
ImRound64 = ImRound64,
ImRoundPositive64 = ImRoundPositive64,
ImRoundSigned64 = ImRoundSigned64,
ImRsqrt = ImRsqrt,
ImRsqrtPrecise = ImRsqrtPrecise,
ImS16 = ImS16,
ImS32 = ImS32,
ImS8 = ImS8,
ImSaturate = ImSaturate,
ImSign = ImSign,
ImSin = ImSin,
ImSqrt = ImSqrt,
ImStb = ImStb,
ImStd = ImStd,
ImStrByte = ImStrByte,
ImSubClampOverflow = ImSubClampOverflow,
ImTextClassifierClear = ImTextClassifierClear,
ImTextClassifierGet = ImTextClassifierGet,
ImTextClassifierSetCharClass = ImTextClassifierSetCharClass,
ImTextClassifierSetCharClassFromStr = ImTextClassifierSetCharClassFromStr,
ImTextInitClassifiers = ImTextInitClassifiers,
ImTextureData = ImTextureData,
ImTextureDataGetFormatBytesPerPixel = ImTextureDataGetFormatBytesPerPixel,
ImTextureDataGetFormatName = ImTextureDataGetFormatName,
ImTextureDataGetStatusName = ImTextureDataGetStatusName,
ImTextureDataUpdateNewFrame = ImTextureDataUpdateNewFrame,
ImTextureFormat = ImTextureFormat,
ImTextureID_Invalid = ImTextureID_Invalid,
ImTextureRect = ImTextureRect,
ImTextureRef = ImTextureRef,
ImTextureStatus = ImTextureStatus,
ImToUpper = ImToUpper,
ImTriangleArea = ImTriangleArea,
ImTriangleIsClockwise = ImTriangleIsClockwise,
ImTrunc = ImTrunc,
ImTrunc64 = ImTrunc64,
ImU16 = ImU16,
ImU32 = ImU32,
ImU8 = ImU8,
ImUpperPowerOfTwo = ImUpperPowerOfTwo,
ImVec1_Copy = ImVec1_Copy,
ImVec2 = ImVec2,
ImVec2_AddVA = ImVec2_AddVA,
ImVec2_Copy = ImVec2_Copy,
ImVec2_CopyV = ImVec2_CopyV,
ImVec2_MulVX = ImVec2_MulVX,
ImVec2_SubVA = ImVec2_SubVA,
ImVec2i = ImVec2i,
ImVec2ih = ImVec2ih,
ImVec4 = ImVec4,
ImVec4_Copy = ImVec4_Copy,
ImVector = ImVector,
ImWcharClass = ImWcharClass,
MyDocument = MyDocument,
MyItem = MyItem,
MyItemColumnID_Action = MyItemColumnID_Action,
MyItemColumnID_Description = MyItemColumnID_Description,
MyItemColumnID_ID = MyItemColumnID_ID,
MyItemColumnID_Name = MyItemColumnID_Name,
MyItemColumnID_Quantity = MyItemColumnID_Quantity,
MyItem_CompareWithSortSpecs = MyItem_CompareWithSortSpecs,
MyItem_SortWithSortSpecs = MyItem_SortWithSortSpecs,
MyItem_s_current_sort_specs = MyItem_s_current_sort_specs,
NAV_ACTIVATE_HIGHLIGHT_TIMER = NAV_ACTIVATE_HIGHLIGHT_TIMER,
NAV_ACTIVATE_INPUT_WITH_GAMEPAD_DELAY = NAV_ACTIVATE_INPUT_WITH_GAMEPAD_DELAY,
NAV_WINDOWING_HIGHLIGHT_DELAY = NAV_WINDOWING_HIGHLIGHT_DELAY,
PopStyleCompact = PopStyleCompact,
PushStyleCompact = PushStyleCompact,
STB_TEXTEDIT_K_BACKSPACE = STB_TEXTEDIT_K_BACKSPACE,
STB_TEXTEDIT_K_DELETE = STB_TEXTEDIT_K_DELETE,
STB_TEXTEDIT_K_DOWN = STB_TEXTEDIT_K_DOWN,
STB_TEXTEDIT_K_LEFT = STB_TEXTEDIT_K_LEFT,
STB_TEXTEDIT_K_LINEEND = STB_TEXTEDIT_K_LINEEND,
STB_TEXTEDIT_K_LINESTART = STB_TEXTEDIT_K_LINESTART,
STB_TEXTEDIT_K_PGDOWN = STB_TEXTEDIT_K_PGDOWN,
STB_TEXTEDIT_K_PGUP = STB_TEXTEDIT_K_PGUP,
STB_TEXTEDIT_K_REDO = STB_TEXTEDIT_K_REDO,
STB_TEXTEDIT_K_RIGHT = STB_TEXTEDIT_K_RIGHT,
STB_TEXTEDIT_K_SHIFT = STB_TEXTEDIT_K_SHIFT,
STB_TEXTEDIT_K_TEXTEND = STB_TEXTEDIT_K_TEXTEND,
STB_TEXTEDIT_K_TEXTSTART = STB_TEXTEDIT_K_TEXTSTART,
STB_TEXTEDIT_K_UNDO = STB_TEXTEDIT_K_UNDO,
STB_TEXTEDIT_K_UP = STB_TEXTEDIT_K_UP,
STB_TEXTEDIT_K_WORDLEFT = STB_TEXTEDIT_K_WORDLEFT,
STB_TEXTEDIT_K_WORDRIGHT = STB_TEXTEDIT_K_WORDRIGHT,
STB_TEXTEDIT_NEWLINE = STB_TEXTEDIT_NEWLINE,
STB_TexteditState = STB_TexteditState,
ShowDockingDisabledMessage = ShowDockingDisabledMessage,
ShowExampleAppAssetsBrowser = ShowExampleAppAssetsBrowser,
ShowExampleAppAutoResize = ShowExampleAppAutoResize,
ShowExampleAppConsole = ShowExampleAppConsole,
ShowExampleAppConstrainedResize = ShowExampleAppConstrainedResize,
ShowExampleAppCustomRendering = ShowExampleAppCustomRendering,
ShowExampleAppDockSpace = ShowExampleAppDockSpace,
ShowExampleAppDockSpaceAdvanced = ShowExampleAppDockSpaceAdvanced,
ShowExampleAppDockSpaceBasic = ShowExampleAppDockSpaceBasic,
ShowExampleAppDocuments = ShowExampleAppDocuments,
ShowExampleAppFullscreen = ShowExampleAppFullscreen,
ShowExampleAppImageViewer = ShowExampleAppImageViewer,
ShowExampleAppLayout = ShowExampleAppLayout,
ShowExampleAppLog = ShowExampleAppLog,
ShowExampleAppLongText = ShowExampleAppLongText,
ShowExampleAppMainMenuBar = ShowExampleAppMainMenuBar,
ShowExampleAppPropertyEditor = ShowExampleAppPropertyEditor,
ShowExampleAppSimpleOverlay = ShowExampleAppSimpleOverlay,
ShowExampleAppWindowTitles = ShowExampleAppWindowTitles,
ShowExampleMenuFile = ShowExampleMenuFile,
ShowTableColumnsStatusFlags = ShowTableColumnsStatusFlags,
TILE_SIZE = TILE_SIZE,
UINT_MAX = UINT_MAX,
VTX_STRIDE = VTX_STRIDE,
_PathArcToFastExXY = _PathArcToFastExXY,
stb_decompress = stb_decompress,
stb_decompress_length = stb_decompress_length,
stb_text_locate_coord = stb_text_locate_coord,
stb_text_makeundo_delete = stb_text_makeundo_delete,
stb_text_makeundo_insert = stb_text_makeundo_insert,
stb_text_makeundo_replace = stb_text_makeundo_replace,
stb_text_redo = stb_text_redo,
stb_text_undo = stb_text_undo,
stb_textedit_click = stb_textedit_click,
stb_textedit_drag = stb_textedit_drag,
stb_textedit_find_charpos = stb_textedit_find_charpos,
stb_textedit_key = stb_textedit_key,
stbrp_init_target = stbrp_init_target,
stbte = stbte,
stbtt_FindGlyphIndex = stbtt_FindGlyphIndex,
stbtt_GetFontOffsetForIndex = stbtt_GetFontOffsetForIndex,
stbtt_GetFontVMetrics = stbtt_GetFontVMetrics,
stbtt_GetGlyphBitmapBox = stbtt_GetGlyphBitmapBox,
stbtt_GetGlyphBitmapBoxSubpixel = stbtt_GetGlyphBitmapBoxSubpixel,
stbtt_GetGlyphHMetrics = stbtt_GetGlyphHMetrics,
stbtt_GetGlyphShape = stbtt_GetGlyphShape,
stbtt_InitFont = stbtt_InitFont,
stbtt_MakeGlyphBitmapSubpixelPrefilter = stbtt_MakeGlyphBitmapSubpixelPrefilter,
stbtt_ScaleForPixelHeight = stbtt_ScaleForPixelHeight,
stbtt__GetGlyphInfoT2 = stbtt__GetGlyphInfoT2,
stbtt__GetGlyphShapeT2 = stbtt__GetGlyphShapeT2,
stbtt_fontinfo = stbtt_fontinfo,
}