11 Commits
Author SHA1 Message Date
4DBug 1431d2e19d 0.1.3
Build & Release / build (push) Waiting to run
2026-10-14 06:26:28 -05:00
4DBug 7eb7d5fcbb Docking: set node.LastBgColor (closing dock hosts drew white bodies), host skips own bg, fix SetNextWindowBgAlpha; Roblox backend: frames skipped by the render rate cap are drawn later (UI could freeze when the app stopped rendering); t_dock_close test 2026-10-14 06:26:28 -05:00
4DBug 2558c30d32 0.1.2
Build & Release / build (push) Canceled after 0s
2026-10-09 02:24:21 -05:00
4DBug e228ed8638 Roblox backend: text proxy is opt-in (SetTextProxy), remove SetTextProxyDebug; new example main.client.luau 2026-10-09 02:22:30 -05:00
4DBug 28dc08e043 README: move 'How it works' below Getting Started
Build & Release / build (push) Canceled after 0s
2026-10-08 20:57:31 -05:00
4DBug f63a37f07d README: Wally package link; 0.1.1
Build & Release / build (push) Canceled after 0s
2026-10-08 20:54:31 -05:00
4DBug 08356b4f1f Wally: scope 4dbug (must match the GitHub account)
Build & Release / build (push) Canceled after 0s
2026-10-08 20:52:52 -05:00
4DBug 14e6da3b29 SetNextWindowCached(key): opt-in cached windows on top of the refresh policy (skip contents while idle), t_cached_window test, README snippet
Build & Release / build (push) Canceled after 0s
2026-10-08 20:43:56 -05:00
4DBug df4cf4cd80 Luau types: generated typed entry module build/ImGuiTyped.luau (requires the bundle as child Impl); Rojo/Wally use it 2026-10-08 20:36:27 -05:00
4DBug 2bef2ad43a Wally package (bug/imgui-luau), published by CI on v* tags 2026-10-08 20:30:22 -05:00
4DBug df9f194d87 README: AI disclosure wording; .gitignore 2026-10-08 20:29:29 -05:00
23 changed files with 2024 additions and 392 deletions
+40 -24
View File
@@ -10,7 +10,7 @@ Dear ImGui for Roblox (and more)
A Lua/Luau port of [Dear ImGui](https://github.com/ocornut/imgui) (docking branch, 1.93 WIP). **Roblox is the primary target** (rendered with EditableImages); the same sources also run on [LÖVE](https://love2d.org) (LuaJIT, GPU renderer), and the backend layout leaves room for more. It follows the C++ source function by function, so upstream documentation, examples and the demo apply almost directly.
| [The Pitch](#the-pitch) - [Usage](#usage) - [How it works](#how-it-works) - [Demo](#demo) - [Getting Started & Integration](#getting-started--integration) - [Backends](#backends) |
| [The Pitch](#the-pitch) - [Usage](#usage) - [Demo](#demo) - [Getting Started & Integration](#getting-started--integration-roblox) - [How it works](#how-it-works-roblox-backend) - [Backends](#backends) |
:----------------------------------------------------------: |
| [Differences from C++](#differences-from-c-dear-imgui) - [Performance](#performance) - [Testing](#testing) - [Not ported yet](#not-ported-yet) |
| [FAQ](#faq) - [Credits](#credits) - [License](#license) |
@@ -79,27 +79,6 @@ Dear ImGui allows you to **create elaborate tools** as well as very short-lived
Every snippet in this README is run by [`tools/test/scripts/t_readme.luau`](/tools/test/scripts/t_readme.luau), and every image was rendered by this port with [`tools/screenshots/make.py`](/tools/screenshots/make.py).
### How it works (Roblox backend)
Dear ImGui itself never draws anything: every frame it produces `ImDrawData`, a list of textured, coloured triangles plus clip rectangles, and expects the backend to put them on screen. On desktop that is a few GPU draw calls. Roblox has no API to draw arbitrary triangles in 2D, so the Roblox backend ([backends/imgui_impl_roblox.lua](/backends/imgui_impl_roblox.lua)) does what a GPU would do, in Luau, and pushes the result into **EditableImages**.
**Why software rasterization?** The alternatives don't fit Dear ImGui:
- *One GUI Instance per widget/shape* (what most Roblox UI libraries do) means re-creating, re-parenting or diffing thousands of Instances every frame, can't express anti-aliased curves, per-vertex gradients or font-atlas glyphs, and would no longer be Dear ImGui's renderer output.
- *Rasterizing the draw data* keeps the output identical to Dear ImGui's: the same fonts, anti-aliasing, rounding and gradients, and every widget (including custom `ImDrawList` drawing) works without backend support.
The cost is CPU time in Luau, so the backend is built around **not drawing**:
1. **Layers per window.** Every draw list (one per top-level window, popup, tooltip) gets its own `Frame` with `ZIndex` following Dear ImGui's draw order. Its content is rasterized relative to the window, so **moving a window only changes the Frame's `Position`**; no pixel is redrawn or uploaded.
2. **Tiles.** Each layer is covered by 128×128 EditableImage tiles (`ImageLabel`s). Every primitive is hashed (positions relative to the layer, UVs, colours, texture, clip) into the tiles it touches. **Only tiles whose hash changed are re-rasterized and uploaded**, and only the dirty region of those (`WritePixelsBuffer` with a sub-rectangle). A static UI uploads nothing.
3. **Scrolling reuses pixels.** Window content is a separate layer inside a `ClipsDescendants` frame sized to the window's visible area, positioned at the scroll offset. Scrolling moves it like dragging moves a window; only rows entering view are drawn. Decorations (title bar, borders, scrollbar) stay in their own layers.
4. **Skipping unchanged windows.** Vertices are stored in a flat Luau `buffer` (36 bytes each) instead of tables. Before any per-tile work, a window's raw vertex/index bytes are compared with last frame's; if identical, its layers are just restacked.
5. **Fast rasterizer.** `@native` functions; axis-aligned quads (most rects and every glyph) take a span-fill path, flat rows are copied instead of re-blended, gradient rects (color pickers) are filled per row, and only real triangles (anti-aliased edges, curves) use the generic edge-function rasterizer.
6. **Render rate.** Tiles update at most `SetRenderRate(hz)` times per second (default 60), optionally lower while scrolling/resizing (`SetBusyRenderRate`). Your UI code and Dear ImGui's logic still run every frame, so input is never dropped.
**Input.** Mouse and keys come from `UserInputService`. While the mouse is over Dear ImGui (`io.WantCaptureMouse`), the tiles are `Active`, so clicks and the wheel don't reach the game (camera zoom). Games can't read the clipboard, so while a text field is active a hidden `TextBox` holds focus: the OS delivers typing (any layout), paste and copy/cut through it, and its text mirrors Dear ImGui's current selection. Fonts are TTFs rasterized by the ported stb_truetype into the font atlas, which becomes a backend texture like any image (`CreateTexture` works for your own pixels too).
**Instances inside windows.** The backend can place your own Instances (e.g. a `ViewportFrame`) inside a window's layer, so they stack, clip and move with it; see the `ImGui.Embed` widget in [examples/example_roblox/main.client.luau](/examples/example_roblox/main.client.luau).
### Demo
Calling `ImGui.ShowDemoWindow()` creates a demo window that showcases a variety of features and examples. The code is in [imgui_demo.lua](/imgui_demo.lua), like upstream's `imgui_demo.cpp`. **It's strongly recommended to keep it at hand as a reference**: you can find the code for any widget you see by searching for its label.
@@ -137,7 +116,9 @@ The Style Editor (`ImGui.ShowStyleEditor()`) and the Metrics/Debugger (`ImGui.Sh
### Getting Started & Integration (Roblox)
1. Build the module: `python3 tools/bundle.py` writes `build/ImGui.luau` (`--no-demo` leaves the demo out).
With [Wally](https://wally.run) ([4dbug/imgui-luau](https://wally.run/package/4dbug/imgui-luau)): add `ImGui = "4dbug/[email protected]"` under `[dependencies]` in your `wally.toml`, run `wally install`, then `require(ReplicatedStorage.Packages.ImGui)`. Or from source:
1. Build the module: `python3 tools/bundle.py` writes `build/ImGui.luau` (`--no-demo` leaves the demo out) and `build/ImGuiTyped.luau`, a small typed entry module (Luau types for the public `ImGui.*` API, `ImVec2`/`ImVec4` and the flag enums) that requires `build/ImGui.luau` as its child `Impl`. The Wally package and the Rojo projects use that layout, so you get autocomplete and type errors in `--!strict` scripts. The single-file `ImGui.luau` also works on its own, just untyped.
2. Sync with [Rojo](https://rojo.space): [`default.project.json`](/default.project.json) puts the module in `ReplicatedStorage.ImGui` and the example LocalScript in `StarterPlayerScripts`.
3. A minimal LocalScript (full version: [examples/example_roblox/main.client.luau](/examples/example_roblox/main.client.luau)):
@@ -182,6 +163,27 @@ io.Fonts:AddFontDefault()
io.Fonts:AddFontFromFileTTF("fonts/Roboto-Medium.ttf", 16.0)
```
### How it works (Roblox backend)
Dear ImGui itself never draws anything: every frame it produces `ImDrawData`, a list of textured, coloured triangles plus clip rectangles, and expects the backend to put them on screen. On desktop that is a few GPU draw calls. Roblox has no API to draw arbitrary triangles in 2D, so the Roblox backend ([backends/imgui_impl_roblox.lua](/backends/imgui_impl_roblox.lua)) does what a GPU would do, in Luau, and pushes the result into **EditableImages**.
**Why software rasterization?** The alternatives don't fit Dear ImGui:
- *One GUI Instance per widget/shape* (what most Roblox UI libraries do) means re-creating, re-parenting or diffing thousands of Instances every frame, can't express anti-aliased curves, per-vertex gradients or font-atlas glyphs, and would no longer be Dear ImGui's renderer output.
- *Rasterizing the draw data* keeps the output identical to Dear ImGui's: the same fonts, anti-aliasing, rounding and gradients, and every widget (including custom `ImDrawList` drawing) works without backend support.
The cost is CPU time in Luau, so the backend is built around **not drawing**:
1. **Layers per window.** Every draw list (one per top-level window, popup, tooltip) gets its own `Frame` with `ZIndex` following Dear ImGui's draw order. Its content is rasterized relative to the window, so **moving a window only changes the Frame's `Position`**; no pixel is redrawn or uploaded.
2. **Tiles.** Each layer is covered by 128×128 EditableImage tiles (`ImageLabel`s). Every primitive is hashed (positions relative to the layer, UVs, colours, texture, clip) into the tiles it touches. **Only tiles whose hash changed are re-rasterized and uploaded**, and only the dirty region of those (`WritePixelsBuffer` with a sub-rectangle). A static UI uploads nothing.
3. **Scrolling reuses pixels.** Window content is a separate layer inside a `ClipsDescendants` frame sized to the window's visible area, positioned at the scroll offset. Scrolling moves it like dragging moves a window; only rows entering view are drawn. Decorations (title bar, borders, scrollbar) stay in their own layers.
4. **Skipping unchanged windows.** Vertices are stored in a flat Luau `buffer` (36 bytes each) instead of tables. Before any per-tile work, a window's raw vertex/index bytes are compared with last frame's; if identical, its layers are just restacked.
5. **Fast rasterizer.** `@native` functions; axis-aligned quads (most rects and every glyph) take a span-fill path, flat rows are copied instead of re-blended, gradient rects (color pickers) are filled per row, and only real triangles (anti-aliased edges, curves) use the generic edge-function rasterizer.
6. **Render rate.** Tiles update at most `SetRenderRate(hz)` times per second (default 60), optionally lower while scrolling/resizing (`SetBusyRenderRate`). Your UI code and Dear ImGui's logic still run every frame, so input is never dropped.
**Input.** Mouse and keys come from `UserInputService`. While the mouse is over Dear ImGui (`io.WantCaptureMouse`), the tiles are `Active`, so clicks and the wheel don't reach the game (camera zoom). Typing comes from key codes by default (printable ASCII). Call `ImGui_ImplRoblox.SetTextProxy(true)` to opt in to a hidden `TextBox` that holds focus while a text field is active: the OS then delivers typing (any layout), paste and copy/cut through it, and its text mirrors Dear ImGui's current selection. Fonts are TTFs rasterized by the ported stb_truetype into the font atlas, which becomes a backend texture like any image (`CreateTexture` works for your own pixels too).
**Instances inside windows.** The backend can place your own Instances (e.g. a `ViewportFrame`) inside a window's layer, so they stack, clip and move with it; see the `ImGui.Embed` widget in [examples/example_roblox/main.client.luau](/examples/example_roblox/main.client.luau).
### Backends
| Backend | File | Build | Renderer | Example |
@@ -242,6 +244,20 @@ Measured headless (Luau CLI, Studio-like native code generation), demo window op
Most of the remaining cost is widget logic (your UI code and Dear ImGui's per-widget work), which runs every frame by design. Use `ImGuiListClipper` for long lists.
Windows that rarely change can opt in to caching: `ImGui.SetNextWindowCached(key)` keeps last frame's drawing and makes `Begin` return `visible = false`, so your code for that window doesn't run. It refreshes when `key` changes, while the window is hovered, focused or active (plus one frame after), and when it appears, moves, resizes, scrolls, collapses or is docked. An idle 200-widget window goes from ~2.2 ms to ~0.15 ms per frame.
```lua
local stats = { version = 0, kills = 0 }
local function StatsWindow()
ImGui.SetNextWindowCached(stats.version) -- bump stats.version whenever stats change
local _, visible = ImGui.Begin("Stats")
if visible then -- false while the cached contents are reused
ImGui.Text("Kills: %d", stats.kills)
end
ImGui.End()
end
```
### Testing
`tools/test/run.py` runs any LocalScript headless against a mocked Roblox (`tools/test/roblox_mock.luau`):
@@ -283,7 +299,7 @@ Multi-select / box-select, typing-select, the Assets Browser example, the Item P
### AI disclosure
AI tools were used to assist in making this project (porting code, the Roblox and LÖVE backends, optimizations, tests and documentation). All changes were reviewed and tested by a human, and correctness is checked by the test suite (draw-data checksums against the reference behaviour, pixel comparisons, behaviour tests). Bug reports are welcome.
AI tools were used to assist in making this project (porting code, optimizations, tests and documentation). All changes were reviewed and tested by a human, and correctness is checked by the test suite (draw-data checksums against the reference behaviour, pixel comparisons, behaviour tests). Bug reports are welcome.
### Credits
+18 -1
View File
@@ -42,10 +42,13 @@ jobs:
python3 tools/sync_github_readme.py --check
luau-compile --null dist/roblox/ImGui.luau
luau-compile --null dist/roblox-nodemo/ImGui.luau
sed 's|require(script:WaitForChild("Impl"))|nil|' dist/roblox/ImGuiTyped.luau > /tmp/ImGuiTyped.luau && luau-analyze /tmp/ImGuiTyped.luau
luajit -bl dist/love/imgui_love.lua > /dev/null
luajit -bl dist/love-nodemo/imgui_love.lua > /dev/null
python3 tools/test/run.py --main tools/test/scripts/t_readme.luau --frames 30 --quiet --roblox --timeout 300 | tee /tmp/r.txt
grep -q "README OK" /tmp/r.txt
python3 tools/test/run.py --main tools/test/scripts/t_cached_window.luau --frames 130 --quiet --roblox --timeout 300 | tee /tmp/c.txt
grep -q "CACHED OK" /tmp/c.txt
python3 tools/bundle.py --target love >/dev/null
luajit tools/test/love/t_drawhash.lua 60 Widgets,Basic
luajit tools/test/love/t_examples.lua
@@ -59,7 +62,7 @@ jobs:
cp love/imgui_love.lua imgui_love.lua
cp love-nodemo/imgui_love.lua imgui_love-nodemo.lua
# Roblox: module + example LocalScript + Rojo project
mkdir -p pkg-roblox/build && cp ImGui.luau pkg-roblox/build/ && cp ../default.project.json pkg-roblox/
mkdir -p pkg-roblox/build && cp ImGui.luau roblox/ImGuiTyped.luau pkg-roblox/build/ && cp ../default.project.json pkg-roblox/
mkdir -p pkg-roblox/examples && cp -r ../examples/example_roblox ../examples/example_shared pkg-roblox/examples/
(cd pkg-roblox && zip -qr ../imgui-luau-roblox-$V.zip .)
# LÖVE: bundle + fonts + examples (run: love examples/example_love)
@@ -80,6 +83,20 @@ jobs:
dist/*.zip
generate_release_notes: true
- name: Publish to Wally (tag)
if: startsWith(github.ref, 'refs/tags/v')
env:
WALLY_TOKEN: ${{ secrets.WALLY_TOKEN }}
run: |
[ -n "$WALLY_TOKEN" ] || { echo "WALLY_TOKEN not set, skipping"; exit 0; }
grep -q "^version = \"${GITHUB_REF_NAME#v}\"" wally.toml || { echo "wally.toml version != tag"; exit 1; }
curl -fsSL -o wally.zip https://github.com/UpliftGames/wally/releases/download/v0.3.2/wally-v0.3.2-linux.zip
unzip -q wally.zip -d wallybin && chmod +x wallybin/wally
mkdir -p dist/wally/src && cp wally.toml dist/wally/ && cp dist/roblox/ImGuiTyped.luau dist/wally/src/init.luau && cp dist/roblox/ImGui.luau dist/wally/src/Impl.luau
echo '{"name":"imgui-luau","tree":{"$path":"src"}}' > dist/wally/default.project.json
wallybin/wally login --token "$WALLY_TOKEN"
wallybin/wally publish --project-path dist/wally
- name: Release (latest)
if: "!startsWith(github.ref, 'refs/tags/v')"
env:
+4
View File
@@ -0,0 +1,4 @@
.opencode/
Packages/
wally.lock
__pycache__/
+40 -24
View File
@@ -7,7 +7,7 @@ Dear ImGui for Roblox (and more)
A Lua/Luau port of [Dear ImGui](https://github.com/ocornut/imgui) (docking branch, 1.93 WIP). **Roblox is the primary target** (rendered with EditableImages); the same sources also run on [LÖVE](https://love2d.org) (LuaJIT, GPU renderer), and the backend layout leaves room for more. It follows the C++ source function by function, so upstream documentation, examples and the demo apply almost directly.
| [The Pitch](#the-pitch) - [Usage](#usage) - [How it works](#how-it-works) - [Demo](#demo) - [Getting Started & Integration](#getting-started--integration) - [Backends](#backends) |
| [The Pitch](#the-pitch) - [Usage](#usage) - [Demo](#demo) - [Getting Started & Integration](#getting-started--integration-roblox) - [How it works](#how-it-works-roblox-backend) - [Backends](#backends) |
:----------------------------------------------------------: |
| [Differences from C++](#differences-from-c-dear-imgui) - [Performance](#performance) - [Testing](#testing) - [Not ported yet](#not-ported-yet) |
| [FAQ](#faq) - [Credits](#credits) - [License](#license) |
@@ -76,27 +76,6 @@ Dear ImGui allows you to **create elaborate tools** as well as very short-lived
Every snippet in this README is run by [`tools/test/scripts/t_readme.luau`](tools/test/scripts/t_readme.luau), and every image was rendered by this port with [`tools/screenshots/make.py`](tools/screenshots/make.py).
### How it works (Roblox backend)
Dear ImGui itself never draws anything: every frame it produces `ImDrawData`, a list of textured, coloured triangles plus clip rectangles, and expects the backend to put them on screen. On desktop that is a few GPU draw calls. Roblox has no API to draw arbitrary triangles in 2D, so the Roblox backend ([backends/imgui_impl_roblox.lua](backends/imgui_impl_roblox.lua)) does what a GPU would do, in Luau, and pushes the result into **EditableImages**.
**Why software rasterization?** The alternatives don't fit Dear ImGui:
- *One GUI Instance per widget/shape* (what most Roblox UI libraries do) means re-creating, re-parenting or diffing thousands of Instances every frame, can't express anti-aliased curves, per-vertex gradients or font-atlas glyphs, and would no longer be Dear ImGui's renderer output.
- *Rasterizing the draw data* keeps the output identical to Dear ImGui's: the same fonts, anti-aliasing, rounding and gradients, and every widget (including custom `ImDrawList` drawing) works without backend support.
The cost is CPU time in Luau, so the backend is built around **not drawing**:
1. **Layers per window.** Every draw list (one per top-level window, popup, tooltip) gets its own `Frame` with `ZIndex` following Dear ImGui's draw order. Its content is rasterized relative to the window, so **moving a window only changes the Frame's `Position`**; no pixel is redrawn or uploaded.
2. **Tiles.** Each layer is covered by 128×128 EditableImage tiles (`ImageLabel`s). Every primitive is hashed (positions relative to the layer, UVs, colours, texture, clip) into the tiles it touches. **Only tiles whose hash changed are re-rasterized and uploaded**, and only the dirty region of those (`WritePixelsBuffer` with a sub-rectangle). A static UI uploads nothing.
3. **Scrolling reuses pixels.** Window content is a separate layer inside a `ClipsDescendants` frame sized to the window's visible area, positioned at the scroll offset. Scrolling moves it like dragging moves a window; only rows entering view are drawn. Decorations (title bar, borders, scrollbar) stay in their own layers.
4. **Skipping unchanged windows.** Vertices are stored in a flat Luau `buffer` (36 bytes each) instead of tables. Before any per-tile work, a window's raw vertex/index bytes are compared with last frame's; if identical, its layers are just restacked.
5. **Fast rasterizer.** `@native` functions; axis-aligned quads (most rects and every glyph) take a span-fill path, flat rows are copied instead of re-blended, gradient rects (color pickers) are filled per row, and only real triangles (anti-aliased edges, curves) use the generic edge-function rasterizer.
6. **Render rate.** Tiles update at most `SetRenderRate(hz)` times per second (default 60), optionally lower while scrolling/resizing (`SetBusyRenderRate`). Your UI code and Dear ImGui's logic still run every frame, so input is never dropped.
**Input.** Mouse and keys come from `UserInputService`. While the mouse is over Dear ImGui (`io.WantCaptureMouse`), the tiles are `Active`, so clicks and the wheel don't reach the game (camera zoom). Games can't read the clipboard, so while a text field is active a hidden `TextBox` holds focus: the OS delivers typing (any layout), paste and copy/cut through it, and its text mirrors Dear ImGui's current selection. Fonts are TTFs rasterized by the ported stb_truetype into the font atlas, which becomes a backend texture like any image (`CreateTexture` works for your own pixels too).
**Instances inside windows.** The backend can place your own Instances (e.g. a `ViewportFrame`) inside a window's layer, so they stack, clip and move with it; see the `ImGui.Embed` widget in [examples/example_roblox/main.client.luau](examples/example_roblox/main.client.luau).
### Demo
Calling `ImGui.ShowDemoWindow()` creates a demo window that showcases a variety of features and examples. The code is in [imgui_demo.lua](imgui_demo.lua), like upstream's `imgui_demo.cpp`. **It's strongly recommended to keep it at hand as a reference**: you can find the code for any widget you see by searching for its label.
@@ -134,7 +113,9 @@ The Style Editor (`ImGui.ShowStyleEditor()`) and the Metrics/Debugger (`ImGui.Sh
### Getting Started & Integration (Roblox)
1. Build the module: `python3 tools/bundle.py` writes `build/ImGui.luau` (`--no-demo` leaves the demo out).
With [Wally](https://wally.run) ([4dbug/imgui-luau](https://wally.run/package/4dbug/imgui-luau)): add `ImGui = "4dbug/[email protected]"` under `[dependencies]` in your `wally.toml`, run `wally install`, then `require(ReplicatedStorage.Packages.ImGui)`. Or from source:
1. Build the module: `python3 tools/bundle.py` writes `build/ImGui.luau` (`--no-demo` leaves the demo out) and `build/ImGuiTyped.luau`, a small typed entry module (Luau types for the public `ImGui.*` API, `ImVec2`/`ImVec4` and the flag enums) that requires `build/ImGui.luau` as its child `Impl`. The Wally package and the Rojo projects use that layout, so you get autocomplete and type errors in `--!strict` scripts. The single-file `ImGui.luau` also works on its own, just untyped.
2. Sync with [Rojo](https://rojo.space): [`default.project.json`](default.project.json) puts the module in `ReplicatedStorage.ImGui` and the example LocalScript in `StarterPlayerScripts`.
3. A minimal LocalScript (full version: [examples/example_roblox/main.client.luau](examples/example_roblox/main.client.luau)):
@@ -179,6 +160,27 @@ io.Fonts:AddFontDefault()
io.Fonts:AddFontFromFileTTF("fonts/Roboto-Medium.ttf", 16.0)
```
### How it works (Roblox backend)
Dear ImGui itself never draws anything: every frame it produces `ImDrawData`, a list of textured, coloured triangles plus clip rectangles, and expects the backend to put them on screen. On desktop that is a few GPU draw calls. Roblox has no API to draw arbitrary triangles in 2D, so the Roblox backend ([backends/imgui_impl_roblox.lua](backends/imgui_impl_roblox.lua)) does what a GPU would do, in Luau, and pushes the result into **EditableImages**.
**Why software rasterization?** The alternatives don't fit Dear ImGui:
- *One GUI Instance per widget/shape* (what most Roblox UI libraries do) means re-creating, re-parenting or diffing thousands of Instances every frame, can't express anti-aliased curves, per-vertex gradients or font-atlas glyphs, and would no longer be Dear ImGui's renderer output.
- *Rasterizing the draw data* keeps the output identical to Dear ImGui's: the same fonts, anti-aliasing, rounding and gradients, and every widget (including custom `ImDrawList` drawing) works without backend support.
The cost is CPU time in Luau, so the backend is built around **not drawing**:
1. **Layers per window.** Every draw list (one per top-level window, popup, tooltip) gets its own `Frame` with `ZIndex` following Dear ImGui's draw order. Its content is rasterized relative to the window, so **moving a window only changes the Frame's `Position`**; no pixel is redrawn or uploaded.
2. **Tiles.** Each layer is covered by 128×128 EditableImage tiles (`ImageLabel`s). Every primitive is hashed (positions relative to the layer, UVs, colours, texture, clip) into the tiles it touches. **Only tiles whose hash changed are re-rasterized and uploaded**, and only the dirty region of those (`WritePixelsBuffer` with a sub-rectangle). A static UI uploads nothing.
3. **Scrolling reuses pixels.** Window content is a separate layer inside a `ClipsDescendants` frame sized to the window's visible area, positioned at the scroll offset. Scrolling moves it like dragging moves a window; only rows entering view are drawn. Decorations (title bar, borders, scrollbar) stay in their own layers.
4. **Skipping unchanged windows.** Vertices are stored in a flat Luau `buffer` (36 bytes each) instead of tables. Before any per-tile work, a window's raw vertex/index bytes are compared with last frame's; if identical, its layers are just restacked.
5. **Fast rasterizer.** `@native` functions; axis-aligned quads (most rects and every glyph) take a span-fill path, flat rows are copied instead of re-blended, gradient rects (color pickers) are filled per row, and only real triangles (anti-aliased edges, curves) use the generic edge-function rasterizer.
6. **Render rate.** Tiles update at most `SetRenderRate(hz)` times per second (default 60), optionally lower while scrolling/resizing (`SetBusyRenderRate`). Your UI code and Dear ImGui's logic still run every frame, so input is never dropped.
**Input.** Mouse and keys come from `UserInputService`. While the mouse is over Dear ImGui (`io.WantCaptureMouse`), the tiles are `Active`, so clicks and the wheel don't reach the game (camera zoom). Typing comes from key codes by default (printable ASCII). Call `ImGui_ImplRoblox.SetTextProxy(true)` to opt in to a hidden `TextBox` that holds focus while a text field is active: the OS then delivers typing (any layout), paste and copy/cut through it, and its text mirrors Dear ImGui's current selection. Fonts are TTFs rasterized by the ported stb_truetype into the font atlas, which becomes a backend texture like any image (`CreateTexture` works for your own pixels too).
**Instances inside windows.** The backend can place your own Instances (e.g. a `ViewportFrame`) inside a window's layer, so they stack, clip and move with it; see the `ImGui.Embed` widget in [examples/example_roblox/main.client.luau](examples/example_roblox/main.client.luau).
### Backends
| Backend | File | Build | Renderer | Example |
@@ -239,6 +241,20 @@ Measured headless (Luau CLI, Studio-like native code generation), demo window op
Most of the remaining cost is widget logic (your UI code and Dear ImGui's per-widget work), which runs every frame by design. Use `ImGuiListClipper` for long lists.
Windows that rarely change can opt in to caching: `ImGui.SetNextWindowCached(key)` keeps last frame's drawing and makes `Begin` return `visible = false`, so your code for that window doesn't run. It refreshes when `key` changes, while the window is hovered, focused or active (plus one frame after), and when it appears, moves, resizes, scrolls, collapses or is docked. An idle 200-widget window goes from ~2.2 ms to ~0.15 ms per frame.
```lua
local stats = { version = 0, kills = 0 }
local function StatsWindow()
ImGui.SetNextWindowCached(stats.version) -- bump stats.version whenever stats change
local _, visible = ImGui.Begin("Stats")
if visible then -- false while the cached contents are reused
ImGui.Text("Kills: %d", stats.kills)
end
ImGui.End()
end
```
### Testing
`tools/test/run.py` runs any LocalScript headless against a mocked Roblox (`tools/test/roblox_mock.luau`):
@@ -280,7 +296,7 @@ Multi-select / box-select, typing-select, the Assets Browser example, the Item P
### AI disclosure
AI tools were used to assist in making this project (porting code, the Roblox and LÖVE backends, optimizations, tests and documentation). All changes were reviewed and tested by a human, and correctness is checked by the test suite (draw-data checksums against the reference behaviour, pixel comparisons, behaviour tests). Bug reports are welcome.
AI tools were used to assist in making this project (porting code, optimizations, tests and documentation). All changes were reviewed and tested by a human, and correctness is checked by the test suite (draw-data checksums against the reference behaviour, pixel comparisons, behaviour tests). Bug reports are welcome.
### Credits
+39 -19
View File
@@ -862,7 +862,8 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
end
if rate > 0 then
local now = os.clock()
if now + 0.002 < bd.NextRender then table.clear(bd.Overlays); PE(); return end
-- 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
@@ -1127,8 +1128,33 @@ function ImGui_ImplRoblox_Init(parent, render_scale)
end
end)
-- Text proxy: 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 the proxy's text = imgui's current selection).
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)
end
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
@@ -1140,12 +1166,11 @@ function ImGui_ImplRoblox_Init(parent, render_scale)
proxy.ClearTextOnFocus = false
proxy.TextEditable = true
proxy.Text = ""
proxy.Parent = gui
bd.Proxy, bd.ProxyText, bd.PendingChars = proxy, "", {}
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.)
connect(proxy.FocusLost, function()
if bd.ProxyDebug then print("[ImGui text proxy] FocusLost, mouse over imgui:", ImGui.GetIO().WantCaptureMouse) end
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.
@@ -1154,9 +1179,6 @@ function ImGui_ImplRoblox_Init(parent, render_scale)
local g = ImGui.GetCurrentContext()
if g and g.ActiveId ~= 0 and g.ActiveId == bd.ProxyActiveId then ImGui.ClearActiveID() end
end)
connect(UserInputService.WindowFocused, function() ImGui.GetIO():AddFocusEvent(true) end)
connect(UserInputService.WindowFocusReleased, function() ImGui.GetIO():AddFocusEvent(false) end)
end
function ImGui_ImplRoblox_Shutdown()
@@ -1177,6 +1199,7 @@ 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)
local screen = bd.Gui.AbsoluteSize
local w, h = floor(screen.X * bd.Scale), floor(screen.Y * bd.Scale)
@@ -1201,15 +1224,16 @@ function ImGui_ImplRoblox_NewFrame()
-- otherwise the KeyCode fallback keeps typing working.
local proxy = bd.Proxy
local g = ImGui.GetCurrentContext()
local focused_box = UserInputService:GetFocusedTextBox()
if io.WantTextInput and g.ActiveId ~= 0 then
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()
if bd.ProxyDebug then print("[ImGui text proxy] CaptureFocus ->", focused_box == proxy and "focused" or ("FAILED, focused = " .. tostring(focused_box))) end
end
elseif focused_box == proxy then
bd.ProxyReleasing = true
@@ -1217,13 +1241,11 @@ function ImGui_ImplRoblox_NewFrame()
bd.ProxyReleasing = false
focused_box = nil
end
local was_focused = proxy_focused
proxy_focused = focused_box == proxy
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 bd.ProxyDebug then print("[ImGui text proxy] text", string.format("%q", text), "was", string.format("%q", tostring(bd.ProxyText))) end
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
@@ -1259,7 +1281,6 @@ function ImGui_ImplRoblox_NewFrame()
proxy.Text = sel
bd.ProxyLastRaw = nil
bd.ProxyText = sel
if bd.ProxyDebug then print("[ImGui text proxy] mirror selection", string.format("%q", sel)) end
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
@@ -1275,7 +1296,6 @@ function ImGui_ImplRoblox_NewFrame()
if not used_proxy_text then for _, c in ipairs(pending) do io:AddInputCharacter(c) end end
table.clear(pending)
end
if bd.ProxyDebug and was_focused ~= proxy_focused then print("[ImGui text proxy] proxy_focused =", proxy_focused) 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
@@ -1317,7 +1337,7 @@ ImGui_ImplRoblox = {
SetRenderScale = ImGui_ImplRoblox_SetRenderScale,
SetRenderRate = ImGui_ImplRoblox_SetRenderRate,
SetOverlay = ImGui_ImplRoblox_SetOverlay,
SetTextProxyDebug = function(on) ImGui_ImplRoblox_GetBackendData().ProxyDebug = on end, -- prints focus/text events
SetTextProxy = ImGui_ImplRoblox_SetTextProxy,
CreateTexture = ImGui_ImplRoblox_CreateTexture,
DestroyTexture = ImGui_ImplRoblox_DestroyTexture,
GetRenderScale = function() return ImGui_ImplRoblox_GetBackendData().Scale end,
+1 -1
View File
@@ -1 +1 @@
[[12, "imgui.lua"], [18472, "imgui_demo.lua"], [25757, "imgui_draw.lua"], [30995, "imgui_h.lua"], [33193, "imgui_impl_roblox.lua"], [34525, "imgui_impl_roblox_fonts.lua"], [40928, "imgui_internal.lua"], [44100, "imgui_tables.lua"], [48447, "imgui_widgets.lua"], [58202, "imstb_rectpack.lua"], [58648, "imstb_textedit.lua"], [59889, "imstb_truetype.lua"], [62357, "imstd_minstdio.lua"]]
[[12, "imgui.lua"], [18514, "imgui_demo.lua"], [25799, "imgui_draw.lua"], [31037, "imgui_h.lua"], [33235, "imgui_impl_roblox.lua"], [34587, "imgui_impl_roblox_fonts.lua"], [40990, "imgui_internal.lua"], [44166, "imgui_tables.lua"], [48513, "imgui_widgets.lua"], [58268, "imstb_rectpack.lua"], [58714, "imstb_textedit.lua"], [59955, "imstb_truetype.lua"], [62423, "imstd_minstdio.lua"]]
+88 -21
View File
@@ -8637,9 +8637,13 @@ local function RenderWindowDecorations(window, title_bar_rect, title_bar_is_high
end
if override_alpha then
bg_col = bit32.band(bg_col, bit32.bnot(IM_COL32_A_MASK)) or bit32.lshift(IM_F32_TO_INT8_SAT(alpha), IM_COL32_A_SHIFT)
bg_col = bit32.bor(bit32.band(bg_col, bit32.bnot(IM_COL32_A_MASK)), bit32.lshift(IM_F32_TO_INT8_SAT(alpha), 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, ImGuiWindowFlags.DockNodeHost) ~= 0 then bg_col = 0 end
if bit32.band(bg_col, IM_COL32_A_MASK) ~= 0 then
local bg_rect = ImRect(window.Pos + ImVec2(0, window.TitleBarHeight), window.Pos + window.Size)
local bg_rounding_flags
@@ -8857,6 +8861,26 @@ function ImGui.UpdateWindowSkipRefresh(window)
end
if bit32.band(g.NextWindowData.RefreshFlagsVal, 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 < FLT_MAX or window.ScrollTarget.y < 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
@@ -9488,6 +9512,20 @@ function ImGui.SetNextWindowRefreshPolicy(flags)
local g = GImGui
g.NextWindowData.HasFlags = bit32.bor(g.NextWindowData.HasFlags, 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(ImGuiWindowRefreshFlags.TryToAvoidRefresh, ImGuiWindowRefreshFlags.RefreshOnHover, ImGuiWindowRefreshFlags.RefreshOnFocus))
g.NextWindowData.CacheKeySet = true
g.NextWindowData.CacheKeyVal = key
end
function ImGui.GetWindowDpiScale()
@@ -10690,6 +10728,10 @@ function ImGui.End()
IM_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
@@ -34054,7 +34096,8 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
end
if rate > 0 then
local now = os.clock()
if now + 0.002 < bd.NextRender then table.clear(bd.Overlays); PE(); return end
-- 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
@@ -34319,8 +34362,33 @@ function ImGui_ImplRoblox_Init(parent, render_scale)
end
end)
-- Text proxy: 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 the proxy's text = imgui's current selection).
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)
end
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
@@ -34332,12 +34400,11 @@ function ImGui_ImplRoblox_Init(parent, render_scale)
proxy.ClearTextOnFocus = false
proxy.TextEditable = true
proxy.Text = ""
proxy.Parent = gui
bd.Proxy, bd.ProxyText, bd.PendingChars = proxy, "", {}
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.)
connect(proxy.FocusLost, function()
if bd.ProxyDebug then print("[ImGui text proxy] FocusLost, mouse over imgui:", ImGui.GetIO().WantCaptureMouse) end
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.
@@ -34346,9 +34413,6 @@ function ImGui_ImplRoblox_Init(parent, render_scale)
local g = ImGui.GetCurrentContext()
if g and g.ActiveId ~= 0 and g.ActiveId == bd.ProxyActiveId then ImGui.ClearActiveID() end
end)
connect(UserInputService.WindowFocused, function() ImGui.GetIO():AddFocusEvent(true) end)
connect(UserInputService.WindowFocusReleased, function() ImGui.GetIO():AddFocusEvent(false) end)
end
function ImGui_ImplRoblox_Shutdown()
@@ -34369,6 +34433,7 @@ 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)
local screen = bd.Gui.AbsoluteSize
local w, h = floor(screen.X * bd.Scale), floor(screen.Y * bd.Scale)
@@ -34393,15 +34458,16 @@ function ImGui_ImplRoblox_NewFrame()
-- otherwise the KeyCode fallback keeps typing working.
local proxy = bd.Proxy
local g = ImGui.GetCurrentContext()
local focused_box = UserInputService:GetFocusedTextBox()
if io.WantTextInput and g.ActiveId ~= 0 then
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()
if bd.ProxyDebug then print("[ImGui text proxy] CaptureFocus ->", focused_box == proxy and "focused" or ("FAILED, focused = " .. tostring(focused_box))) end
end
elseif focused_box == proxy then
bd.ProxyReleasing = true
@@ -34409,13 +34475,11 @@ function ImGui_ImplRoblox_NewFrame()
bd.ProxyReleasing = false
focused_box = nil
end
local was_focused = proxy_focused
proxy_focused = focused_box == proxy
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 bd.ProxyDebug then print("[ImGui text proxy] text", string.format("%q", text), "was", string.format("%q", tostring(bd.ProxyText))) end
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
@@ -34451,7 +34515,6 @@ function ImGui_ImplRoblox_NewFrame()
proxy.Text = sel
bd.ProxyLastRaw = nil
bd.ProxyText = sel
if bd.ProxyDebug then print("[ImGui text proxy] mirror selection", string.format("%q", sel)) end
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
@@ -34467,7 +34530,6 @@ function ImGui_ImplRoblox_NewFrame()
if not used_proxy_text then for _, c in ipairs(pending) do io:AddInputCharacter(c) end end
table.clear(pending)
end
if bd.ProxyDebug and was_focused ~= proxy_focused then print("[ImGui text proxy] proxy_focused =", proxy_focused) 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
@@ -34509,7 +34571,7 @@ ImGui_ImplRoblox = {
SetRenderScale = ImGui_ImplRoblox_SetRenderScale,
SetRenderRate = ImGui_ImplRoblox_SetRenderRate,
SetOverlay = ImGui_ImplRoblox_SetOverlay,
SetTextProxyDebug = function(on) ImGui_ImplRoblox_GetBackendData().ProxyDebug = on end, -- prints focus/text events
SetTextProxy = ImGui_ImplRoblox_SetTextProxy,
CreateTexture = ImGui_ImplRoblox_CreateTexture,
DestroyTexture = ImGui_ImplRoblox_DestroyTexture,
GetRenderScale = function() return ImGui_ImplRoblox_GetBackendData().Scale end,
@@ -42163,7 +42225,9 @@ function ImGuiNextWindowData()
SizeCallbackUserData = nil,
BgAlphaVal = nil,
MenuBarOffsetMinVal = ImVec2(),
RefreshFlagsVal = nil
RefreshFlagsVal = nil,
CacheKeySet = false, -- [Port] SetNextWindowCached()
CacheKeyVal = nil
}, IMGUI_NEXT_WINDOW_DATA)
end
@@ -43335,6 +43399,8 @@ function ImGuiWindow(ctx, name)
SkipItems = false,
SkipRefresh = false,
CacheSnap = nil, -- [Port] SetNextWindowCached(): state at the last refresh
CacheSkipItemsBackup = nil,
Appearing = false,
@@ -62976,6 +63042,7 @@ return {
ImGui_ImplRoblox_SetProfiling = ImGui_ImplRoblox_SetProfiling,
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,
+958
View File
@@ -0,0 +1,958 @@
--!strict
-- GENERATED by tools/gen_types.py - do not edit. The library is the child ModuleScript 'Impl'.
export type ImVec2 = { x: number, y: number, [any]: any }
export type ImVec4 = { x: number, y: number, z: number, w: number, [any]: any }
export type ImGuiAPI = {
AcceptDragDropPayload: (type: string, flags: number?) -> any,
ActivateItemByID: (...any) -> ...any,
AddDrawListToDrawDataEx: (...any) -> ...any,
AddSettingsHandler: (...any) -> ...any,
AddUpdateViewport: (...any) -> ...any,
AlignTextToFramePadding: () -> (),
ArrowButton: (str_id: string, dir: number) -> boolean,
ArrowButtonEx: (...any) -> ...any,
Begin: (name: string, open: boolean?, flags: number?) -> (boolean, boolean?),
BeginChild: (...any) -> ...any,
BeginChildEx: (...any) -> ...any,
BeginColumns: (...any) -> ...any,
BeginCombo: (label: string, preview_value: string, flags: number?) -> boolean,
BeginComboPopup: (...any) -> ...any,
BeginComboPreview: (...any) -> ...any,
BeginDisabled: (disabled: boolean?) -> (),
BeginDisabledOverrideReenable: (...any) -> ...any,
BeginDockableDragDropSource: (...any) -> ...any,
BeginDockableDragDropTarget: (...any) -> ...any,
BeginDocked: (...any) -> ...any,
BeginDragDropSource: (flags: number?) -> boolean,
BeginDragDropTarget: () -> boolean,
BeginDragDropTargetCustom: (...any) -> ...any,
BeginDragDropTargetViewport: (...any) -> ...any,
BeginErrorTooltip: (...any) -> ...any,
BeginGroup: () -> (),
BeginItemTooltip: () -> boolean,
BeginListBox: (label: string, size_arg: ImVec2?) -> boolean,
BeginMainMenuBar: () -> boolean,
BeginMenu: (label: string, enabled: boolean?) -> boolean,
BeginMenuBar: () -> boolean,
BeginMenuEx: (...any) -> ...any,
BeginPopup: (str_id: string, flags: number?) -> boolean,
BeginPopupContextItem: (str_id: string?, popup_flags: number?) -> boolean,
BeginPopupContextVoid: (str_id: string?, popup_flags: number?) -> boolean,
BeginPopupContextWindow: (str_id: string?, popup_flags: number?) -> boolean,
BeginPopupEx: (...any) -> ...any,
BeginPopupMenuEx: (...any) -> ...any,
BeginPopupModal: (name: string, p_open: boolean?, flags: number?) -> (boolean, boolean?),
BeginTabBar: (str_id: string, flags: number?) -> boolean,
BeginTabBarEx: (...any) -> ...any,
BeginTabItem: (label: string, p_open: boolean?, flags: number?) -> (boolean, boolean?),
BeginTable: (str_id: string, columns_count: number, flags: number?, outer_size: ImVec2?, inner_width: number?) -> boolean,
BeginTableEx: (...any) -> ...any,
BeginTooltip: () -> boolean,
BeginTooltipEx: (...any) -> ...any,
BeginTooltipHidden: (...any) -> ...any,
BeginViewportSideBar: (...any) -> ...any,
BringWindowToDisplayBack: (...any) -> ...any,
BringWindowToDisplayBehind: (...any) -> ...any,
BringWindowToDisplayFront: (...any) -> ...any,
BringWindowToFocusFront: (...any) -> ...any,
Bullet: () -> (),
BulletText: (fmt: string, ...any) -> (),
Button: (label: string, size_arg: ImVec2?) -> boolean,
ButtonBehavior: (...any) -> ...any,
ButtonEx: (...any) -> ...any,
CalcClipRectVisibleItemsY: (...any) -> ...any,
CalcItemSize: (...any) -> ...any,
CalcItemWidth: () -> number,
CalcRoundingFlagsForRectInRect: (...any) -> ...any,
CalcTextSize: (text: string, text_end: string?, hide_text_after_double_hash: boolean?, wrap_width: number?) -> ImVec2,
CalcTextSizeEx: (...any) -> ...any,
CalcTypematicRepeatAmount: (...any) -> ...any,
CalcWindowNextAutoFitSize: (...any) -> ...any,
CalcWrapWidthForPos: (...any) -> ...any,
Checkbox: (label: string, v: boolean?) -> (boolean, boolean?),
CheckboxFlags: (...any) -> ...any,
CleanupIniSettings: (...any) -> ...any,
ClearActiveID: (...any) -> ...any,
ClearDragDrop: (...any) -> ...any,
ClearIniSettings: (...any) -> ...any,
ClearWindowSettings: (...any) -> ...any,
CloseButton: (...any) -> ...any,
CloseCurrentPopup: () -> (),
ClosePopupToLevel: (...any) -> ...any,
ClosePopupsExceptModals: (...any) -> ...any,
ClosePopupsOverWindow: (...any) -> ...any,
CollapseButton: (...any) -> ...any,
CollapsingHeader: (...any) -> ...any,
ColorButton: (desc_id: string, col: ImVec4, flags: number?, size_arg: ImVec2?) -> boolean,
ColorConvertFloat4ToU32: (in_col: ImVec4) -> number,
ColorConvertHSVtoRGB: (...any) -> ...any,
ColorConvertRGBtoHSV: (...any) -> ...any,
ColorConvertU32ToFloat4: (...any) -> ...any,
ColorEdit3: (label: string, col: any, flags: number?) -> boolean,
ColorEdit4: (label: string, col: any, flags: number?) -> boolean,
ColorEditOptionsPopup: (...any) -> ...any,
ColorPicker3: (label: string, col: any, flags: number?) -> boolean,
ColorPicker4: (label: string, col: any, flags: number?, ref_col: any) -> boolean,
ColorPickerOptionsPopup: (...any) -> ...any,
ColorTooltip: (...any) -> ...any,
Columns: (columns_count: number?, id: string?, borders: boolean?) -> (),
Combo: (...any) -> ...any,
ConvertSingleModFlagToKey: (...any) -> ...any,
CreateContext: (shared_font_atlas: any) -> ...any,
CreateNewWindowSettings: (...any) -> ...any,
DataTypeApplyFromText: (...any) -> ...any,
DataTypeApplyOp: (...any) -> ...any,
DataTypeClamp: (...any) -> ...any,
DataTypeCompare: (...any) -> ...any,
DataTypeFormatString: (...any) -> ...any,
DataTypeGetInfo: (...any) -> ...any,
DataTypeIsZero: (...any) -> ...any,
DebugAllocHook: (...any) -> ...any,
DebugHookIdInfo: (...any) -> ...any,
DebugLog: (fmt: string, ...any) -> (),
DebugNodeColumns: (...any) -> ...any,
DebugNodeDockNode: (...any) -> ...any,
DebugNodeDrawList: (...any) -> ...any,
DebugNodeFont: (...any) -> ...any,
DebugNodeFontGlyph: (...any) -> ...any,
DebugNodeFontGlyphsForSrcMask: (...any) -> ...any,
DebugNodeStorage: (...any) -> ...any,
DebugNodeTabBar: (...any) -> ...any,
DebugNodeTable: (...any) -> ...any,
DebugNodeTableSettings: (...any) -> ...any,
DebugNodeTexture: (...any) -> ...any,
DebugNodeViewport: (...any) -> ...any,
DebugNodeWindow: (...any) -> ...any,
DebugNodeWindowSettings: (...any) -> ...any,
DebugNodeWindowsList: (...any) -> ...any,
DebugNodeWindowsListByBeginStackParent: (...any) -> ...any,
DebugRenderViewportThumbnail: (...any) -> ...any,
DebugTextEncoding: (str: string) -> (),
DestroyContext: (ctx: any) -> ...any,
DestroyPlatformWindow: (...any) -> ...any,
DestroyPlatformWindows: () -> (),
DestroyViewport: (...any) -> ...any,
DockBuilderAddNode: (...any) -> ...any,
DockBuilderCopyDockSpace: (...any) -> ...any,
DockBuilderCopyNode: (...any) -> ...any,
DockBuilderCopyWindowSettings: (...any) -> ...any,
DockBuilderDockWindow: (...any) -> ...any,
DockBuilderFinish: (...any) -> ...any,
DockBuilderGetCentralNode: (...any) -> ...any,
DockBuilderGetNode: (...any) -> ...any,
DockBuilderRemoveNode: (...any) -> ...any,
DockBuilderRemoveNodeChildNodes: (...any) -> ...any,
DockBuilderRemoveNodeDockedWindows: (...any) -> ...any,
DockBuilderSetNodePos: (...any) -> ...any,
DockBuilderSetNodeSize: (...any) -> ...any,
DockBuilderSplitNode: (...any) -> ...any,
DockContextAddNode: (...any) -> ...any,
DockContextBindNodeToWindow: (...any) -> ...any,
DockContextBuildAddWindowsToNodes: (...any) -> ...any,
DockContextBuildNodesFromSettings: (...any) -> ...any,
DockContextCalcDropPosForDocking: (...any) -> ...any,
DockContextClearNodes: (...any) -> ...any,
DockContextDeleteNode: (...any) -> ...any,
DockContextEndFrame: (...any) -> ...any,
DockContextFindNodeByID: (...any) -> ...any,
DockContextGenNodeID: (...any) -> ...any,
DockContextInitialize: (...any) -> ...any,
DockContextNewFrameUpdateDocking: (...any) -> ...any,
DockContextNewFrameUpdateUndocking: (...any) -> ...any,
DockContextProcessDock: (...any) -> ...any,
DockContextProcessUndockNode: (...any) -> ...any,
DockContextProcessUndockWindow: (...any) -> ...any,
DockContextPruneUnusedSettingsNodes: (...any) -> ...any,
DockContextQueueDock: (...any) -> ...any,
DockContextQueueNotifyRemovedNode: (...any) -> ...any,
DockContextQueueUndockNode: (...any) -> ...any,
DockContextQueueUndockWindow: (...any) -> ...any,
DockContextRebuildNodes: (...any) -> ...any,
DockContextRemoveNode: (...any) -> ...any,
DockContextShutdown: (...any) -> ...any,
DockNodeAddTabBar: (...any) -> ...any,
DockNodeAddWindow: (...any) -> ...any,
DockNodeApplyPosSizeToWindows: (...any) -> ...any,
DockNodeBeginAmendTabBar: (...any) -> ...any,
DockNodeCalcDropRectsAndTestMousePos: (...any) -> ...any,
DockNodeCalcSplitRects: (...any) -> ...any,
DockNodeCalcTabBarLayout: (...any) -> ...any,
DockNodeEndAmendTabBar: (...any) -> ...any,
DockNodeFindWindowByID: (...any) -> ...any,
DockNodeGetDepth: (...any) -> ...any,
DockNodeGetRootNode: (...any) -> ...any,
DockNodeGetTabOrder: (...any) -> ...any,
DockNodeGetWindowMenuButtonId: (...any) -> ...any,
DockNodeHideHostWindow: (...any) -> ...any,
DockNodeIsDropAllowed: (...any) -> ...any,
DockNodeIsInHierarchyOf: (...any) -> ...any,
DockNodeMoveChildNodes: (...any) -> ...any,
DockNodeMoveWindows: (...any) -> ...any,
DockNodePreviewDockRender: (...any) -> ...any,
DockNodePreviewDockSetup: (...any) -> ...any,
DockNodeRemoveTabBar: (...any) -> ...any,
DockNodeRemoveWindow: (...any) -> ...any,
DockNodeStartMouseMovingWindow: (...any) -> ...any,
DockNodeTreeFindFallbackLeafNode: (...any) -> ...any,
DockNodeTreeFindVisibleNodeByPos: (...any) -> ...any,
DockNodeTreeMerge: (...any) -> ...any,
DockNodeTreeSplit: (...any) -> ...any,
DockNodeTreeUpdatePosSize: (...any) -> ...any,
DockNodeTreeUpdateSplitter: (...any) -> ...any,
DockNodeUpdate: (...any) -> ...any,
DockNodeUpdateFlagsAndCollapse: (...any) -> ...any,
DockNodeUpdateForRootNode: (...any) -> ...any,
DockNodeUpdateHasCentralNodeChild: (...any) -> ...any,
DockNodeUpdateTabBar: (...any) -> ...any,
DockNodeUpdateVisibleFlag: (...any) -> ...any,
DockNodeWindowMenuHandler_Default: (...any) -> ...any,
DockNodeWindowMenuUpdate: (...any) -> ...any,
DockSpace: (dockspace_id: number, size_arg: ImVec2?, flags: number?, window_class: any) -> number,
DockSpaceOverViewport: (dockspace_id: number?, viewport: any, dockspace_flags: number?, window_class: any) -> number,
DragBehavior: (...any) -> ...any,
DragBehaviorT: (...any) -> ...any,
DragFloat: (label: string, v: number, v_speed: number?, v_min: number?, v_max: number?, format: string?, flags: number?) -> (number, boolean),
DragFloatRange2: (label: string, v_current_min: number, v_current_max: number, v_speed: number?, v_min: number?, v_max: number?, format: string?, format_max: string?, flags: number?) -> ...any,
DragInt: (label: string, v: number, v_speed: number?, v_min: number?, v_max: number?, format: string?, flags: number?) -> (number, boolean),
DragIntRange2: (label: string, v_current_min: number, v_current_max: number, v_speed: number?, v_min: number?, v_max: number?, format: string?, format_max: string?, flags: number?) -> ...any,
DragScalar: (label: string, data_type: number, data: any, v_speed: number?, min: any, max: any, format: string?, flags: number?) -> ...any,
DragScalarN: (label: string, data_type: number, v: any, components: number, v_speed: number?, v_min: any, v_max: any, format: string?, flags: number?) -> ...any,
Dummy: (size: ImVec2) -> (),
End: () -> (),
EndChild: () -> (),
EndColumns: (...any) -> ...any,
EndCombo: () -> (),
EndComboPreview: (...any) -> ...any,
EndDisabled: () -> (),
EndDisabledOverrideReenable: (...any) -> ...any,
EndDragDropSource: () -> (),
EndDragDropTarget: () -> (),
EndErrorTooltip: (...any) -> ...any,
EndFrame: () -> (),
EndGroup: () -> (),
EndListBox: () -> (),
EndMainMenuBar: () -> (),
EndMenu: () -> (),
EndMenuBar: () -> (),
EndPopup: () -> (),
EndTabBar: () -> (),
EndTabItem: () -> (),
EndTable: () -> (),
EndTooltip: () -> (),
ErrorCheckEndFrameFinalizeErrorTooltip: (...any) -> ...any,
ErrorCheckUsingSetCursorPosToExtendParentBoundaries: (...any) -> ...any,
ErrorLog: (...any) -> ...any,
ErrorRecoveryStoreState: (...any) -> ...any,
ErrorRecoveryTryToRecoverState: (...any) -> ...any,
ErrorRecoveryTryToRecoverWindowState: (...any) -> ...any,
ExtendHitBoxWhenNearViewportEdge: (...any) -> ...any,
FindBestWindowPosForPopup: (...any) -> ...any,
FindBestWindowPosForPopupEx: (...any) -> ...any,
FindBlockingModal: (...any) -> ...any,
FindBottomMostVisibleWindowWithinBeginStack: (...any) -> ...any,
FindFrontMostVisibleChildWindow: (...any) -> ...any,
FindHoveredViewportFromPlatformWindowStack: (...any) -> ...any,
FindOrCreateColumns: (...any) -> ...any,
FindPlatformMonitorForPos: (...any) -> ...any,
FindPlatformMonitorForRect: (...any) -> ...any,
FindRenderedTextEnd: (...any) -> ...any,
FindSettingsHandler: (...any) -> ...any,
FindViewportByID: (viewport_id: number) -> any,
FindWindowByID: (...any) -> ...any,
FindWindowByName: (...any) -> ...any,
FindWindowDisplayIndex: (...any) -> ...any,
FindWindowFocusIndex: (...any) -> ...any,
FindWindowNavFocusable: (...any) -> ...any,
FindWindowSettingsByID: (...any) -> ...any,
FindWindowSettingsByWindow: (...any) -> ...any,
FixupKeyChord: (...any) -> ...any,
FocusItem: (...any) -> ...any,
FocusTopMostWindowUnderOne: (...any) -> ...any,
FocusWindow: (...any) -> ...any,
GcAwakeTransientWindowBuffers: (...any) -> ...any,
GcCompactTransientMiscBuffers: (...any) -> ...any,
GcCompactTransientWindowBuffers: (...any) -> ...any,
GetBackgroundDrawList: (viewport: any) -> ...any,
GetClipboardText: () -> any,
GetColorU32: (...any) -> ...any,
GetColumnIndex: () -> number,
GetColumnNormFromOffset: (...any) -> ...any,
GetColumnOffset: (column_index: number?) -> number,
GetColumnOffsetFromNorm: (...any) -> ...any,
GetColumnWidth: (column_index: number?) -> number,
GetColumnsCount: () -> number,
GetColumnsID: (...any) -> ...any,
GetContentRegionAvail: () -> ImVec2,
GetContentRegionMax: (...any) -> ...any,
GetCurrentContext: () -> any,
GetCurrentWindow: (...any) -> ...any,
GetCurrentWindowRead: (...any) -> ...any,
GetCursorPos: () -> ImVec2,
GetCursorPosX: () -> number,
GetCursorPosY: () -> number,
GetCursorScreenPos: () -> ImVec2,
GetCursorStartPos: () -> ImVec2,
GetDefaultFont: (...any) -> ...any,
GetDragDropPayload: () -> any,
GetDrawData: () -> any,
GetDrawListSharedData: () -> any,
GetFont: () -> any,
GetFontSize: () -> number,
GetFontTexUvWhitePixel: () -> ImVec2,
GetForegroundDrawList: (viewport: any) -> ...any,
GetFrameCount: () -> number,
GetFrameHeight: () -> number,
GetFrameHeightWithSpacing: () -> number,
GetHoveredID: (...any) -> ...any,
GetID: (...any) -> ...any,
GetIDWithSeed: (...any) -> ...any,
GetIO: () -> any,
GetInputTextState: (...any) -> ...any,
GetItemClickedCountWithSingleClickDelay: (mouse_button: number?, delay: number?) -> number,
GetItemFlags: () -> number,
GetItemID: () -> number,
GetItemRectMax: () -> ImVec2,
GetItemRectMin: () -> ImVec2,
GetItemRectSize: () -> ImVec2,
GetKeyChordName: (...any) -> ...any,
GetKeyData: (...any) -> ...any,
GetKeyMagnitude2d: (...any) -> ...any,
GetKeyName: (key: number) -> any,
GetKeyOwner: (...any) -> ...any,
GetKeyOwnerData: (...any) -> ...any,
GetKeyPressedAmount: (key: number, repeat_delay: number, repeat_rate: number) -> number,
GetMainViewport: () -> any,
GetMergedModsFromKeys: (...any) -> ...any,
GetMetatables: (...any) -> ...any,
GetMouseButtonFromPopupFlags: (...any) -> ...any,
GetMouseClickedCount: (button: number) -> number,
GetMouseCursor: () -> number,
GetMouseDragDelta: (button: number?, lock_threshold: number?) -> ImVec2,
GetMousePos: () -> ImVec2,
GetMousePosOnOpeningCurrentPopup: () -> ImVec2,
GetNavTweakPressedAmount: (...any) -> ...any,
GetPlatformIO: () -> any,
GetPopupAllowedExtentRect: (...any) -> ...any,
GetRoundedFontSize: (...any) -> ...any,
GetScale: (...any) -> ...any,
GetScrollMaxX: () -> number,
GetScrollMaxY: () -> number,
GetScrollX: () -> number,
GetScrollY: () -> number,
GetShortcutRoutingData: (...any) -> ...any,
GetStateStorage: () -> any,
GetStyle: () -> any,
GetStyleColorName: (idx: number) -> any,
GetStyleColorVec4: (idx: number) -> ImVec4,
GetStyleVarInfo: (...any) -> ...any,
GetTextLineHeight: () -> number,
GetTextLineHeightWithSpacing: () -> number,
GetTime: () -> number,
GetTopMostAndVisiblePopupModal: (...any) -> ...any,
GetTopMostPopupModal: (...any) -> ...any,
GetTreeNodeToLabelSpacing: () -> number,
GetTypematicRepeatRate: (...any) -> ...any,
GetVersion: () -> any,
GetViewportPlatformMonitor: (...any) -> ...any,
GetWindowAlwaysWantOwnTabBar: (...any) -> ...any,
GetWindowAlwaysWantOwnViewport: (...any) -> ...any,
GetWindowContentRegionMax: (...any) -> ...any,
GetWindowContentRegionMin: (...any) -> ...any,
GetWindowDockID: () -> number,
GetWindowDpiScale: () -> number,
GetWindowDrawList: () -> any,
GetWindowHeight: () -> number,
GetWindowPos: () -> ImVec2,
GetWindowScrollbarID: (...any) -> ...any,
GetWindowScrollbarRect: (...any) -> ...any,
GetWindowSize: () -> ImVec2,
GetWindowViewport: () -> any,
GetWindowWidth: () -> number,
Image: (tex_ref: any, image_size: ImVec2, uv0: ImVec2?, uv1: ImVec2?) -> (),
ImageButton: (str_id: string, tex_ref: any, image_size: ImVec2, uv0: ImVec2?, uv1: ImVec2?, bg_col: ImVec4?, tint_col: ImVec4?) -> boolean,
ImageButtonEx: (...any) -> ...any,
ImageWithBg: (tex_ref: any, image_size: ImVec2, uv0: ImVec2?, uv1: ImVec2?, bg_col: ImVec4?, tint_col: ImVec4?) -> (),
Indent: (indent_w: number?) -> (),
Initialize: (...any) -> ...any,
InputDouble: (label: string, v: number, step: number?, step_fast: number?, format: string?, flags: number?) -> (number, boolean),
InputFloat: (label: string, v: number, step: number?, step_fast: number?, format: string?, flags: number?) -> (number, boolean),
InputFloat2: (label: string, v: any, format: string?, flags: number?) -> boolean,
InputFloat3: (label: string, v: any, format: string?, flags: number?) -> boolean,
InputFloat4: (label: string, v: any, format: string?, flags: number?) -> boolean,
InputInt: (label: string, v: number, step: number?, step_fast: number?, flags: number?) -> (number, boolean),
InputInt2: (label: string, v: any, flags: number?) -> boolean,
InputInt3: (label: string, v: any, flags: number?) -> boolean,
InputInt4: (label: string, v: any, flags: number?) -> boolean,
InputScalar: (label: string, data_type: number, data: any, step: any, step_fast: any, format: string?, flags: number?) -> ...any,
InputScalarN: (label: string, data_type: number, data: any, components: number, step: any, step_fast: any, format: string?, flags: number?) -> ...any,
InputText: (label: string, buf: any, buf_size: number, flags: number?, callback: any, user_data: any) -> ...any,
InputTextDeactivateHook: (...any) -> ...any,
InputTextEx: (...any) -> ...any,
InputTextMultiline: (label: string, buf: any, buf_size: number, size: ImVec2?, flags: number?, callback: any, user_data: any) -> ...any,
InputTextWithHint: (label: string, hint: string, buf: any, buf_size: number, flags: number?, callback: any, user_data: any) -> ...any,
InvisibleButton: (str_id: string, size_arg: ImVec2, flags: number?) -> boolean,
IsActiveIdUsingNavDir: (...any) -> ...any,
IsAliasKey: (...any) -> ...any,
IsAnyItemActive: () -> boolean,
IsAnyItemFocused: () -> boolean,
IsAnyItemHovered: () -> boolean,
IsAnyMouseDown: () -> boolean,
IsClippedEx: (...any) -> ...any,
IsDragDropActive: (...any) -> ...any,
IsDragDropPayloadBeingAccepted: (...any) -> ...any,
IsGamepadKey: (...any) -> ...any,
IsInNavFocusRoute: (...any) -> ...any,
IsItemActivated: () -> boolean,
IsItemActive: () -> boolean,
IsItemClicked: (mouse_button: number?) -> boolean,
IsItemDeactivated: () -> boolean,
IsItemDeactivatedAfterEdit: () -> boolean,
IsItemEdited: () -> boolean,
IsItemFocused: () -> boolean,
IsItemHovered: (flags: number?) -> boolean,
IsItemToggledOpen: () -> boolean,
IsItemToggledSelection: () -> boolean,
IsItemVisible: () -> boolean,
IsKeyChordPressed: (...any) -> ...any,
IsKeyDown: (...any) -> ...any,
IsKeyPressed: (key: number, is_repeat: boolean?) -> boolean,
IsKeyPressedEx: (...any) -> ...any,
IsKeyReleased: (...any) -> ...any,
IsKeyboardKey: (...any) -> ...any,
IsLRModKey: (...any) -> ...any,
IsMouseClicked: (button: number, is_repeat: boolean?) -> boolean,
IsMouseClickedEx: (...any) -> ...any,
IsMouseDoubleClicked: (...any) -> ...any,
IsMouseDown: (...any) -> ...any,
IsMouseDragPastThreshold: (...any) -> ...any,
IsMouseDragging: (button: number, lock_threshold: number?) -> boolean,
IsMouseHoveringRect: (r_min: ImVec2, r_max: ImVec2, clip: boolean?) -> boolean,
IsMouseKey: (...any) -> ...any,
IsMousePosValid: (mouse_pos: any) -> boolean,
IsMouseReleased: (...any) -> ...any,
IsMouseReleasedWithDelay: (button: number, delay: number?) -> boolean,
IsNamedKey: (...any) -> ...any,
IsNamedKeyOrMod: (...any) -> ...any,
IsPopupOpen: (id: string, popup_flags: number?) -> boolean,
IsPopupOpenRequestForItem: (...any) -> ...any,
IsPopupOpenRequestForWindow: (...any) -> ...any,
IsRectVisible: (...any) -> ...any,
IsViewportAbove: (...any) -> ...any,
IsWindowAbove: (...any) -> ...any,
IsWindowActiveAndVisible: (...any) -> ...any,
IsWindowAppearing: () -> boolean,
IsWindowChildOf: (...any) -> ...any,
IsWindowCollapsed: () -> boolean,
IsWindowContentHoverable: (...any) -> ...any,
IsWindowDocked: () -> boolean,
IsWindowFocused: (flags: number?) -> boolean,
IsWindowHovered: (flags: number?) -> boolean,
IsWindowInBeginStack: (...any) -> ...any,
IsWindowNavFocusable: (...any) -> ...any,
IsWindowWithinBeginStackOf: (...any) -> ...any,
ItemAdd: (...any) -> ...any,
ItemHandleShortcut: (...any) -> ...any,
ItemHoverable: (...any) -> ...any,
ItemSize: (...any) -> ...any,
KeepAliveID: (...any) -> ...any,
LabelText: (label: string, fmt: string, ...any) -> (),
ListBox: (...any) -> ...any,
LoadIniSettingsFromDisk: (ini_filename: string) -> (),
LoadIniSettingsFromMemory: (ini_data: string, ini_size: number?) -> (),
LocalizeGetMsg: (...any) -> ...any,
LocalizeRegisterEntries: (...any) -> ...any,
LogBegin: (...any) -> ...any,
LogButtons: () -> (),
LogFinish: () -> (),
LogRenderedText: (...any) -> ...any,
LogSetNextTextDecoration: (...any) -> ...any,
LogText: (fmt: string, ...any) -> (),
LogToBuffer: (...any) -> ...any,
LogToClipboard: (auto_open_depth: number?) -> (),
LogToFile: (auto_open_depth: number?, filename: string?) -> (),
LogToTTY: (auto_open_depth: number?) -> (),
MarkIniSettingsDirty: (...any) -> ...any,
MarkItemEdited: (...any) -> ...any,
MemAlloc: (...any) -> ...any,
MemFree: (...any) -> ...any,
MenuItem: (...any) -> ...any,
MenuItemEx: (...any) -> ...any,
MouseButtonToKey: (...any) -> ...any,
NavApplyItemToResult: (...any) -> ...any,
NavBiasScoringRect: (...any) -> ...any,
NavCalcPreferredRefPos: (...any) -> ...any,
NavCalcPreferredRefPosSource: (...any) -> ...any,
NavClearPreferredPosForAxis: (...any) -> ...any,
NavEndFrame: (...any) -> ...any,
NavHighlightActivated: (...any) -> ...any,
NavInitRequestApplyResult: (...any) -> ...any,
NavInitWindow: (...any) -> ...any,
NavMoveRequestApplyResult: (...any) -> ...any,
NavMoveRequestButNoResultYet: (...any) -> ...any,
NavMoveRequestCancel: (...any) -> ...any,
NavMoveRequestForward: (...any) -> ...any,
NavMoveRequestResolveWithLastItem: (...any) -> ...any,
NavMoveRequestResolveWithPastTreeNode: (...any) -> ...any,
NavMoveRequestSubmit: (...any) -> ...any,
NavMoveRequestTryWrapping: (...any) -> ...any,
NavProcessItem: (...any) -> ...any,
NavProcessItemForTabbingRequest: (...any) -> ...any,
NavRestoreLastChildNavWindow: (...any) -> ...any,
NavRestoreLayer: (...any) -> ...any,
NavSaveLastChildNavWindowIntoParent: (...any) -> ...any,
NavScoreItem: (...any) -> ...any,
NavUpdate: (...any) -> ...any,
NavUpdateAnyRequestFlag: (...any) -> ...any,
NavUpdateCancelRequest: (...any) -> ...any,
NavUpdateContextMenuRequest: (...any) -> ...any,
NavUpdateCreateMoveRequest: (...any) -> ...any,
NavUpdateCreateTabbingRequest: (...any) -> ...any,
NavUpdateCurrentWindowIsScrollPushableX: (...any) -> ...any,
NavUpdatePageUpPageDown: (...any) -> ...any,
NavUpdateWindowing: (...any) -> ...any,
NavUpdateWindowingApplyFocus: (...any) -> ...any,
NavUpdateWindowingOverlay: (...any) -> ...any,
NavUpdateWindowingTarget: (...any) -> ...any,
NewFrame: () -> (),
NewLine: () -> (),
NewTextBuffer: (str: string, size: number?) -> {number},
NextColumn: () -> (),
OpenPopup: (...any) -> ...any,
OpenPopupEx: (...any) -> ...any,
OpenPopupOnItemClick: (str_id: string?, popup_flags: number?) -> boolean,
PayloadIsDataType: (...any) -> ...any,
PlotEx: (...any) -> ...any,
PlotHistogram: (...any) -> ...any,
PlotLines: (...any) -> ...any,
PopClipRect: () -> (),
PopColumnsBackground: (...any) -> ...any,
PopFocusScope: (...any) -> ...any,
PopFont: () -> (),
PopID: () -> (),
PopItemFlag: () -> (),
PopItemWidth: () -> (),
PopPasswordFont: (...any) -> ...any,
PopStyleColor: (count: number?) -> (),
PopStyleVar: (count: number?) -> (),
PopTextWrapPos: () -> (),
ProgressBar: (fraction: number, size_arg: ImVec2?, overlay: string?) -> (),
PushClipRect: (clip_rect_min: ImVec2, clip_rect_max: ImVec2, intersect_with_current_clip_rect: boolean) -> (),
PushColumnClipRect: (...any) -> ...any,
PushColumnsBackground: (...any) -> ...any,
PushFocusScope: (...any) -> ...any,
PushFont: (font: any, font_size_base: number) -> ...any,
PushID: (...any) -> ...any,
PushItemFlag: (option: number, enabled: boolean) -> (),
PushItemWidth: (item_width: number) -> (),
PushMultiItemsWidths: (...any) -> ...any,
PushOverrideID: (...any) -> ...any,
PushPasswordFont: (...any) -> ...any,
PushStyleColor: (...any) -> ...any,
PushStyleVar: (...any) -> ...any,
PushStyleVarX: (idx: number, val_x: number) -> (),
PushStyleVarY: (idx: number, val_y: number) -> (),
PushTextWrapPos: (wrap_local_pos_x: number?) -> (),
RadioButton: (...any) -> ...any,
RadioButtonEx: (...any) -> ...any,
RegisterFontAtlas: (...any) -> ...any,
RemoveSettingsHandler: (...any) -> ...any,
Render: () -> (),
RenderArrow: (...any) -> ...any,
RenderArrowDockMenu: (...any) -> ...any,
RenderArrowPointingAt: (...any) -> ...any,
RenderBullet: (...any) -> ...any,
RenderCheckMark: (...any) -> ...any,
RenderColorComponentMarker: (...any) -> ...any,
RenderColorRectWithAlphaCheckerboard: (...any) -> ...any,
RenderDimmedBackgroundBehindWindow: (...any) -> ...any,
RenderDimmedBackgrounds: (...any) -> ...any,
RenderDragDropTargetRectEx: (...any) -> ...any,
RenderDragDropTargetRectForItem: (...any) -> ...any,
RenderDragDropTargetRectForViewport: (...any) -> ...any,
RenderFrame: (...any) -> ...any,
RenderFrameBorder: (...any) -> ...any,
RenderMouseCursor: (...any) -> ...any,
RenderNavCursor: (...any) -> ...any,
RenderPlatformWindowsDefault: (platform_render_arg: any, renderer_render_arg: any) -> ...any,
RenderRectFilledInRangeH: (...any) -> ...any,
RenderRectFilledWithHole: (...any) -> ...any,
RenderText: (...any) -> ...any,
RenderTextClipped: (...any) -> ...any,
RenderTextClippedEx: (...any) -> ...any,
RenderTextEllipsis: (...any) -> ...any,
RenderTextWrapped: (...any) -> ...any,
RenderViewportsThumbnails: (...any) -> ...any,
ResetMouseDragDelta: (button: number?) -> (),
RoundScalarWithFormatT: (...any) -> ...any,
SameLine: (offset_from_start_x: number?, spacing_w: number?) -> (),
SaveIniSettingsToDisk: (ini_filename: string) -> (),
SaveIniSettingsToMemory: (...any) -> ...any,
ScaleRatioFromValueT: (...any) -> ...any,
ScaleValueFromRatioT: (...any) -> ...any,
ScaleWindowsInViewport: (...any) -> ...any,
ScrollToItem: (...any) -> ...any,
ScrollToRect: (...any) -> ...any,
ScrollToRectEx: (...any) -> ...any,
Scrollbar: (...any) -> ...any,
ScrollbarEx: (...any) -> ...any,
Selectable: (...any) -> ...any,
Separator: () -> (),
SeparatorEx: (...any) -> ...any,
SeparatorText: (label: string) -> (),
SeparatorTextEx: (...any) -> ...any,
SetActiveID: (...any) -> ...any,
SetActiveIdUsingAllKeyboardKeys: (...any) -> ...any,
SetAllocatorFunctions: (alloc_func: any, free_func: any, user_data: any) -> ...any,
SetClipboardText: (text: string) -> (),
SetColorEditOptions: (...any) -> ...any,
SetColumnOffset: (column_index: number, offset: number) -> (),
SetColumnWidth: (column_index: number, width: number) -> (),
SetCurrentContext: (ctx: any) -> ...any,
SetCurrentFont: (...any) -> ...any,
SetCurrentViewport: (...any) -> ...any,
SetCursorPos: (local_pos: ImVec2) -> (),
SetCursorPosX: (x: number) -> (),
SetCursorPosY: (y: number) -> (),
SetCursorScreenPos: (pos: ImVec2) -> (),
SetDragDropPayload: (type: string, data: any, data_size: number, cond: number?) -> boolean,
SetFocusID: (...any) -> ...any,
SetHoveredID: (...any) -> ...any,
SetItemDefaultFocus: () -> (),
SetItemKeyOwner: (...any) -> ...any,
SetItemTooltip: (fmt: string, ...any) -> (),
SetKeyOwner: (...any) -> ...any,
SetKeyOwnersForKeyChord: (...any) -> ...any,
SetKeyboardFocusHere: (offset: number?) -> (),
SetLastItemData: (...any) -> ...any,
SetLastItemDataForChildWindowItem: (...any) -> ...any,
SetLastItemDataForWindow: (...any) -> ...any,
SetMouseCursor: (cursor_type: number) -> (),
SetNavCursorVisible: (visible: boolean) -> (),
SetNavCursorVisibleAfterMove: (...any) -> ...any,
SetNavFocusScope: (...any) -> ...any,
SetNavID: (...any) -> ...any,
SetNavWindow: (...any) -> ...any,
SetNextFrameWantCaptureKeyboard: (want: boolean) -> (),
SetNextFrameWantCaptureMouse: (want: boolean) -> (),
SetNextItemAllowOverlap: () -> (),
SetNextItemColorMarker: (...any) -> ...any,
SetNextItemOpen: (is_open: boolean, cond: number?) -> (),
SetNextItemRefVal: (...any) -> ...any,
SetNextItemShortcut: (key_chord: any, flags: number?) -> (),
SetNextItemStorageID: (storage_id: number) -> (),
SetNextItemWidth: (item_width: number) -> (),
SetNextWindowBgAlpha: (alpha: number) -> (),
SetNextWindowCached: (key: any) -> (),
SetNextWindowChildFlags: (...any) -> ...any,
SetNextWindowClass: (window_class: any) -> (),
SetNextWindowCollapsed: (collapsed: boolean, cond: number?) -> (),
SetNextWindowContentSize: (size: ImVec2) -> (),
SetNextWindowDockID: (id: number, cond: number?) -> (),
SetNextWindowFlags: (...any) -> ...any,
SetNextWindowFocus: () -> (),
SetNextWindowPos: (pos: ImVec2, cond: number?, pivot: ImVec2?) -> (),
SetNextWindowRefreshPolicy: (...any) -> ...any,
SetNextWindowScroll: (scroll: ImVec2) -> (),
SetNextWindowSize: (size: ImVec2, cond: number?) -> (),
SetNextWindowSizeConstraints: (size_min: ImVec2, size_max: ImVec2, custom_callback: any, custom_callback_user_data: any) -> ...any,
SetNextWindowViewport: (id: number) -> (),
SetScrollFromPosX: (...any) -> ...any,
SetScrollFromPosY: (...any) -> ...any,
SetScrollHereX: (center_x_ratio: number?) -> (),
SetScrollHereY: (center_y_ratio: number?) -> (),
SetScrollX: (...any) -> ...any,
SetScrollY: (...any) -> ...any,
SetShortcutRouting: (...any) -> ...any,
SetStateStorage: (tree: any) -> ...any,
SetTabItemClosed: (label: string) -> (),
SetTooltip: (fmt: string, ...any) -> (),
SetWindowClipRectBeforeSetChannel: (...any) -> ...any,
SetWindowCollapsed: (...any) -> ...any,
SetWindowDock: (...any) -> ...any,
SetWindowFocus: (...any) -> ...any,
SetWindowFontScale: (...any) -> ...any,
SetWindowHiddenAndSkipItemsForCurrentFrame: (...any) -> ...any,
SetWindowHitTestHole: (...any) -> ...any,
SetWindowPos: (...any) -> ...any,
SetWindowSize: (...any) -> ...any,
SetWindowViewport: (...any) -> ...any,
ShadeVertsLinearColorGradientKeepAlpha: (...any) -> ...any,
ShadeVertsLinearUV: (...any) -> ...any,
ShadeVertsTransformPos: (...any) -> ...any,
Shortcut: (...any) -> ...any,
ShowAboutWindow: (p_open: boolean?) -> ...any,
ShowDebugLogWindow: (p_open: boolean?) -> ...any,
ShowDemoWindow: (p_open: boolean?) -> ...any,
ShowFontAtlas: (...any) -> ...any,
ShowFontSelector: (label: string) -> (),
ShowIDStackToolWindow: (p_open: boolean?) -> ...any,
ShowMetricsWindow: (p_open: boolean?) -> ...any,
ShowStyleEditor: (ref: any) -> ...any,
ShowStyleSelector: (label: string) -> boolean,
ShowUserGuide: () -> (),
ShrinkWidths: (...any) -> ...any,
Shutdown: (...any) -> ...any,
SliderAngle: (label: string, v_rad: number, v_degrees_min: number?, v_degrees_max: number?, format: string?, flags: number?) -> (number, boolean),
SliderBehavior: (...any) -> ...any,
SliderBehaviorT: (...any) -> ...any,
SliderFloat: (label: string, v: number, v_min: number, v_max: number, format: string?, flags: number?) -> (number, boolean),
SliderFloat2: (label: string, v: any, v_min: number, v_max: number, format: string?, flags: number?) -> boolean,
SliderFloat3: (label: string, v: any, v_min: number, v_max: number, format: string?, flags: number?) -> boolean,
SliderFloat4: (label: string, v: any, v_min: number, v_max: number, format: string?, flags: number?) -> boolean,
SliderInt: (label: string, v: number, v_min: number, v_max: number, format: string?, flags: number?) -> (number, boolean),
SliderInt2: (label: string, v: any, v_min: number, v_max: number, format: string?, flags: number?) -> boolean,
SliderInt3: (label: string, v: any, v_min: number, v_max: number, format: string?, flags: number?) -> boolean,
SliderInt4: (label: string, v: any, v_min: number, v_max: number, format: string?, flags: number?) -> boolean,
SliderScalar: (label: string, data_type: number, data: any, min: any, max: any, format: string?, flags: number?) -> ...any,
SliderScalarN: (label: string, data_type: number, v: any, components: number, v_min: any, v_max: any, format: string?, flags: number?) -> ...any,
SmallButton: (label: string) -> boolean,
Spacing: () -> (),
SplitterBehavior: (...any) -> ...any,
StartMouseMovingWindow: (...any) -> ...any,
StartMouseMovingWindowOrNode: (...any) -> ...any,
StopMouseMovingWindow: (...any) -> ...any,
StyleColorsClassic: (dst: any) -> ...any,
StyleColorsDark: (dst: any) -> ...any,
StyleColorsLight: (dst: any) -> ...any,
TabBarAddTab: (...any) -> ...any,
TabBarCalcMaxTabWidth: (...any) -> ...any,
TabBarCalcTabID: (...any) -> ...any,
TabBarCloseTab: (...any) -> ...any,
TabBarFindByID: (...any) -> ...any,
TabBarFindMostRecentlySelectedTabForActiveWindow: (...any) -> ...any,
TabBarFindTabByID: (...any) -> ...any,
TabBarFindTabByOrder: (...any) -> ...any,
TabBarGetCurrentTab: (...any) -> ...any,
TabBarGetTabName: (...any) -> ...any,
TabBarGetTabOrder: (...any) -> ...any,
TabBarGetTabPos: (...any) -> ...any,
TabBarLayout: (...any) -> ...any,
TabBarProcessReorder: (...any) -> ...any,
TabBarQueueFocus: (...any) -> ...any,
TabBarQueueReorder: (...any) -> ...any,
TabBarQueueReorderFromMousePos: (...any) -> ...any,
TabBarRemove: (...any) -> ...any,
TabBarRemoveTab: (...any) -> ...any,
TabBarScrollClamp: (...any) -> ...any,
TabBarScrollToTab: (...any) -> ...any,
TabBarScrollingButtons: (...any) -> ...any,
TabBarTabListPopupButton: (...any) -> ...any,
TabItemBackground: (...any) -> ...any,
TabItemButton: (label: string, flags: number?) -> boolean,
TabItemCalcSize: (...any) -> ...any,
TabItemEx: (...any) -> ...any,
TabItemLabelAndCloseButton: (...any) -> ...any,
TabItemSpacing: (...any) -> ...any,
TableAngledHeadersRow: () -> (),
TableAngledHeadersRowEx: (...any) -> ...any,
TableApplyExternalUnclipRect: (...any) -> ...any,
TableApplyQueuedRequests: (...any) -> ...any,
TableBeginCell: (...any) -> ...any,
TableBeginContextMenuPopup: (...any) -> ...any,
TableBeginInitMemory: (...any) -> ...any,
TableBeginRow: (...any) -> ...any,
TableCalcMaxColumnWidth: (...any) -> ...any,
TableDrawBorders: (...any) -> ...any,
TableDrawDefaultContextMenu: (...any) -> ...any,
TableEndCell: (...any) -> ...any,
TableEndRow: (...any) -> ...any,
TableFindByID: (...any) -> ...any,
TableFixColumnSortDirection: (...any) -> ...any,
TableFixDisplayOrder: (...any) -> ...any,
TableGcCompactSettings: (...any) -> ...any,
TableGcCompactTransientBuffers: (...any) -> ...any,
TableGetBoundSettings: (...any) -> ...any,
TableGetCellBgRect: (...any) -> ...any,
TableGetColumnCount: () -> number,
TableGetColumnFlags: (column_n: number?) -> number,
TableGetColumnIndex: () -> number,
TableGetColumnName: (...any) -> ...any,
TableGetColumnNextSortDirection: (...any) -> ...any,
TableGetColumnResizeID: (...any) -> ...any,
TableGetColumnWidthAuto: (...any) -> ...any,
TableGetHeaderAngledMaxLabelWidth: (...any) -> ...any,
TableGetHeaderRowHeight: (...any) -> ...any,
TableGetHoveredColumn: () -> number,
TableGetHoveredRow: (...any) -> ...any,
TableGetInstanceData: (...any) -> ...any,
TableGetInstanceID: (...any) -> ...any,
TableGetRowIndex: () -> number,
TableGetSortSpecs: () -> any,
TableHeader: (label: string) -> (),
TableHeadersRow: () -> (),
TableInitColumnDefaults: (...any) -> ...any,
TableLoadSettings: (...any) -> ...any,
TableLoadSettingsForColumn: (...any) -> ...any,
TableLoadSettingsForColumns: (...any) -> ...any,
TableMergeDrawChannels: (...any) -> ...any,
TableNextColumn: () -> boolean,
TableNextRow: (row_flags: number?, row_min_height: number?) -> (),
TableOpenContextMenu: (...any) -> ...any,
TablePopBackgroundChannel: (...any) -> ...any,
TablePopColumnChannel: (...any) -> ...any,
TablePushBackgroundChannel: (...any) -> ...any,
TablePushColumnChannel: (...any) -> ...any,
TableQueueSetColumnDisplayOrder: (...any) -> ...any,
TableReconcileColumns: (...any) -> ...any,
TableRemove: (...any) -> ...any,
TableResetSettings: (...any) -> ...any,
TableSaveSettings: (...any) -> ...any,
TableSetBgColor: (target: number, color: number, column_n: number?) -> (),
TableSetColumnDisplayOrder: (...any) -> ...any,
TableSetColumnEnabled: (column_n: number, enabled: boolean) -> (),
TableSetColumnIndex: (column_n: number) -> boolean,
TableSetColumnSortDirection: (...any) -> ...any,
TableSetColumnWidth: (...any) -> ...any,
TableSetColumnWidthAutoAll: (...any) -> ...any,
TableSetColumnWidthAutoSingle: (...any) -> ...any,
TableSettingsAddSettingsHandler: (...any) -> ...any,
TableSettingsCreate: (...any) -> ...any,
TableSettingsFindByID: (...any) -> ...any,
TableSetupColumn: (label: string, flags: number?, init_width_or_weight: number?, user_data: number?) -> (),
TableSetupDrawChannels: (...any) -> ...any,
TableSetupScrollFreeze: (columns: number, rows: number) -> (),
TableSortSpecsBuild: (...any) -> ...any,
TableSortSpecsSanitize: (...any) -> ...any,
TableUpdateBorders: (...any) -> ...any,
TableUpdateColumnsWeightFromWidth: (...any) -> ...any,
TableUpdateLayout: (...any) -> ...any,
TeleportMousePos: (...any) -> ...any,
TempInputIsActive: (...any) -> ...any,
TempInputScalar: (...any) -> ...any,
TempInputText: (...any) -> ...any,
TestKeyOwner: (...any) -> ...any,
Text: (fmt: string, ...any) -> (),
TextAligned: (...any) -> ...any,
TextBufferToString: (buf: {number}) -> string,
TextColored: (col: ImVec4, fmt: string, ...any) -> (),
TextDisabled: (fmt: string, ...any) -> (),
TextEx: (...any) -> ...any,
TextLink: (label: string) -> boolean,
TextLinkOpenURL: (label: string, url: string?) -> boolean,
TextUnformatted: (text: string, text_end: string?) -> (),
TextV: (fmt: string, ...any) -> (),
TextWrapped: (fmt: string, ...any) -> (),
TranslateWindow: (...any) -> ...any,
TranslateWindowsInViewport: (...any) -> ...any,
TreeNode: (...any) -> ...any,
TreeNodeBehavior: (...any) -> ...any,
TreeNodeDrawLineToChildNode: (...any) -> ...any,
TreeNodeDrawLineToTreePop: (...any) -> ...any,
TreeNodeEx: (...any) -> ...any,
TreeNodeExV: (...any) -> ...any,
TreeNodeGetOpen: (storage_id: number) -> boolean,
TreeNodeSetOpen: (...any) -> ...any,
TreeNodeUpdateNextOpen: (...any) -> ...any,
TreeNodeV: (...any) -> ...any,
TreePop: () -> (),
TreePush: (...any) -> ...any,
TreePushOverrideID: (...any) -> ...any,
Unindent: (indent_w: number?) -> (),
UnregisterFontAtlas: (...any) -> ...any,
UpdateAliasKey: (...any) -> ...any,
UpdateCurrentFontSize: (...any) -> ...any,
UpdateDebugToolItemPathQuery: (...any) -> ...any,
UpdateFontsEndFrame: (...any) -> ...any,
UpdateFontsNewFrame: (...any) -> ...any,
UpdateHoveredWindowAndCaptureFlags: (...any) -> ...any,
UpdateInputEvents: (...any) -> ...any,
UpdateKeyRoutingTable: (...any) -> ...any,
UpdateKeyboardInputs: (...any) -> ...any,
UpdateMouseInputs: (...any) -> ...any,
UpdateMouseMovingWindowEndFrame: (...any) -> ...any,
UpdateMouseMovingWindowNewFrame: (...any) -> ...any,
UpdateMouseWheel: (...any) -> ...any,
UpdatePlatformWindows: () -> (),
UpdateSettings: (...any) -> ...any,
UpdateTexturesEndFrame: (...any) -> ...any,
UpdateTexturesNewFrame: (...any) -> ...any,
UpdateTryMergeWindowIntoHostViewport: (...any) -> ...any,
UpdateTryMergeWindowIntoHostViewports: (...any) -> ...any,
UpdateViewportPlatformMonitor: (...any) -> ...any,
UpdateViewportsEndFrame: (...any) -> ...any,
UpdateViewportsNewFrame: (...any) -> ...any,
UpdateWindowInFocusOrderList: (...any) -> ...any,
UpdateWindowParentAndRootLinks: (...any) -> ...any,
UpdateWindowSkipRefresh: (...any) -> ...any,
VSliderFloat: (label: string, size: ImVec2, v: number, v_min: number, v_max: number, format: string?, flags: number?) -> (number, boolean),
VSliderInt: (label: string, size: ImVec2, v: number, v_min: number, v_max: number, format: string?, flags: number?) -> (number, boolean),
VSliderScalar: (label: string, size: ImVec2, data_type: number, data: any, min: any, max: any, format: string?, flags: number?) -> ...any,
Value: (...any) -> ...any,
WindowPosAbsToRel: (...any) -> ...any,
WindowRectAbsToRel: (...any) -> ...any,
WindowRectRelToAbs: (...any) -> ...any,
WindowSelectViewport: (...any) -> ...any,
WindowSettingsAddSettingsHandler: (...any) -> ...any,
WindowSyncOwnedViewport: (...any) -> ...any,
_ByteArrayToString: (...any) -> ...any,
_GetTypeFunc: (...any) -> ...any,
_SetCurrentContext_Internal: (...any) -> ...any,
_SetCurrentContext_Tables: (...any) -> ...any,
_SetCurrentContext_Widgets: (...any) -> ...any,
[string]: any,
}
export type ImDrawFlags = { None: number, RoundCornersTopLeft: number, RoundCornersTopRight: number, RoundCornersBottomLeft: number, RoundCornersBottomRight: number, RoundCornersNone: number, Closed: number, RoundCornersTop: number, RoundCornersBottom: number, RoundCornersLeft: number, RoundCornersRight: number, RoundCornersAll: number, RoundCornersDefault_: number, RoundCornersMask_: number, InvalidMask_: number, [string]: number }
export type ImDrawListFlags = { None: number, AntiAliasedLines: number, AntiAliasedLinesUseTex: number, AntiAliasedFill: number, AllowVtxOffset: number, TextNoPixelSnap: number, [string]: number }
export type ImFontFlags = { None: number, NoLoadError: number, NoLoadGlyphs: number, LockBakedSizes: number, ImplicitRefSize: number, [string]: number }
export type ImGuiBackendFlags = { None: number, HasGamepad: number, HasMouseCursors: number, HasSetMousePos: number, RendererHasVtxOffset: number, RendererHasTextures: number, RendererHasViewports: number, PlatformHasViewports: number, HasMouseHoveredViewport: number, HasParentViewport: number, [string]: number }
export type ImGuiButtonFlags = { None: number, MouseButtonLeft: number, MouseButtonRight: number, MouseButtonMiddle: number, EnableNav: number, PressedOnClick: number, PressedOnClickRelease: number, PressedOnClickReleaseAnywhere: number, PressedOnRelease: number, PressedOnDoubleClick: number, PressedOnDragDropHold: number, FlattenChildren: number, AllowOverlap: number, AlignTextBaseLine: number, NoKeyModsAllowed: number, NoHoldingActiveId: number, NoNavFocus: number, NoHoveredOnFocus: number, NoSetKeyOwner: number, NoTestKeyOwner: number, NoFocus: number, MouseButtonMask_: number, PressedOnMask_: number, PressedOnDefault_: number, NoKeyModifiers: number, [string]: number }
export type ImGuiChildFlags = { None: number, Borders: number, AlwaysUseWindowPadding: number, ResizeX: number, ResizeY: number, AutoResizeX: number, AutoResizeY: number, AlwaysAutoResize: number, FrameStyle: number, NavFlattened: number, ResizeBoth: number, ResizeXAndY: number, [string]: number }
export type ImGuiCol = { Text: number, TextDisabled: number, WindowBg: number, ChildBg: number, PopupBg: number, Border: number, BorderShadow: number, FrameBg: number, FrameBgHovered: number, FrameBgActive: number, TitleBg: number, TitleBgActive: number, TitleBgCollapsed: number, MenuBarBg: number, ScrollbarBg: number, ScrollbarGrab: number, ScrollbarGrabHovered: number, ScrollbarGrabActive: number, CheckMark: number, CheckboxSelectedBg: number, SliderGrab: number, SliderGrabActive: number, Button: number, ButtonHovered: number, ButtonActive: number, Header: number, HeaderHovered: number, HeaderActive: number, Separator: number, SeparatorHovered: number, SeparatorActive: number, ResizeGrip: number, ResizeGripHovered: number, ResizeGripActive: number, InputTextCursor: number, TabHovered: number, Tab: number, TabSelected: number, TabSelectedOverline: number, TabDimmed: number, TabDimmedSelected: number, TabDimmedSelectedOverline: number, DockingPreview: number, DockingEmptyBg: number, PlotLines: number, PlotLinesHovered: number, PlotHistogram: number, PlotHistogramHovered: number, TableHeaderBg: number, TableBorderStrong: number, TableBorderLight: number, TableRowBg: number, TableRowBgAlt: number, TextLink: number, TextSelectedBg: number, TreeLines: number, DragDropTarget: number, DragDropTargetBg: number, UnsavedMarker: number, NavCursor: number, NavWindowingHighlight: number, NavWindowingDimBg: number, ModalWindowDimBg: number, COUNT: number, [string]: number }
export type ImGuiColorEditFlags = { None: number, NoAlpha: number, NoPicker: number, NoOptions: number, NoSmallPreview: number, NoInputs: number, NoTooltip: number, NoLabel: number, NoSidePreview: number, NoDragDrop: number, NoBorder: number, NoColorMarkers: number, AlphaOpaque: number, AlphaNoBg: number, AlphaPreviewHalf: number, AlphaBar: number, HDR: number, DisplayRGB: number, DisplayHSV: number, DisplayHex: number, Uint8: number, Float: number, PickerHueBar: number, PickerHueWheel: number, PickerNoRotate: number, InputRGB: number, InputHSV: number, DefaultOptions_: number, AlphaMask_: number, DisplayMask_: number, DataTypeMask_: number, PickerMask_: number, InputMask_: number, [string]: number }
export type ImGuiComboFlags = { None: number, PopupAlignLeft: number, HeightSmall: number, HeightRegular: number, HeightLarge: number, HeightLargest: number, NoArrowButton: number, NoPreview: number, WidthFitPreview: number, HeightMask_: number, CustomPreview: number, [string]: number }
export type ImGuiCond = { None: number, Always: number, Once: number, FirstUseEver: number, Appearing: number, [string]: number }
export type ImGuiConfigFlags = { None: number, NavEnableKeyboard: number, NavEnableGamepad: number, NoMouse: number, NoMouseCursorChange: number, NoKeyboard: number, DockingEnable: number, ViewportsEnable: number, IsSRGB: number, IsTouchScreen: number, [string]: number }
export type ImGuiDragDropFlags = { None: number, SourceNoPreviewTooltip: number, SourceNoDisableHover: number, SourceNoHoldToOpenOthers: number, SourceAllowNullID: number, SourceExtern: number, PayloadAutoExpire: number, PayloadNoCrossContext: number, PayloadNoCrossProcess: number, AcceptBeforeDelivery: number, AcceptNoDrawDefaultRect: number, AcceptNoPreviewTooltip: number, AcceptDrawAsHovered: number, AcceptPeekOnly: number, [string]: number }
export type ImGuiFocusedFlags = { None: number, ChildWindows: number, RootWindow: number, AnyWindow: number, NoPopupHierarchy: number, DockHierarchy: number, RootAndChildWindows: number, [string]: number }
export type ImGuiHoveredFlags = { None: number, ChildWindows: number, RootWindow: number, AnyWindow: number, NoPopupHierarchy: number, DockHierarchy: number, AllowWhenBlockedByPopup: number, AllowWhenBlockedByActiveItem: number, AllowWhenOverlappedByItem: number, AllowWhenOverlappedByWindow: number, AllowWhenDisabled: number, NoNavOverride: number, ForTooltip: number, Stationary: number, DelayNone: number, DelayShort: number, DelayNormal: number, NoSharedDelay: number, AllowWhenOverlapped: number, RectOnly: number, RootAndChildWindows: number, DelayMask_: number, AllowedMaskForIsWindowHovered: number, AllowedMaskForIsItemHovered: number, [string]: number }
export type ImGuiInputFlags = { None: number, Repeat: number, RouteActive: number, RouteFocused: number, RouteGlobal: number, RouteAlways: number, RouteOverFocused: number, RouteOverActive: number, RouteUnlessBgFocused: number, RouteFromRootWindow: number, Tooltip: number, RepeatRateDefault: number, RepeatRateNavMove: number, RepeatRateNavTweak: number, RepeatUntilRelease: number, RepeatUntilKeyModsChange: number, RepeatUntilKeyModsChangeFromNone: number, RepeatUntilOtherKeyPress: number, LockThisFrame: number, LockUntilRelease: number, CondHovered: number, CondActive: number, CondDefault_: number, RepeatRateMask_: number, RepeatUntilMask_: number, RepeatMask_: number, CondMask_: number, RouteTypeMask_: number, RouteOptionsMask_: number, SupportedByIsKeyPressed: number, SupportedByIsMouseClicked: number, SupportedByShortcut: number, SupportedBySetNextItemShortcut: number, SupportedBySetKeyOwner: number, SupportedBySetItemKeyOwner: number, [string]: number }
export type ImGuiInputTextFlags = { None: number, CharsDecimal: number, CharsHexadecimal: number, CharsScientific: number, CharsUppercase: number, CharsNoBlank: number, AllowTabInput: number, EnterReturnsTrue: number, EscapeClearsAll: number, CtrlEnterForNewLine: number, ReadOnly: number, Password: number, AlwaysOverwrite: number, AutoSelectAll: number, ParseEmptyRefVal: number, DisplayEmptyRefVal: number, NoHorizontalScroll: number, NoUndoRedo: number, ElideLeft: number, CallbackCompletion: number, CallbackHistory: number, CallbackAlways: number, CallbackCharFilter: number, CallbackResize: number, CallbackEdit: number, WordWrap: number, Multiline: number, TempInput: number, LocalizeDecimalPoint: number, [string]: number }
export type ImGuiItemFlags = { None: number, NoTabStop: number, NoNav: number, NoNavDefaultFocus: number, ButtonRepeat: number, AutoClosePopups: number, AllowDuplicateId: number, Disabled: number, LiveEditOnInputText: number, LiveEditOnInputScalar: number, LiveEditOnInput: number, ReadOnly: number, MixedValue: number, NoWindowHoverableCheck: number, AllowOverlap: number, NoNavDisableMouseHover: number, NoMarkEdited: number, NoFocus: number, Inputable: number, HasSelectionUserData: number, IsMultiSelect: number, Default_: number, [string]: number }
export type ImGuiItemStatusFlags = { None: number, HoveredRect: number, HasDisplayRect: number, Edited: number, ToggledSelection: number, ToggledOpen: number, HasDeactivated: number, Deactivated: number, HoveredWindow: number, Visible: number, HasClipRect: number, HasShortcut: number, EditedInternal: number, [string]: number }
export type ImGuiKey = { None: number, NamedKey_BEGIN: number, Tab: number, LeftArrow: number, RightArrow: number, UpArrow: number, DownArrow: number, PageUp: number, PageDown: number, Home: number, End: number, Insert: number, Delete: number, Backspace: number, Space: number, Enter: number, Escape: number, LeftCtrl: number, RightCtrl: number, Menu: number, K0: number, A: number, K: number, U: number, F1: number, F7: number, F13: number, F19: number, Apostrophe: number, CapsLock: number, Keypad0: number, KeypadDecimal: number, AppBack: number, AppForward: number, Oem102: number, GamepadStart: number, GamepadFaceLeft: number, GamepadDpadLeft: number, GamepadL1: number, GamepadL2: number, GamepadL3: number, GamepadLStickLeft: number, GamepadRStickLeft: number, MouseLeft: number, ReservedForModCtrl: number, NamedKey_END: number, NamedKey_COUNT: number, NavKeyboardTweakSlow: number, NavKeyboardTweakFast: number, NavGamepadTweakSlow: number, NavGamepadTweakFast: number, NavGamepadMenu: number, NavGamepadContextMenu: number, [string]: number }
export type ImGuiMouseSource = { Mouse: number, TouchScreen: number, Pen: number, COUNT: number, [string]: number }
export type ImGuiNextItemDataFlags = { None: number, HasWidth: number, HasOpen: number, HasShortcut: number, HasRefVal: number, HasStorageID: number, HasColorMarker: number, [string]: number }
export type ImGuiPopupFlags = { None: number, MouseButtonLeft: number, MouseButtonRight: number, MouseButtonMiddle: number, NoReopen: number, NoOpenOverExistingPopup: number, NoOpenOverItems: number, AnyPopupId: number, AnyPopupLevel: number, AnyPopup: number, MouseButtonShift_: number, MouseButtonMask_: number, InvalidMask_: number, [string]: number }
export type ImGuiSelectableFlags = { None: number, NoAutoClosePopups: number, SpanAllColumns: number, AllowDoubleClick: number, Disabled: number, AllowOverlap: number, Highlight: number, SelectOnNav: number, NoHoldingActiveID: number, SelectOnClick: number, SelectOnRelease: number, SpanAvailWidth: number, SetNavIdOnHover: number, NoPadWithHalfSpacing: number, NoSetKeyOwner: number, [string]: number }
export type ImGuiSliderFlags = { None: number, Logarithmic: number, NoRoundToFormat: number, NoInput: number, WrapAround: number, ClampOnInput: number, ClampZeroRange: number, NoSpeedTweaks: number, ColorMarkers: number, InvalidMask_: number, AlwaysClamp: number, Vertical: number, ReadOnly: number, [string]: number }
export type ImGuiStyleVar = { Alpha: number, DisabledAlpha: number, WindowPadding: number, WindowRounding: number, WindowBorderSize: number, WindowMinSize: number, WindowTitleAlign: number, ChildRounding: number, ChildBorderSize: number, PopupRounding: number, PopupBorderSize: number, FramePadding: number, FrameRounding: number, FrameBorderSize: number, ItemSpacing: number, ItemInnerSpacing: number, IndentSpacing: number, CellPadding: number, ScrollbarSize: number, ScrollbarRounding: number, ScrollbarPadding: number, GrabMinSize: number, GrabRounding: number, ImageRounding: number, ImageBorderSize: number, TabRounding: number, TabBorderSize: number, TabMinWidthBase: number, TabMinWidthShrink: number, TabBarBorderSize: number, TabBarOverlineSize: number, TableAngledHeadersAngle: number, TableAngledHeadersTextAlign: number, TreeLinesSize: number, TreeLinesRounding: number, MenuItemRounding: number, SelectableRounding: number, DragDropTargetRounding: number, ButtonTextAlign: number, SelectableTextAlign: number, SeparatorTextBorderSize: number, SeparatorTextAlign: number, SeparatorTextPadding: number, COUNT: number, [string]: number }
export type ImGuiTreeNodeFlags = { None: number, Selected: number, Framed: number, AllowOverlap: number, NoTreePushOnOpen: number, NoAutoOpenOnLog: number, DefaultOpen: number, OpenOnDoubleClick: number, OpenOnArrow: number, Leaf: number, Bullet: number, FramePadding: number, SpanAvailWidth: number, SpanFullWidth: number, SpanLabelWidth: number, SpanAllColumns: number, LabelSpanAllColumns: number, NavLeftJumpsToParent: number, DrawLinesNone: number, DrawLinesFull: number, DrawLinesToNodes: number, NoNavFocus: number, ClipLabelForTrailingButton: number, UpsideDownArrow: number, CollapsingHeader: number, OpenOnMask_: number, DrawLinesMask_: number, [string]: number }
export type ImGuiViewportFlags = { None: number, IsPlatformWindow: number, IsPlatformMonitor: number, OwnedByApp: number, NoDecoration: number, NoTaskBarIcon: number, NoFocusOnAppearing: number, NoFocusOnClick: number, NoInputs: number, NoRendererClear: number, NoAutoMerge: number, TopMost: number, CanHostOtherWindows: number, IsMinimized: number, IsFocused: number, [string]: number }
export type ImGuiWindowFlags = { None: number, NoTitleBar: number, NoResize: number, NoMove: number, NoScrollbar: number, NoScrollWithMouse: number, NoCollapse: number, AlwaysAutoResize: number, NoBackground: number, NoSavedSettings: number, NoMouseInputs: number, MenuBar: number, HorizontalScrollbar: number, NoFocusOnAppearing: number, NoBringToFrontOnFocus: number, AlwaysVerticalScrollbar: number, AlwaysHorizontalScrollbar: number, NoNavInputs: number, NoNavFocus: number, UnsavedDocument: number, NoDocking: number, DockNodeHost: number, ChildWindow: number, Tooltip: number, Popup: number, Modal: number, ChildMenu: number, NoNav: number, NoDecoration: number, NoInputs: number, [string]: number }
export type ImGuiModule = {
ImGui: ImGuiAPI,
ImVec2: (x: number?, y: number?, z: number?, w: number?) -> ImVec2,
ImVec4: (x: number?, y: number?, z: number?, w: number?) -> ImVec4,
ImDrawFlags: ImDrawFlags,
ImDrawListFlags: ImDrawListFlags,
ImFontFlags: ImFontFlags,
ImGuiBackendFlags: ImGuiBackendFlags,
ImGuiButtonFlags: ImGuiButtonFlags,
ImGuiChildFlags: ImGuiChildFlags,
ImGuiCol: ImGuiCol,
ImGuiColorEditFlags: ImGuiColorEditFlags,
ImGuiComboFlags: ImGuiComboFlags,
ImGuiCond: ImGuiCond,
ImGuiConfigFlags: ImGuiConfigFlags,
ImGuiDragDropFlags: ImGuiDragDropFlags,
ImGuiFocusedFlags: ImGuiFocusedFlags,
ImGuiHoveredFlags: ImGuiHoveredFlags,
ImGuiInputFlags: ImGuiInputFlags,
ImGuiInputTextFlags: ImGuiInputTextFlags,
ImGuiItemFlags: ImGuiItemFlags,
ImGuiItemStatusFlags: ImGuiItemStatusFlags,
ImGuiKey: ImGuiKey,
ImGuiMouseSource: ImGuiMouseSource,
ImGuiNextItemDataFlags: ImGuiNextItemDataFlags,
ImGuiPopupFlags: ImGuiPopupFlags,
ImGuiSelectableFlags: ImGuiSelectableFlags,
ImGuiSliderFlags: ImGuiSliderFlags,
ImGuiStyleVar: ImGuiStyleVar,
ImGuiTreeNodeFlags: ImGuiTreeNodeFlags,
ImGuiViewportFlags: ImGuiViewportFlags,
ImGuiWindowFlags: ImGuiWindowFlags,
[string]: any,
}
return (require(script:WaitForChild("Impl")) :: any) :: ImGuiModule
+1 -1
View File
@@ -1 +1 @@
[[149, "imgui.lua"], [18609, "imgui_demo.lua"], [25894, "imgui_draw.lua"], [31132, "imgui_h.lua"], [33330, "imgui_impl_love.lua"], [33870, "imgui_internal.lua"], [37042, "imgui_tables.lua"], [41389, "imgui_widgets.lua"], [51144, "imstb_rectpack.lua"], [51590, "imstb_textedit.lua"], [52831, "imstb_truetype.lua"], [55299, "imstd_minstdio.lua"]]
[[149, "imgui.lua"], [18647, "imgui_demo.lua"], [25932, "imgui_draw.lua"], [31170, "imgui_h.lua"], [33368, "imgui_impl_love.lua"], [33908, "imgui_internal.lua"], [37084, "imgui_tables.lua"], [41431, "imgui_widgets.lua"], [51186, "imstb_rectpack.lua"], [51632, "imstb_textedit.lua"], [52873, "imstb_truetype.lua"], [55341, "imstd_minstdio.lua"]]
+43 -1
View File
@@ -8994,6 +8994,26 @@ function ImGui.UpdateWindowSkipRefresh(window)
end
if bit32.band(g.NextWindowData.RefreshFlagsVal, 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 < FLT_MAX or window.ScrollTarget.y < 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
@@ -9625,6 +9645,20 @@ function ImGui.SetNextWindowRefreshPolicy(flags)
local g = GImGui
g.NextWindowData.HasFlags = bit32.bor(g.NextWindowData.HasFlags, 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(ImGuiWindowRefreshFlags.TryToAvoidRefresh, ImGuiWindowRefreshFlags.RefreshOnHover, ImGuiWindowRefreshFlags.RefreshOnFocus))
g.NextWindowData.CacheKeySet = true
g.NextWindowData.CacheKeyVal = key
end
function ImGui.GetWindowDpiScale()
@@ -10827,6 +10861,10 @@ function ImGui.End()
IM_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
@@ -35105,7 +35143,9 @@ function ImGuiNextWindowData()
SizeCallbackUserData = nil,
BgAlphaVal = nil,
MenuBarOffsetMinVal = ImVec2(),
RefreshFlagsVal = nil
RefreshFlagsVal = nil,
CacheKeySet = false, -- [Port] SetNextWindowCached()
CacheKeyVal = nil
}, IMGUI_NEXT_WINDOW_DATA)
end
@@ -36277,6 +36317,8 @@ function ImGuiWindow(ctx, name)
SkipItems = false,
SkipRefresh = false,
CacheSnap = nil, -- [Port] SetNextWindowCached(): state at the last refresh
CacheSkipItemsBackup = nil,
Appearing = false,
+1 -1
View File
@@ -3,7 +3,7 @@
"tree": {
"$className": "DataModel",
"ReplicatedStorage": {
"ImGui": { "$path": "build/ImGui.luau" }
"ImGui": { "$path": "build/ImGuiTyped.luau", "Impl": { "$path": "build/ImGui.luau" } }
},
"StarterPlayer": {
"StarterPlayerScripts": {
-1
View File
@@ -45,7 +45,6 @@ KNOWN ISSUES
PERFORMANCE IDEAS (not done)
----------------------------
- Opt-in cached windows (skip user code for idle windows).
- Cheaper visible ColorEdit4 rows; cheaper Begin() for child windows.
PLANNED EXTENSIONS
+452 -293
View File
@@ -1,336 +1,495 @@
-- LocalScript. Build the library first: `python3 tools/bundle.py` -> build/ImGui.luau (see default.project.json)
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local AssetService = game:GetService("AssetService")
local G = require(ReplicatedStorage:WaitForChild("ImGui"))
local ExampleImageViewerData, ExampleImageViewer_DrawOptions, ExampleImageViewer_DrawCanvas = G.ExampleImageViewerData, G.ExampleImageViewer_DrawOptions, G.ExampleImageViewer_DrawCanvas
local ImGui, ImVec4, ImGuiConfigFlags, ImGui_ImplRoblox = G.ImGui, G.ImVec4, G.ImGuiConfigFlags, G.ImGui_ImplRoblox
local BaseEnvironment = getfenv()
setfenv(1, setmetatable({}, {
__index = function(_, Key)
local Value = G[Key]
if Value ~= nil then
return Value
end
return BaseEnvironment[Key]
end,
}))
local main_scale = 1.0
-- Roblox UI points are multiplied by the OS display scale (e.g. 150%), which makes imgui look large.
-- Set this to your display scale to render imgui at physical-pixel resolution (same size as a desktop imgui app).
local render_scale = 1.0
local clear_color = ImVec4(0.45, 0.55, 0.60, 1.00)
local show_demo_window = true
local show_another_window = false
local f = 0.0
local counter = 0
local MainScale = 1.0
local RenderScale = 1.0
local RenderRate = 60
local ThumbnailSize = 420
local FontSize = 13.0
local FontNames = { "Cousine-Regular", "DroidSans", "Karla-Regular", "ProggyTiny", "Roboto-Medium" }
local State = {
ClearColor = ImVec4(0.45, 0.55, 0.60, 1.00),
ShowDemoWindow = true,
ShowAnotherWindow = false,
Float = 0.0,
Counter = 0,
}
local Spinner = {
Show = true,
Speed = 1.0,
Angle = 0.0,
Color = ImVec4(0.80, 0.65, 0.97, 1.0),
}
ImGui.CreateContext()
local io = ImGui.GetIO()
io.ConfigFlags = bit32.bor(io.ConfigFlags, ImGuiConfigFlags.NavEnableKeyboard, ImGuiConfigFlags.DockingEnable)
local IO = ImGui.GetIO()
IO.ConfigFlags = bit32.bor(IO.ConfigFlags, ImGuiConfigFlags.NavEnableKeyboard, ImGuiConfigFlags.DockingEnable)
-- Catppuccin Mocha, Mauve accent (https://catppuccin.com/palette)
local function StyleColorsCatppuccinMocha(style)
local function hex(h, a)
return ImVec4(bit32.extract(h, 16, 8) / 255, bit32.extract(h, 8, 8) / 255, bit32.extract(h, 0, 8) / 255, a or 1.0)
end
local crust, mantle, base = 0x11111b, 0x181825, 0x1e1e2e
local surface0, surface1, surface2 = 0x313244, 0x45475a, 0x585b70
local overlay0, overlay2, text = 0x6c7086, 0x9399b2, 0xcdd6f4
local mauve, lavender = 0xcba6f7, 0xb4befe
local C, c = G.ImGuiCol, style.Colors
local function set(idx, h, a) c[idx] = hex(h, a) end
set(C.Text, text); set(C.TextDisabled, overlay0)
set(C.WindowBg, base); set(C.ChildBg, base, 0.0)
set(C.PopupBg, mantle, 0.98); set(C.Border, surface1); set(C.BorderShadow, crust, 0.0)
set(C.FrameBg, surface0); set(C.FrameBgHovered, surface1); set(C.FrameBgActive, surface2)
set(C.TitleBg, mantle); set(C.TitleBgActive, crust); set(C.TitleBgCollapsed, mantle, 0.75)
set(C.MenuBarBg, mantle)
set(C.ScrollbarBg, mantle); set(C.ScrollbarGrab, surface1)
set(C.ScrollbarGrabHovered, surface2); set(C.ScrollbarGrabActive, overlay0)
set(C.CheckMark, mauve); set(C.SliderGrab, mauve, 0.8); set(C.SliderGrabActive, mauve)
set(C.Button, surface0); set(C.ButtonHovered, surface1); set(C.ButtonActive, mauve, 0.6)
set(C.Header, surface0); set(C.HeaderHovered, surface1); set(C.HeaderActive, mauve, 0.5)
set(C.Separator, surface1); set(C.SeparatorHovered, mauve, 0.7); set(C.SeparatorActive, mauve)
set(C.ResizeGrip, mauve, 0.2); set(C.ResizeGripHovered, mauve, 0.6); set(C.ResizeGripActive, mauve, 0.9)
set(C.InputTextCursor, text)
set(C.TabHovered, surface2); set(C.Tab, surface0)
set(C.TabSelected, surface1); set(C.TabSelectedOverline, mauve)
set(C.TabDimmed, mantle); set(C.TabDimmedSelected, surface0); set(C.TabDimmedSelectedOverline, overlay0)
set(C.DockingPreview, mauve, 0.7); set(C.DockingEmptyBg, crust)
set(C.PlotLines, overlay2); set(C.PlotLinesHovered, mauve)
set(C.PlotHistogram, mauve, 0.8); set(C.PlotHistogramHovered, lavender)
set(C.TableHeaderBg, surface0); set(C.TableBorderStrong, surface1); set(C.TableBorderLight, surface0)
set(C.TableRowBg, base, 0.0); set(C.TableRowBgAlt, surface0, 0.3)
set(C.TextLink, mauve); set(C.TextSelectedBg, mauve, 0.35)
set(C.TreeLines, surface2)
set(C.DragDropTarget, mauve, 0.9)
set(C.NavCursor, lavender); set(C.NavWindowingHighlight, lavender, 0.7)
set(C.NavWindowingDimBg, crust, 0.5); set(C.ModalWindowDimBg, crust, 0.6)
-- catppuccin mocha mauve https://catppuccin.com/palette
local function StyleColorsCatppuccinMocha(Style)
local function Hex(Value, Alpha)
return ImVec4(
bit32.extract(Value, 16, 8) / 255,
bit32.extract(Value, 8, 8) / 255,
bit32.extract(Value, 0, 8) / 255,
Alpha or 1.0
)
end
local Crust, Mantle, Base = 0x11111b, 0x181825, 0x1e1e2e
local Surface0, Surface1, Surface2 = 0x313244, 0x45475a, 0x585b70
local Overlay0, Overlay2, Text = 0x6c7086, 0x9399b2, 0xcdd6f4
local Mauve, Lavender = 0xcba6f7, 0xb4befe
local Entries = {
{ "Text", Text },
{ "TextDisabled", Overlay0 },
{ "WindowBg", Base },
{ "ChildBg", Base, 0.0 },
{ "PopupBg", Mantle, 0.98 },
{ "Border", Surface1 },
{ "BorderShadow", Crust, 0.0 },
{ "FrameBg", Surface0 },
{ "FrameBgHovered", Surface1 },
{ "FrameBgActive", Surface2 },
{ "TitleBg", Mantle },
{ "TitleBgActive", Crust },
{ "TitleBgCollapsed", Mantle, 0.75 },
{ "MenuBarBg", Mantle },
{ "ScrollbarBg", Mantle },
{ "ScrollbarGrab", Surface1 },
{ "ScrollbarGrabHovered", Surface2 },
{ "ScrollbarGrabActive", Overlay0 },
{ "CheckMark", Mauve },
{ "SliderGrab", Mauve, 0.8 },
{ "SliderGrabActive", Mauve },
{ "Button", Surface0 },
{ "ButtonHovered", Surface1 },
{ "ButtonActive", Mauve, 0.6 },
{ "Header", Surface0 },
{ "HeaderHovered", Surface1 },
{ "HeaderActive", Mauve, 0.5 },
{ "Separator", Surface1 },
{ "SeparatorHovered", Mauve, 0.7 },
{ "SeparatorActive", Mauve },
{ "ResizeGrip", Mauve, 0.2 },
{ "ResizeGripHovered", Mauve, 0.6 },
{ "ResizeGripActive", Mauve, 0.9 },
{ "InputTextCursor", Text },
{ "Tab", Surface0 },
{ "TabHovered", Surface2 },
{ "TabSelected", Surface1 },
{ "TabSelectedOverline", Mauve },
{ "TabDimmed", Mantle },
{ "TabDimmedSelected", Surface0 },
{ "TabDimmedSelectedOverline", Overlay0 },
{ "DockingPreview", Mauve, 0.7 },
{ "DockingEmptyBg", Crust },
{ "PlotLines", Overlay2 },
{ "PlotLinesHovered", Mauve },
{ "PlotHistogram", Mauve, 0.8 },
{ "PlotHistogramHovered", Lavender },
{ "TableHeaderBg", Surface0 },
{ "TableBorderStrong", Surface1 },
{ "TableBorderLight", Surface0 },
{ "TableRowBg", Base, 0.0 },
{ "TableRowBgAlt", Surface0, 0.3 },
{ "TextLink", Mauve },
{ "TextSelectedBg", Mauve, 0.35 },
{ "TreeLines", Surface2 },
{ "DragDropTarget", Mauve, 0.9 },
{ "NavCursor", Lavender },
{ "NavWindowingHighlight", Lavender, 0.7 },
{ "NavWindowingDimBg", Crust, 0.5 },
{ "ModalWindowDimBg", Crust, 0.6 },
}
for _, Entry in ipairs(Entries) do
local Name, Color, Alpha = Entry[1], Entry[2], Entry[3]
Style.Colors[ImGuiCol[Name]] = Hex(Color, Alpha)
end
end
local style = ImGui.GetStyle()
StyleColorsCatppuccinMocha(style)
style:ScaleAllSizes(main_scale)
style.FontScaleDpi = main_scale
local Style = ImGui.GetStyle()
StyleColorsCatppuccinMocha(Style)
Style:ScaleAllSizes(MainScale)
Style.FontScaleDpi = MainScale
-- Fonts: ProggyClean stays the default (added first); the rest show up in Style Editor > Fonts
io.Fonts:AddFontDefault()
for _, name in ipairs({ "Cousine-Regular", "DroidSans", "Karla-Regular", "ProggyTiny", "Roboto-Medium" }) do
io.Fonts:AddFontFromFileTTF("fonts/" .. name .. ".ttf", 13.0)
IO.Fonts:AddFontDefault()
for _, Name in ipairs(FontNames) do
IO.Fonts:AddFontFromFileTTF("fonts/" .. Name .. ".ttf", FontSize)
end
ImGui_ImplRoblox.Init(nil, render_scale)
ImGui_ImplRoblox.SetRenderRate(60) -- tile redraws per second; imgui logic + your code still run every frame
-- ImGui_ImplRoblox.SetBusyRenderRate(30) -- optional: lower redraw rate while scrolling/resizing/moving windows
ImGui_ImplRoblox.SetProfiling(true) -- MicroProfiler (Ctrl+F6) labels; turn off when not profiling
ImGui_ImplRoblox.Init(nil, RenderScale)
ImGui_ImplRoblox.SetRenderRate(RenderRate)
-- ImGui_ImplRoblox.SetBusyRenderRate(30) -- Optional: lower redraw rate while scrolling/resizing/moving windows
-- ImGui_ImplRoblox.SetProfiling(true) -- MicroProfiler labels; turn off when not profiling
-- ImGui_ImplRoblox.SetTextProxy(true) -- Optional: hidden TextBox for OS typing (any layout), paste and copy/cut
local function ProfileBegin(Name)
if ImGui_ImplRoblox.IsProfiling() then
debug.profilebegin(Name)
end
end
local function ProfileEnd()
if ImGui_ImplRoblox.IsProfiling() then
debug.profileend()
end
end
local function P(name) if ImGui_ImplRoblox.IsProfiling() then debug.profilebegin(name) end end
local function PE() if ImGui_ImplRoblox.IsProfiling() then debug.profileend() end end
--------------------------------------------------------------------------------------------------------------------
-- Custom widget: ImGui.Embed(id, size, flags, init) -> Frame
-- Reserves a widget-sized area in the current window and shows a real Frame there, so any Instances (UI, a
-- ViewportFrame, ...) can live inside an imgui window. Immediate-mode friendly:
-- * one Frame per id (per window, through the ID stack, like any widget); `init(frame)` runs only when it is created
-- * call it every frame the Frame should be visible; when it isn't called (window closed/collapsed, tab or tree node
-- closed, scrolled fully out of view) the Frame is hidden, never destroyed, so your Instances keep their state
-- * size uses imgui sizing (ImVec2(-1, h) = fill width, 0 = default), style gives padding/rounding/background/border
-- * returns the Frame: edit properties, add or remove children any time
-- ImGui.DestroyEmbed(id) removes it for good (call while the same window is current, like the widget itself).
--------------------------------------------------------------------------------------------------------------------
local EmbedFlags = {
None = 0,
NoClip = 1, -- don't clip to the window's content area (still moves/hides/stacks with the window)
NoBackground = 2, -- no FrameBg behind the Frame
NoBorder = 4, -- no style border
NoPadding = 8, -- no FramePadding inside the Frame (UIPadding)
NoRounding = 16, -- no FrameRounding corners (UICorner)
None = 0,
NoClip = 1, -- Don't clip to the window's content area (still moves/hides/stacks with the window)
NoBackground = 2, -- No FrameBg behind the Frame
NoBorder = 4, -- No style border
NoPadding = 8, -- No FramePadding inside the Frame (UIPadding)
NoRounding = 16, -- No FrameRounding corners (UICorner)
}
G.ImGuiEmbedFlags = EmbedFlags
local embeds = {} -- [ImGuiID] = { Frame, Padding, Corner }
local Embeds = {}
function ImGui.Embed(id, size, flags, init)
flags = flags or EmbedFlags.None
local window = ImGui.GetCurrentWindow()
local gid = window:GetID(id)
local e = embeds[gid]
if not e then
local frame = Instance.new("Frame")
frame.Name = tostring(id)
frame.BackgroundTransparency = 1
frame.BorderSizePixel = 0
frame.Visible = false
e = { Frame = frame, Padding = Instance.new("UIPadding"), Corner = Instance.new("UICorner") }
e.Padding.Parent = frame
e.Corner.Parent = frame
embeds[gid] = e
if init then init(frame) end
end
if window.SkipItems then return e.Frame end
local style = ImGui.GetStyle()
local sz = ImGui.CalcItemSize(size or G.ImVec2(0, 0), ImGui.CalcItemWidth(), ImGui.GetFrameHeight() * 4)
local p = window.DC.CursorPos
local bb = G.ImRect(p.x, p.y, p.x + sz.x, p.y + sz.y)
ImGui.ItemSize(sz)
if not ImGui.ItemAdd(bb, gid) then return e.Frame end -- fully clipped: not submitted -> hidden this frame
local rounding = bit32.band(flags, EmbedFlags.NoRounding) == 0 and style.FrameRounding or 0
if bit32.band(flags, EmbedFlags.NoBackground) == 0 then
ImGui.RenderFrame(bb.Min, bb.Max, ImGui.GetColorU32(G.ImGuiCol.FrameBg), bit32.band(flags, EmbedFlags.NoBorder) == 0, rounding)
elseif bit32.band(flags, EmbedFlags.NoBorder) == 0 then
ImGui.RenderFrameBorder(bb.Min, bb.Max, rounding)
end
-- style -> Instances (only written when changed)
local scale = ImGui_ImplRoblox.GetRenderScale()
local px, py = 0, 0
if bit32.band(flags, EmbedFlags.NoPadding) == 0 then px, py = style.FramePadding.x / scale, style.FramePadding.y / scale end
if e.PX ~= px or e.PY ~= py then
e.PX, e.PY = px, py
e.Padding.PaddingLeft, e.Padding.PaddingRight = UDim.new(0, px), UDim.new(0, px)
e.Padding.PaddingTop, e.Padding.PaddingBottom = UDim.new(0, py), UDim.new(0, py)
end
if e.R ~= rounding then e.R = rounding; e.Corner.CornerRadius = UDim.new(0, rounding / scale) end
ImGui_ImplRoblox.SetOverlay(e.Frame, bb.Min.x, bb.Min.y, sz.x, sz.y, bit32.band(flags, EmbedFlags.NoClip) == 0)
return e.Frame
local function HasFlag(Flags, Flag)
return bit32.band(Flags, Flag) ~= 0
end
function ImGui.DestroyEmbed(id)
local gid = ImGui.GetCurrentWindow():GetID(id)
local e = embeds[gid]
if e then e.Frame:Destroy(); embeds[gid] = nil end
local function CreateEmbed(Id)
local Frame = Instance.new("Frame")
Frame.Name = tostring(Id)
Frame.BackgroundTransparency = 1
Frame.BorderSizePixel = 0
Frame.Visible = false
local Padding = Instance.new("UIPadding")
Padding.Parent = Frame
local Corner = Instance.new("UICorner")
Corner.Parent = Frame
return { Frame = Frame, Padding = Padding, Corner = Corner }
end
-- Example: Decal Viewer. Loads a decal/image asset's pixels into an imgui texture and shows it in the demo's
-- image viewer (zoom with the wheel, drag to pan, toggleable pixel grid).
local AssetService = game:GetService("AssetService")
local MarketplaceService = game:GetService("MarketplaceService")
local decal = { input = ImGui.NewTextBuffer("", 32), status = "Enter a decal or image id", tex = nil, w = 0, h = 0, loading = false,
viewer = ExampleImageViewerData(), show = true }
function ImGui.Embed(Id, Size, Flags, Init)
Flags = Flags or EmbedFlags.None
-- A Decal asset wraps an Image asset with a different id; the image is almost always uploaded just before it
-- (same creator, id slightly lower). ponytail: heuristic scan of 10 ids, InsertService:LoadAsset when owned is exact.
local function ResolveImageId(id)
local ok, info = pcall(MarketplaceService.GetProductInfo, MarketplaceService, id)
if not ok then return nil, "GetProductInfo failed: " .. tostring(info) end
if info.AssetTypeId == 1 then return id end -- already an Image
if info.AssetTypeId ~= 13 then return nil, "asset is not a decal or image" end
for cand = id - 1, id - 10, -1 do
local ok2, ci = pcall(MarketplaceService.GetProductInfo, MarketplaceService, cand)
if ok2 and ci.AssetTypeId == 1 and ci.Creator and info.Creator and ci.Creator.Id == info.Creator.Id then return cand end
end
return nil, "couldn't find the decal's image id (try the image id directly)"
local Window = ImGui.GetCurrentWindow()
local GuiId = Window:GetID(Id)
local Embed = Embeds[GuiId]
if not Embed then
Embed = CreateEmbed(Id)
Embeds[GuiId] = Embed
if Init then
Init(Embed.Frame)
end
end
if Window.SkipItems then
return Embed.Frame
end
local EmbedStyle = ImGui.GetStyle()
local ItemSize = ImGui.CalcItemSize(
Size or ImVec2(0, 0),
ImGui.CalcItemWidth(),
ImGui.GetFrameHeight() * 4
)
local CursorPos = Window.DC.CursorPos
local BoundingBox = ImRect(CursorPos.x, CursorPos.y, CursorPos.x + ItemSize.x, CursorPos.y + ItemSize.y)
ImGui.ItemSize(ItemSize)
if not ImGui.ItemAdd(BoundingBox, GuiId) then
return Embed.Frame
end
local Rounding = HasFlag(Flags, EmbedFlags.NoRounding) and 0 or EmbedStyle.FrameRounding
local ShowBorder = not HasFlag(Flags, EmbedFlags.NoBorder)
if not HasFlag(Flags, EmbedFlags.NoBackground) then
ImGui.RenderFrame(
BoundingBox.Min,
BoundingBox.Max,
ImGui.GetColorU32(ImGuiCol.FrameBg),
ShowBorder,
Rounding
)
elseif ShowBorder then
ImGui.RenderFrameBorder(BoundingBox.Min, BoundingBox.Max, Rounding)
end
local Scale = ImGui_ImplRoblox.GetRenderScale()
local PaddingX, PaddingY = 0, 0
if not HasFlag(Flags, EmbedFlags.NoPadding) then
PaddingX = EmbedStyle.FramePadding.x / Scale
PaddingY = EmbedStyle.FramePadding.y / Scale
end
if Embed.PaddingX ~= PaddingX or Embed.PaddingY ~= PaddingY then
Embed.PaddingX, Embed.PaddingY = PaddingX, PaddingY
local Padding = Embed.Padding
Padding.PaddingLeft = UDim.new(0, PaddingX)
Padding.PaddingRight = UDim.new(0, PaddingX)
Padding.PaddingTop = UDim.new(0, PaddingY)
Padding.PaddingBottom = UDim.new(0, PaddingY)
end
if Embed.Rounding ~= Rounding then
Embed.Rounding = Rounding
Embed.Corner.CornerRadius = UDim.new(0, Rounding / Scale)
end
ImGui_ImplRoblox.SetOverlay(
Embed.Frame,
BoundingBox.Min.x,
BoundingBox.Min.y,
ItemSize.x,
ItemSize.y,
not HasFlag(Flags, EmbedFlags.NoClip)
)
return Embed.Frame
end
local function LoadDecal(id)
decal.loading = true
decal.status = "Loading " .. id .. "..."
task.spawn(function()
local image_id, err = ResolveImageId(id)
decal.thumb = nil
if image_id then
-- 1) the image itself (needs permission), 2) its 420x420 thumbnail (may be allowed when 1 isn't)
local ok, img = pcall(AssetService.CreateEditableImageAsync, AssetService, Content.fromUri("rbxassetid://" .. image_id))
if not ok then
local ok2, img2 = pcall(AssetService.CreateEditableImageAsync, AssetService, Content.fromUri("rbxthumb://type=Asset&id=" .. id .. "&w=420&h=420"))
if ok2 then ok, img = true, img2 end
end
if ok then
local size = img.Size
local pixels = img:ReadPixelsBuffer(Vector2.zero, size)
img:Destroy()
if decal.tex then ImGui_ImplRoblox.DestroyTexture(decal.tex) end
decal.thumb = nil
decal.tex, decal.w, decal.h = ImGui_ImplRoblox.CreateTexture(pixels, size.X, size.Y), size.X, size.Y
decal.viewer.ViewReset = true
decal.status = string.format("Image %d: %dx%d", image_id, size.X, size.Y)
else
-- 3) no pixel access: Roblox still displays any asset, so show its thumbnail in an ImageLabel (no grid)
decal.thumb = "rbxthumb://type=Asset&id=" .. id .. "&w=420&h=420"
decal.status = "No pixel access (asset not owned by this experience's creator): display only, no zoom/grid"
end
else
decal.thumb = "rbxthumb://type=Asset&id=" .. id .. "&w=420&h=420"
decal.status = (err or "") .. " - showing thumbnail"
end
decal.loading = false
end)
function ImGui.DestroyEmbed(Id)
local GuiId = ImGui.GetCurrentWindow():GetID(Id)
local Embed = Embeds[GuiId]
if Embed then
Embed.Frame:Destroy()
Embeds[GuiId] = nil
end
end
local Decal = {
Show = true,
Input = ImGui.NewTextBuffer("", 32),
Status = "Enter a decal or image id",
Loading = false,
Texture = nil,
Width = 0,
Height = 0,
Viewer = ExampleImageViewerData(),
}
local function LoadDecal(Id)
Decal.Loading = true
Decal.Status = "Loading " .. Id .. "..."
task.spawn(function()
local Uri = string.format("rbxthumb://type=Asset&id=%d&w=%d&h=%d", Id, ThumbnailSize, ThumbnailSize)
local Success, Result = pcall(AssetService.CreateEditableImageAsync, AssetService, Content.fromUri(Uri))
if Success then
local Size = Result.Size
local Pixels = Result:ReadPixelsBuffer(Vector2.zero, Size)
Result:Destroy()
if Decal.Texture then
ImGui_ImplRoblox.DestroyTexture(Decal.Texture)
end
Decal.Texture = ImGui_ImplRoblox.CreateTexture(Pixels, Size.X, Size.Y)
Decal.Width, Decal.Height = Size.X, Size.Y
Decal.Viewer.ViewReset = true
Decal.Status = string.format("Thumbnail %d: %dx%d", Id, Size.X, Size.Y)
else
Decal.Status = "Failed to load thumbnail: " .. tostring(Result)
end
Decal.Loading = false
end)
end
local function ShowDecalViewer()
if not decal.show then return end
decal.show = ImGui.Begin("Decal Viewer", decal.show)
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 12)
local enter = ImGui.InputTextWithHint("##id", "decal id", decal.input, 32, G.ImGuiInputTextFlags.CharsDecimal + G.ImGuiInputTextFlags.EnterReturnsTrue)
ImGui.SameLine()
ImGui.BeginDisabled(decal.loading)
if (ImGui.Button("Load") or enter) and not decal.loading then
local id = tonumber(ImGui.TextBufferToString(decal.input))
if id then LoadDecal(id) else decal.status = "Not a number" end
end
ImGui.EndDisabled()
ImGui.SameLine(); ImGui.TextDisabled(decal.status)
if decal.thumb then
local avail = ImGui.GetContentRegionAvail()
local frame = ImGui.Embed("decal_thumb", G.ImVec2(-1, math.max(avail.y, 50)), G.ImGuiEmbedFlags.None, function(f)
local img = Instance.new("ImageLabel")
img.Name = "Image"
img.Size = UDim2.fromScale(1, 1)
img.BackgroundTransparency = 1
img.ScaleType = Enum.ScaleType.Fit
img.Parent = f
end)
if frame.Image.Image ~= decal.thumb then frame.Image.Image = decal.thumb end
elseif decal.tex then
ExampleImageViewer_DrawOptions(decal.viewer)
local avail = ImGui.GetContentRegionAvail()
ExampleImageViewer_DrawCanvas(decal.viewer, G.ImVec2(math.max(avail.x, 50), math.max(avail.y, 50)), decal.tex, decal.w, decal.h)
end
ImGui.End()
if not Decal.Show then
return
end
Decal.Show = ImGui.Begin("Decal Viewer", Decal.Show)
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 12)
local EnterPressed = ImGui.InputTextWithHint(
"##id",
"decal id",
Decal.Input,
32,
ImGuiInputTextFlags.CharsDecimal + ImGuiInputTextFlags.EnterReturnsTrue
)
ImGui.SameLine()
ImGui.BeginDisabled(Decal.Loading)
if (ImGui.Button("Load") or EnterPressed) and not Decal.Loading then
local Id = tonumber(ImGui.TextBufferToString(Decal.Input))
if Id and Id == math.floor(Id) then
LoadDecal(Id)
else
Decal.Status = "Not a valid id"
end
end
ImGui.EndDisabled()
ImGui.SameLine()
ImGui.TextDisabled(Decal.Status)
if Decal.Texture then
ExampleImageViewer_DrawOptions(Decal.Viewer)
local Available = ImGui.GetContentRegionAvail()
ExampleImageViewer_DrawCanvas(
Decal.Viewer,
ImVec2(math.max(Available.x, 50), math.max(Available.y, 50)),
Decal.Texture,
Decal.Width,
Decal.Height
)
end
ImGui.End()
end
-- Example: a ViewportFrame with a spinning part, edited live from imgui widgets
local spin_speed = 1.0
local part_color = ImVec4(0.80, 0.65, 0.97, 1.0)
local spin_angle = 0.0
local show_viewport_window = true
local function ShowHelloWindow()
ImGui.Begin("Hello, world!")
ImGui.Text("This is some useful text.")
local _
_, State.ShowDemoWindow = ImGui.Checkbox("Demo Window", State.ShowDemoWindow)
_, State.ShowAnotherWindow = ImGui.Checkbox("Another Window", State.ShowAnotherWindow)
State.Float = ImGui.SliderFloat("float", State.Float, 0.0, 1.0)
ImGui.ColorEdit3("clear color", State.ClearColor)
if ImGui.Button("Button") then
State.Counter += 1
end
ImGui.SameLine()
ImGui.Text("counter = %d", State.Counter)
ImGui.Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0 / IO.Framerate, IO.Framerate)
ImGui.End()
end
local function ShowAnotherWindow()
if not State.ShowAnotherWindow then
return
end
State.ShowAnotherWindow = ImGui.Begin("Another Window", State.ShowAnotherWindow)
ImGui.Text("Hello from another window!")
if ImGui.Button("Close Me") then
State.ShowAnotherWindow = false
end
ImGui.End()
end
local function CreateSpinnerViewport(Container)
local Viewport = Instance.new("ViewportFrame")
Viewport.Size = UDim2.fromScale(1, 1)
Viewport.BackgroundTransparency = 1
Viewport.Parent = Container
local Camera = Instance.new("Camera")
Camera.CFrame = CFrame.lookAt(Vector3.new(0, 2, 6), Vector3.zero)
Camera.Parent = Viewport
Viewport.CurrentCamera = Camera
local Part = Instance.new("Part")
Part.Name = "Spinner"
Part.Size = Vector3.new(2, 2, 2)
Part.Parent = Viewport
end
local function ShowViewportWindow()
if not Spinner.Show then
return
end
Spinner.Show = ImGui.Begin("Viewport", Spinner.Show)
Spinner.Speed = ImGui.SliderFloat("speed", Spinner.Speed, 0.0, 5.0)
ImGui.ColorEdit3("color", Spinner.Color)
local Frame = ImGui.Embed("viewport", ImVec2(-1, 220), EmbedFlags.None, CreateSpinnerViewport)
Spinner.Angle += Spinner.Speed * IO.DeltaTime
local Part = Frame.ViewportFrame.Spinner
Part.CFrame = CFrame.Angles(0.4, Spinner.Angle, 0)
Part.Color = Color3.new(Spinner.Color.x, Spinner.Color.y, Spinner.Color.z)
ImGui.End()
end
RunService.Stepped:Connect(function()
P("ImGui frame")
ImGui_ImplRoblox.NewFrame()
P("ImGui.NewFrame"); ImGui.NewFrame(); PE()
P("ImGui UI")
ProfileBegin("ImGui frame")
ImGui_ImplRoblox.NewFrame()
-- Dockspace covering the whole screen; the centre stays transparent so the game shows through
ImGui.DockSpaceOverViewport(0, nil, G.ImGuiDockNodeFlags.PassthruCentralNode)
ProfileBegin("ImGui.NewFrame")
ImGui.NewFrame()
ProfileEnd()
if show_demo_window then
ImGui.ShowDemoWindow()
end
ProfileBegin("ImGui UI")
ImGui.DockSpaceOverViewport(0, nil, ImGuiDockNodeFlags.PassthruCentralNode)
do
ImGui.Begin("Hello, world!")
if State.ShowDemoWindow then
-- For reference: https://github.com/4DBug/imgui-luau/blob/master/imgui_demo.lua
ImGui.ShowDemoWindow()
end
ImGui.Text("This is some useful text.")
local _
_, show_demo_window = ImGui.Checkbox("Demo Window", show_demo_window)
_, show_another_window = ImGui.Checkbox("Another Window", show_another_window)
ShowHelloWindow()
ShowAnotherWindow()
ShowDecalViewer()
ShowViewportWindow()
ProfileEnd()
f = ImGui.SliderFloat("float", f, 0.0, 1.0)
ImGui.ColorEdit3("clear color", clear_color)
ProfileBegin("ImGui.Render")
ImGui.EndFrame()
ImGui.Render()
ProfileEnd()
if ImGui.Button("Button") then
counter = counter + 1
end
ImGui.SameLine()
ImGui.Text("counter = %d", counter)
ImGui.Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0 / io.Framerate, io.Framerate)
ImGui.End()
end
if show_another_window then
show_another_window = ImGui.Begin("Another Window", show_another_window)
ImGui.Text("Hello from another window!")
if ImGui.Button("Close Me") then
show_another_window = false
end
ImGui.End()
end
PE()
ShowDecalViewer()
if show_viewport_window then
show_viewport_window = ImGui.Begin("Viewport", show_viewport_window)
spin_speed = ImGui.SliderFloat("speed", spin_speed, 0.0, 5.0)
ImGui.ColorEdit3("color", part_color)
local frame = ImGui.Embed("viewport", G.ImVec2(-1, 220), G.ImGuiEmbedFlags.None, function(f)
-- runs once
local vpf = Instance.new("ViewportFrame")
vpf.Size = UDim2.fromScale(1, 1)
vpf.BackgroundTransparency = 1
vpf.Parent = f
local cam = Instance.new("Camera")
cam.CFrame = CFrame.lookAt(Vector3.new(0, 2, 6), Vector3.new(0, 0, 0))
cam.Parent = vpf
vpf.CurrentCamera = cam
local part = Instance.new("Part")
part.Name = "Spinner"
part.Size = Vector3.new(2, 2, 2)
part.Parent = vpf
end)
-- every frame: edit what was created once
spin_angle += spin_speed * io.DeltaTime
local part = frame.ViewportFrame.Spinner
part.CFrame = CFrame.Angles(0.4, spin_angle, 0)
part.Color = Color3.new(part_color.x, part_color.y, part_color.z)
ImGui.Text("Frames created: 1 (same Frame every frame)")
ImGui.End()
end
P("ImGui.Render"); ImGui.EndFrame(); ImGui.Render(); PE()
ImGui_ImplRoblox.RenderDrawData(ImGui.GetDrawData())
PE()
ImGui_ImplRoblox.RenderDrawData(ImGui.GetDrawData())
ProfileEnd()
end)
+1 -1
View File
@@ -3,7 +3,7 @@
"tree": {
"$className": "DataModel",
"ReplicatedStorage": {
"ImGui": { "$path": "../../build/ImGui.luau" },
"ImGui": { "$path": "../../build/ImGuiTyped.luau", "Impl": { "$path": "../../build/ImGui.luau" } },
"SharedApp": { "$path": "app.lua" }
},
"StarterPlayer": {
+43 -1
View File
@@ -8626,9 +8626,13 @@ local function RenderWindowDecorations(window, title_bar_rect, title_bar_is_high
end
if override_alpha then
bg_col = bit32.band(bg_col, bit32.bnot(IM_COL32_A_MASK)) or bit32.lshift(IM_F32_TO_INT8_SAT(alpha), IM_COL32_A_SHIFT)
bg_col = bit32.bor(bit32.band(bg_col, bit32.bnot(IM_COL32_A_MASK)), bit32.lshift(IM_F32_TO_INT8_SAT(alpha), 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, ImGuiWindowFlags.DockNodeHost) ~= 0 then bg_col = 0 end
if bit32.band(bg_col, IM_COL32_A_MASK) ~= 0 then
local bg_rect = ImRect(window.Pos + ImVec2(0, window.TitleBarHeight), window.Pos + window.Size)
local bg_rounding_flags
@@ -8846,6 +8850,26 @@ function ImGui.UpdateWindowSkipRefresh(window)
end
if bit32.band(g.NextWindowData.RefreshFlagsVal, 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 < FLT_MAX or window.ScrollTarget.y < 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
@@ -9477,6 +9501,20 @@ function ImGui.SetNextWindowRefreshPolicy(flags)
local g = GImGui
g.NextWindowData.HasFlags = bit32.bor(g.NextWindowData.HasFlags, 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(ImGuiWindowRefreshFlags.TryToAvoidRefresh, ImGuiWindowRefreshFlags.RefreshOnHover, ImGuiWindowRefreshFlags.RefreshOnFocus))
g.NextWindowData.CacheKeySet = true
g.NextWindowData.CacheKeyVal = key
end
function ImGui.GetWindowDpiScale()
@@ -10679,6 +10717,10 @@ function ImGui.End()
IM_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
+5 -1
View File
@@ -1236,7 +1236,9 @@ function ImGuiNextWindowData()
SizeCallbackUserData = nil,
BgAlphaVal = nil,
MenuBarOffsetMinVal = ImVec2(),
RefreshFlagsVal = nil
RefreshFlagsVal = nil,
CacheKeySet = false, -- [Port] SetNextWindowCached()
CacheKeyVal = nil
}, IMGUI_NEXT_WINDOW_DATA)
end
@@ -2408,6 +2410,8 @@ function ImGuiWindow(ctx, name)
SkipItems = false,
SkipRefresh = false,
CacheSnap = nil, -- [Port] SetNextWindowCached(): state at the last refresh
CacheSkipItemsBackup = nil,
Appearing = false,
+5
View File
@@ -6,6 +6,7 @@
# Why: ModuleScripts don't share globals, and the setfenv() alternative turns off Luau's fast paths / native codegen.
# Each source file becomes a function in __FILES; IM_INCLUDE(name) runs it once and caches the result.
# The module returns a table with every global the library defines (ImGui, ImVec2, ImGuiConfigFlags, ImGui_ImplRoblox, ...).
# Roblox target also writes ImGuiTyped.luau (types + require(script.Impl), see tools/gen_types.py).
import os, re
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -64,4 +65,8 @@ NAME = "imgui_love.lua" if LOVE else "ImGui.luau"
open(os.path.join(OUT, NAME), "w").write("\n".join(out) + "\n")
import json
json.dump(linemap, open(os.path.join(OUT, NAME.rsplit(".", 1)[0] + ".linemap.json"), "w"))
if not LOVE:
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from gen_types import wrapper
open(os.path.join(OUT, "ImGuiTyped.luau"), "w").write(wrapper(FILES))
print("wrote build/%s (%d files, %d exports)" % (NAME, len(FILES), len(names)))
+135
View File
@@ -0,0 +1,135 @@
#!/usr/bin/env python3
# Typed entry module for the Roblox build: bundle.py writes build/ImGuiTyped.luau = Luau types + `require(script.Impl)`,
# where Impl is the untouched build/ImGui.luau (the bundle itself is too large for Luau's type inference to finish,
# so the types live in this small wrapper instead). Library sources and the LÖVE build are not affected.
# Public ImGui.* signatures come from upstream imgui.h (namespace ImGui), mapped onto the port's own parameter lists.
# Anything ambiguous (overloads, char* buffers, arrays, multiple out-params, count mismatch) falls back to `any`,
# so the types never reject code that runs. Enum tables (ImGuiWindowFlags, ImGuiCol, ...) get their exact keys.
# Self-check: python3 tools/gen_types.py (prints a few signatures)
import os, re
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
NUM = re.compile(r"^(int|float|double|unsigned int|unsigned short|short|size_t|ImU32|ImS32|ImU64|ImS64|ImWchar|ImGuiID|Im\w*Flags|ImGui(Col|Cond|StyleVar|Key|MouseButton|MouseCursor|Dir|DataType|SortDirection|TableBgTarget|Axis))$")
VEC = {"ImVec2": "ImVec2", "ImVec4": "ImVec4"}
def lua_type(ctype):
t = re.sub(r"\bconst\b|&", "", ctype).strip()
t = re.sub(r"\s+", " ", t)
if t == "char*": return "string" if "const" in ctype else None
if t == "bool": return "boolean"
if NUM.match(t): return "number"
return VEC.get(t)
def split_params(s):
out, depth, cur = [], 0, ""
for ch in s:
if ch in "([": depth += 1
if ch in ")]": depth -= 1
if ch == "," and depth == 0: out.append(cur); cur = ""
else: cur += ch
if cur.strip(): out.append(cur)
return [p.strip() for p in out]
def upstream_decls():
src = open(os.path.join(ROOT, "tools", "test", "upstream", "imgui.h")).read()
body = src[src.index("namespace ImGui\n"):src.index("} // namespace ImGui")]
decls = {}
for m in re.finditer(r"IMGUI_API\s+([\w\s\*&]+?)\s*\b(\w+)\((.*?)\)\s*(?:IM_FMT\w+\(\d+\))?\s*;", body):
decls.setdefault(m.group(2), []).append((m.group(1).strip(), split_params(m.group(3)) if m.group(3).strip() not in ("", "void") else []))
return decls
LOOSE = "(...any) -> ...any"
PORT_ONLY = { # port additions that aren't in imgui.h
"SetNextWindowCached": "(key: any) -> ()",
"NewTextBuffer": "(str: string, size: number?) -> {number}",
"TextBufferToString": "(buf: {number}) -> string",
}
def signature(port_params, ret, cparams):
if any(p == "..." for p in cparams):
if port_params and port_params[-1] == "...": port_params = port_params[:-1]
cparams = [p for p in cparams if p != "..."]; vararg = True
else: vararg = False
if len(port_params) != len(cparams): return LOOSE
args, outs = [], []
for name, cp in zip(port_params, cparams):
decl, _, default = cp.partition("=")
m = re.match(r"(.*?)\b(\w+)\s*(\[\w*\])?\s*$", decl.strip())
if not m: return LOOSE
ctype, array = m.group(1).strip(), m.group(3)
if "(*" in cp: return LOOSE
ptr = ctype.endswith("*") and not re.search(r"\bchar\s*\*$", ctype)
if array: t = None; outs.append("array")
elif ptr and re.search(r"\bconst\b", ctype): t = None # input array (e.g. ColorPicker4 ref_col)
elif ptr:
t = lua_type(ctype[:-1])
if t in ("boolean", "number"): outs.append(t)
else: t = None; outs.append("other")
else: t = lua_type(ctype)
if name == "...": args.append("...any"); continue
opt = "?" if (default.strip() or (ptr and t == "boolean")) else ""
args.append("%s: %s%s" % (name, t or "any", opt if t else ""))
if vararg: args.append("...any")
r = re.sub(r"\bconst\b|&|IMGUI_API", "", ret).strip()
if r == "void": rt = "...any" if outs else "()"
elif r == "bool":
scal = [o for o in outs if o != "array"]
if not scal: rt = "boolean"
elif len(scal) == 1 and scal[0] == "boolean": rt = "(boolean, boolean?)"
elif len(scal) == 1 and scal[0] == "number": rt = "(number, boolean)"
else: rt = "...any"
else:
rt = lua_type(r) or "any"
if outs: rt = "...any"
return "(%s) -> %s" % (", ".join(args), rt)
def port_functions(files):
fns = {}
for src in files.values():
for m in re.finditer(r"^function ImGui\.(\w+)\((.*?)\)", src, re.M):
fns[m.group(1)] = [p.strip() for p in m.group(2).split(",") if p.strip()]
return fns
def enums(files):
src = files.get("imgui_h.lua", "")
out = {}
for m in re.finditer(r"^(Im\w+) = \{\n(.*?)^\}", src, re.M | re.S):
keys = re.findall(r"^\s+(\w+)\s*=", m.group(2), re.M)
if keys: out[m.group(1)] = keys
for f in files.values(): # keys added later (ImGuiKey letters in loops are covered by the indexer below)
for name, key in re.findall(r"^\s*(Im\w+)\.(\w+)\s*=", f, re.M):
if name in out and key not in out[name]: out[name].append(key)
return out
def types_header(files):
decls, fns = upstream_decls(), port_functions(files)
lines = ["export type ImVec2 = { x: number, y: number, [any]: any }",
"export type ImVec4 = { x: number, y: number, z: number, w: number, [any]: any }",
"export type ImGuiAPI = {"]
for name in sorted(fns):
d = decls.get(name)
sig = PORT_ONLY.get(name) or (signature(fns[name], *d[0]) if d and len(d) == 1 else LOOSE)
lines.append(" %s: %s," % (name, sig))
lines += [" [string]: any,", "}"]
for name, keys in sorted(enums(files).items()):
lines.append("export type %s = { %s, [string]: number }" % (name, ", ".join("%s: number" % k for k in keys)))
return lines + module_type(set(enums(files)))
def wrapper(files):
return "\n".join(["--!strict", "-- GENERATED by tools/gen_types.py - do not edit. The library is the child ModuleScript 'Impl'.", ""]
+ types_header(files) + ["", "return (require(script:WaitForChild(\"Impl\")) :: any) :: ImGuiModule", ""])
def module_type(enum_names):
# the bundle is too large for Luau to infer, so its return value is cast to this explicit type
lines = ["export type ImGuiModule = {", " ImGui: ImGuiAPI,"]
lines += [" %s: (x: number?, y: number?, z: number?, w: number?) -> %s," % (n, n) for n in ("ImVec2", "ImVec4")]
lines += [" %s: %s," % (n, n) for n in sorted(enum_names)]
return lines + [" [string]: any,", "}"]
if __name__ == "__main__":
import glob
files = {os.path.basename(f): open(f).read() for f in glob.glob(os.path.join(ROOT, "*.lua"))}
lines = types_header(files)
for l in lines:
if re.match(r" (Begin|Checkbox|SliderFloat|SliderFloat3|Text|Button|Combo|GetCursorPos|InputText|DragFloatRange2):", l): print(l)
loose = sum(LOOSE in l for l in lines); print("%d functions, %d loose" % (sum(l.startswith(" ") for l in lines), loose))
+58
View File
@@ -0,0 +1,58 @@
-- SetNextWindowCached: the same scripted scenario runs twice, window "Win##A" uncached then "Win##B" cached.
-- Every frame the window's draw list must hash identically in both phases, while B's body runs far less often.
-- run: python3 tools/test/run.py --main tools/test/scripts/t_cached_window.luau --frames 130 --quiet --roblox
local G = require(game:GetService("ReplicatedStorage"):WaitForChild("ImGui"))
local ImGui, ImVec2 = G.ImGui, G.ImVec2
ImGui.CreateContext()
G.ImGui_ImplRoblox.Init()
G.ImGui_ImplRoblox.SetRenderRate(0)
local io = ImGui.GetIO()
local P = 60 -- frames per phase
local frame, hashes, runs = 0, {}, { A = 0, B = 0 }
local function hash(dl)
local h = 0
local function mix(v) h = (h * 31 + math.floor(v * 64 + 0.5)) % 2147483647 end
local vb, id = dl.VtxBuffer.Buf, dl.IdxBuffer.Data
for i = 1, dl.VtxBuffer.Size do local o = (i - 1) * 36; mix(buffer.readf64(vb, o)); mix(buffer.readf64(vb, o + 8)); mix(buffer.readf64(vb, o + 16) * 4096); mix(buffer.readf64(vb, o + 24) * 4096); mix(buffer.readu32(vb, o + 32) / 64) end
for i = 1, dl.IdxBuffer.Size do mix(id[i]) end
for _, c in dl.CmdBuffer:iter() do mix(c.ClipRect.x); mix(c.ClipRect.y); mix(c.ClipRect.z); mix(c.ClipRect.w); mix(c.ElemCount) end
return h
end
game:GetService("RunService").Stepped:Connect(function()
frame += 1
if frame > 2 * P then return end
local phase = frame <= P and "A" or "B"
local t = (frame - 1) % P + 1 -- frame within the phase
G.ImGui_ImplRoblox.NewFrame()
io.DeltaTime = 1 / 60
-- mouse: away, over the button (hover highlight), away again
if t >= 20 and t < 26 then io:AddMousePosEvent(60, 75) else io:AddMousePosEvent(700, 500) end
local key = math.floor(t / 15) -- data version: changes at t = 15, 30, 45
ImGui.NewFrame()
if t == 3 then ImGui.SetWindowFocus(nil) end -- focused -> unfocused title bar must not stay stale
if t == 1 then ImGui.SetNextWindowFocus() end
ImGui.SetNextWindowPos(ImVec2(20, 20), G.ImGuiCond.Always)
ImGui.SetNextWindowSize(ImVec2(220, 140), G.ImGuiCond.Always)
if phase == "B" then ImGui.SetNextWindowCached(key) end
local _, visible = ImGui.Begin("Win##" .. phase)
if visible then
runs[phase] += 1
ImGui.Button("Button")
ImGui.Text("value %d", key)
ImGui.SliderFloat("slider", key * 0.1, 0, 1)
end
ImGui.End()
ImGui.Render()
local h = hash(ImGui.FindWindowByName("Win##" .. phase).DrawList)
if phase == "A" then hashes[t] = h
elseif h ~= hashes[t] then error(("frame %d: cached draw list differs from uncached"):format(t)) end
G.ImGui_ImplRoblox.RenderDrawData(ImGui.GetDrawData())
if frame == 2 * P then
print(("runs: uncached %d, cached %d"):format(runs.A, runs.B))
assert(runs.A == P, "uncached window must run every frame")
assert(runs.B < P / 3, "cached window ran too often")
print("CACHED OK")
end
end)
+68
View File
@@ -0,0 +1,68 @@
-- Docked windows (floating split node) stop being submitted: the host lingers one frame, its bodies must be filled
-- with the windows' bg colour (node.LastBgColor), not white, and everything must be gone within a few frames.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local G = require(ReplicatedStorage:WaitForChild("ImGui"))
local ImGui, ImVec2 = G.ImGui, G.ImVec2
ImGui.CreateContext()
local io = ImGui.GetIO()
io.ConfigFlags = bit32.bor(io.ConfigFlags, G.ImGuiConfigFlags.DockingEnable)
G.ImGui_ImplRoblox.Init()
local frame, closed_at, done = 0, 31, false
RunService.Stepped:Connect(function()
if done then return end
frame += 1
G.ImGui_ImplRoblox.NewFrame()
ImGui.NewFrame()
if frame == 2 then
local id = ImGui.GetID("floating")
ImGui.DockBuilderRemoveNode(id)
ImGui.DockBuilderAddNode(id, 0)
ImGui.DockBuilderSetNodePos(id, ImVec2(100, 100))
ImGui.DockBuilderSetNodeSize(id, ImVec2(600, 400))
local left, right = ImGui.DockBuilderSplitNode(id, G.ImGuiDir.Left, 0.5)
ImGui.DockBuilderDockWindow("A", left)
ImGui.DockBuilderDockWindow("B", right)
ImGui.DockBuilderFinish(id)
end
if frame < closed_at then
for _, n in { "A", "B" } do
if ImGui.Begin(n) then ImGui.Text("window " .. n) end
ImGui.End()
end
end
if frame == closed_at - 1 then
local a, b = ImGui.FindWindowByName("A"), ImGui.FindWindowByName("B")
assert(a.DockNode and b.DockNode and a.DockNode ~= b.DockNode and a.DockNode.HostWindow, "A, B docked side by side")
end
ImGui.Render()
local dd = ImGui.GetDrawData()
if frame >= closed_at then
for _, dl in dd.CmdLists:iter() do
-- white vertices spanning a big area = a white body fill (white text glyphs are small)
local vb, x0, y0, x1, y1 = dl.VtxBuffer.Buf, math.huge, math.huge, -math.huge, -math.huge
for _, pcmd in dl.CmdBuffer:iter() do
for i = pcmd.IdxOffset + 1, pcmd.IdxOffset + pcmd.ElemCount do
local o = (pcmd.VtxOffset + dl.IdxBuffer.Data[i] - 1) * G.VTX_STRIDE
if buffer.readu32(vb, o + 32) == 0xFFFFFFFF then
local x, y = buffer.readf64(vb, o), buffer.readf64(vb, o + 8)
x0, y0, x1, y1 = math.min(x0, x), math.min(y0, y), math.max(x1, x), math.max(y1, y)
end
end
end
assert(not (x1 - x0 > 100 and y1 - y0 > 100), "white fill on frame " .. frame)
end
if dd.TotalVtxCount == 0 then
G.ImGui_ImplRoblox.RenderDrawData(dd)
for _, set in io.BackendPlatformUserData.Layers do
for kind = 1, 3 do assert(not (set[kind] and set[kind].Visible), "backend layer still visible after close") end
end
print("DOCK CLOSE OK after", frame - closed_at + 1, "frames")
done = true
return
else
assert(frame < closed_at + 3, "docked windows still drawn " .. (frame - closed_at + 1) .. " frames after close")
end
end
G.ImGui_ImplRoblox.RenderDrawData(dd)
end)
+13 -1
View File
@@ -124,12 +124,24 @@ local function MyTool()
end
-- [/snippet]
-- [snippet:cached]
local stats = { version = 0, kills = 0 }
local function StatsWindow()
ImGui.SetNextWindowCached(stats.version) -- bump stats.version whenever stats change
local _, visible = ImGui.Begin("Stats")
if visible then -- false while the cached contents are reused
ImGui.Text("Kills: %d", stats.kills)
end
ImGui.End()
end
-- [/snippet]
local frame = 0
RunService.Stepped:Connect(function()
frame += 1
G.ImGui_ImplRoblox.NewFrame()
ImGui.NewFrame()
MyFirstTool(); MyTool(); Conventions(); MenusAndTable(); Plot(); CustomDrawing()
MyFirstTool(); MyTool(); Conventions(); MenusAndTable(); Plot(); CustomDrawing(); StatsWindow()
ImGui.Render()
G.ImGui_ImplRoblox.RenderDrawData(ImGui.GetDrawData())
if frame == 20 then
+1 -1
View File
@@ -2,7 +2,7 @@
local G = require(game:GetService("ReplicatedStorage"):WaitForChild("ImGui"))
local ImGui, ImVec2 = G.ImGui, G.ImVec2
local UIS = game:GetService("UserInputService")
ImGui.CreateContext(); G.ImGui_ImplRoblox.Init()
ImGui.CreateContext(); G.ImGui_ImplRoblox.Init(); G.ImGui_ImplRoblox.SetTextProxy(true)
local io = ImGui.GetIO()
local buf = ImGui.NewTextBuffer("", 64)
local proxy
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "4dbug/imgui-luau"
version = "0.1.3"
registry = "https://github.com/UpliftGames/wally-index"
realm = "shared"
license = "MIT"
description = "Dear ImGui (docking branch) ported to Luau for Roblox"
repository = "https://github.com/4DBug/imgui-luau"
[dependencies]