Compare commits
15
Commits
c085571602
...
v0.1.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce82582e37 | ||
|
|
090525076f | ||
|
|
95fdf94912 | ||
|
|
e502866075 | ||
|
|
1431d2e19d | ||
|
|
7eb7d5fcbb | ||
|
|
2558c30d32 | ||
|
|
e228ed8638 | ||
|
|
28dc08e043 | ||
|
|
f63a37f07d | ||
|
|
08356b4f1f | ||
|
|
14e6da3b29 | ||
|
|
df4cf4cd80 | ||
|
|
2bef2ad43a | ||
|
|
df9f194d87 |
+40
-24
@@ -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.
|
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) |
|
| [Differences from C++](#differences-from-c-dear-imgui) - [Performance](#performance) - [Testing](#testing) - [Not ported yet](#not-ported-yet) |
|
||||||
| [FAQ](#faq) - [Credits](#credits) - [License](#license) |
|
| [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).
|
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
|
### 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.
|
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)
|
### 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`.
|
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)):
|
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)
|
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
|
### Backends
|
||||||
|
|
||||||
| Backend | File | Build | Renderer | Example |
|
| 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.
|
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
|
### Testing
|
||||||
|
|
||||||
`tools/test/run.py` runs any LocalScript headless against a mocked Roblox (`tools/test/roblox_mock.luau`):
|
`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 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
|
### Credits
|
||||||
|
|
||||||
|
|||||||
@@ -36,18 +36,39 @@ jobs:
|
|||||||
python3 tools/bundle.py --no-demo dist/roblox-nodemo # ImGui.luau (library only)
|
python3 tools/bundle.py --no-demo dist/roblox-nodemo # ImGui.luau (library only)
|
||||||
python3 tools/bundle.py --target love dist/love # imgui_love.lua (library + demo)
|
python3 tools/bundle.py --target love dist/love # imgui_love.lua (library + demo)
|
||||||
python3 tools/bundle.py --target love --no-demo dist/love-nodemo
|
python3 tools/bundle.py --target love --no-demo dist/love-nodemo
|
||||||
|
# minified variants (comments/whitespace stripped, same code): tools/minify.py
|
||||||
|
python3 tools/minify.py dist/roblox/ImGui.luau dist/ImGui.min.luau
|
||||||
|
python3 tools/minify.py dist/roblox-nodemo/ImGui.luau dist/ImGui-nodemo.min.luau
|
||||||
|
python3 tools/minify.py dist/love/imgui_love.lua dist/imgui_love.min.lua
|
||||||
|
python3 tools/minify.py dist/love-nodemo/imgui_love.lua dist/imgui_love-nodemo.min.lua
|
||||||
|
|
||||||
- name: Check
|
- name: Check
|
||||||
run: |
|
run: |
|
||||||
python3 tools/sync_github_readme.py --check
|
python3 tools/sync_github_readme.py --check
|
||||||
luau-compile --null dist/roblox/ImGui.luau
|
luau-compile --null dist/roblox/ImGui.luau
|
||||||
luau-compile --null dist/roblox-nodemo/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/imgui_love.lua > /dev/null
|
||||||
luajit -bl dist/love-nodemo/imgui_love.lua > /dev/null
|
luajit -bl dist/love-nodemo/imgui_love.lua > /dev/null
|
||||||
|
python3 tools/minify.py --test
|
||||||
|
luau-compile --null dist/ImGui.min.luau
|
||||||
|
luau-compile --null dist/ImGui-nodemo.min.luau
|
||||||
|
luajit -bl dist/imgui_love.min.lua > /dev/null
|
||||||
|
luajit -bl dist/imgui_love-nodemo.min.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
|
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
|
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
|
||||||
|
MINIFY=1 python3 tools/test/run.py --main tools/test/scripts/t_readme.luau --frames 30 --quiet --roblox --timeout 300 | tee /tmp/rm.txt
|
||||||
|
grep -q "README OK" /tmp/rm.txt
|
||||||
|
python3 tools/test/run.py --main tools/test/scripts/t_fixes.luau --frames 25 --quiet --roblox --timeout 300 | tee /tmp/f.txt
|
||||||
|
grep -q "FIXES OK" /tmp/f.txt
|
||||||
python3 tools/bundle.py --target love >/dev/null
|
python3 tools/bundle.py --target love >/dev/null
|
||||||
luajit tools/test/love/t_drawhash.lua 60 Widgets,Basic
|
luajit tools/test/love/t_drawhash.lua 60 Widgets,Basic | tee /tmp/h.txt
|
||||||
|
cp build/imgui_love.lua /tmp/imgui_love.lua && cp dist/imgui_love.min.lua build/imgui_love.lua
|
||||||
|
luajit tools/test/love/t_drawhash.lua 60 Widgets,Basic | tee /tmp/hm.txt
|
||||||
|
cp /tmp/imgui_love.lua build/imgui_love.lua
|
||||||
|
diff /tmp/h.txt /tmp/hm.txt # minified LÖVE bundle draws exactly the same
|
||||||
luajit tools/test/love/t_examples.lua
|
luajit tools/test/love/t_examples.lua
|
||||||
|
|
||||||
- name: Package
|
- name: Package
|
||||||
@@ -59,7 +80,7 @@ jobs:
|
|||||||
cp love/imgui_love.lua imgui_love.lua
|
cp love/imgui_love.lua imgui_love.lua
|
||||||
cp love-nodemo/imgui_love.lua imgui_love-nodemo.lua
|
cp love-nodemo/imgui_love.lua imgui_love-nodemo.lua
|
||||||
# Roblox: module + example LocalScript + Rojo project
|
# 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/
|
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 .)
|
(cd pkg-roblox && zip -qr ../imgui-luau-roblox-$V.zip .)
|
||||||
# LÖVE: bundle + fonts + examples (run: love examples/example_love)
|
# LÖVE: bundle + fonts + examples (run: love examples/example_love)
|
||||||
@@ -77,9 +98,25 @@ jobs:
|
|||||||
dist/ImGui-nodemo.luau
|
dist/ImGui-nodemo.luau
|
||||||
dist/imgui_love.lua
|
dist/imgui_love.lua
|
||||||
dist/imgui_love-nodemo.lua
|
dist/imgui_love-nodemo.lua
|
||||||
|
dist/*.min.luau
|
||||||
|
dist/*.min.lua
|
||||||
dist/*.zip
|
dist/*.zip
|
||||||
generate_release_notes: true
|
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)
|
- name: Release (latest)
|
||||||
if: "!startsWith(github.ref, 'refs/tags/v')"
|
if: "!startsWith(github.ref, 'refs/tags/v')"
|
||||||
env:
|
env:
|
||||||
@@ -88,4 +125,4 @@ jobs:
|
|||||||
gh release delete latest --yes --cleanup-tag || true
|
gh release delete latest --yes --cleanup-tag || true
|
||||||
gh release create latest --latest --title "Latest build (${GITHUB_SHA::7})" \
|
gh release create latest --latest --title "Latest build (${GITHUB_SHA::7})" \
|
||||||
--notes "Automatic build of ${GITHUB_SHA} on ${GITHUB_REF_NAME}. Tag a commit v* for a versioned release." \
|
--notes "Automatic build of ${GITHUB_SHA} on ${GITHUB_REF_NAME}. Tag a commit v* for a versioned release." \
|
||||||
dist/ImGui.luau dist/ImGui-nodemo.luau dist/imgui_love.lua dist/imgui_love-nodemo.lua dist/*.zip
|
dist/ImGui.luau dist/ImGui-nodemo.luau dist/imgui_love.lua dist/imgui_love-nodemo.lua dist/*.min.luau dist/*.min.lua dist/*.zip
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
.opencode/
|
||||||
|
Packages/
|
||||||
|
wally.lock
|
||||||
|
__pycache__/
|
||||||
@@ -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.
|
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) |
|
| [Differences from C++](#differences-from-c-dear-imgui) - [Performance](#performance) - [Testing](#testing) - [Not ported yet](#not-ported-yet) |
|
||||||
| [FAQ](#faq) - [Credits](#credits) - [License](#license) |
|
| [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).
|
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
|
### 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.
|
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)
|
### 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`.
|
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)):
|
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)
|
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
|
### Backends
|
||||||
|
|
||||||
| Backend | File | Build | Renderer | Example |
|
| 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.
|
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
|
### Testing
|
||||||
|
|
||||||
`tools/test/run.py` runs any LocalScript headless against a mocked Roblox (`tools/test/roblox_mock.luau`):
|
`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 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
|
### Credits
|
||||||
|
|
||||||
|
|||||||
@@ -862,7 +862,8 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
|||||||
end
|
end
|
||||||
if rate > 0 then
|
if rate > 0 then
|
||||||
local now = os.clock()
|
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)
|
bd.NextRender = math.max(bd.NextRender + 1 / rate, now)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1127,8 +1128,33 @@ function ImGui_ImplRoblox_Init(parent, render_scale)
|
|||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- Text proxy: a hidden TextBox holds focus while an imgui text field is active, so the OS gives us typing (any
|
bd.PendingChars = {}
|
||||||
-- layout/unicode), paste (Ctrl+V) and copy/cut (Ctrl+C/X of the proxy's text = imgui's current selection).
|
|
||||||
|
-- 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")
|
local proxy = Instance.new("TextBox")
|
||||||
proxy.Name = "TextProxy"
|
proxy.Name = "TextProxy"
|
||||||
proxy.BackgroundTransparency = 1
|
proxy.BackgroundTransparency = 1
|
||||||
@@ -1140,12 +1166,11 @@ function ImGui_ImplRoblox_Init(parent, render_scale)
|
|||||||
proxy.ClearTextOnFocus = false
|
proxy.ClearTextOnFocus = false
|
||||||
proxy.TextEditable = true
|
proxy.TextEditable = true
|
||||||
proxy.Text = ""
|
proxy.Text = ""
|
||||||
proxy.Parent = gui
|
proxy.Parent = bd.Gui
|
||||||
bd.Proxy, bd.ProxyText, bd.PendingChars = proxy, "", {}
|
bd.Proxy, bd.ProxyText = proxy, ""
|
||||||
-- Clicking elsewhere (or Roblox taking focus) ends the edit, like clicking outside the field in imgui.
|
-- 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.)
|
-- (Text is polled in NewFrame instead of using Text changed signals: those are deferred in Roblox.)
|
||||||
connect(proxy.FocusLost, function()
|
proxy.FocusLost:Connect(function()
|
||||||
if bd.ProxyDebug then print("[ImGui text proxy] FocusLost, mouse over imgui:", ImGui.GetIO().WantCaptureMouse) end
|
|
||||||
if bd.ProxyReleasing then return end
|
if bd.ProxyReleasing then return end
|
||||||
-- Roblox drops TextBox focus on any click, including clicks inside imgui (e.g. moving the cursor within the
|
-- 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.
|
-- 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()
|
local g = ImGui.GetCurrentContext()
|
||||||
if g and g.ActiveId ~= 0 and g.ActiveId == bd.ProxyActiveId then ImGui.ClearActiveID() end
|
if g and g.ActiveId ~= 0 and g.ActiveId == bd.ProxyActiveId then ImGui.ClearActiveID() end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
connect(UserInputService.WindowFocused, function() ImGui.GetIO():AddFocusEvent(true) end)
|
|
||||||
connect(UserInputService.WindowFocusReleased, function() ImGui.GetIO():AddFocusEvent(false) end)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function ImGui_ImplRoblox_Shutdown()
|
function ImGui_ImplRoblox_Shutdown()
|
||||||
@@ -1177,6 +1199,7 @@ function ImGui_ImplRoblox_NewFrame()
|
|||||||
PB("ImGui NewFrame (backend)")
|
PB("ImGui NewFrame (backend)")
|
||||||
local io = ImGui.GetIO()
|
local io = ImGui.GetIO()
|
||||||
local bd = ImGui_ImplRoblox_GetBackendData()
|
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 screen = bd.Gui.AbsoluteSize
|
||||||
local w, h = floor(screen.X * bd.Scale), floor(screen.Y * bd.Scale)
|
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.
|
-- otherwise the KeyCode fallback keeps typing working.
|
||||||
local proxy = bd.Proxy
|
local proxy = bd.Proxy
|
||||||
local g = ImGui.GetCurrentContext()
|
local g = ImGui.GetCurrentContext()
|
||||||
local focused_box = UserInputService:GetFocusedTextBox()
|
local focused_box = proxy and UserInputService:GetFocusedTextBox()
|
||||||
if io.WantTextInput and g.ActiveId ~= 0 then
|
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
|
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])
|
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
|
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)
|
bd.ProxyText = proxy.Text -- what it already holds is not new input (else re-typing it would collapse imgui's selection)
|
||||||
proxy:CaptureFocus()
|
proxy:CaptureFocus()
|
||||||
focused_box = UserInputService:GetFocusedTextBox()
|
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
|
end
|
||||||
elseif focused_box == proxy then
|
elseif focused_box == proxy then
|
||||||
bd.ProxyReleasing = true
|
bd.ProxyReleasing = true
|
||||||
@@ -1217,13 +1241,11 @@ function ImGui_ImplRoblox_NewFrame()
|
|||||||
bd.ProxyReleasing = false
|
bd.ProxyReleasing = false
|
||||||
focused_box = nil
|
focused_box = nil
|
||||||
end
|
end
|
||||||
local was_focused = proxy_focused
|
proxy_focused = proxy ~= nil and focused_box == proxy
|
||||||
proxy_focused = focused_box == proxy
|
|
||||||
local used_proxy_text = false
|
local used_proxy_text = false
|
||||||
if proxy_focused then
|
if proxy_focused then
|
||||||
-- 1) whatever the OS put in the proxy since last frame (typing, paste, cut) replaces imgui's selection
|
-- 1) whatever the OS put in the proxy since last frame (typing, paste, cut) replaces imgui's selection
|
||||||
local text = proxy.Text
|
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)
|
if text ~= bd.ProxyText and text ~= bd.ProxyLastRaw then -- (LastRaw: don't re-send if our reset hasn't applied yet)
|
||||||
bd.ProxyLastRaw = text
|
bd.ProxyLastRaw = text
|
||||||
-- InputText ignores characters while Ctrl is held (upstream, so shortcuts don't type); a paste arrives with
|
-- 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
|
proxy.Text = sel
|
||||||
bd.ProxyLastRaw = nil
|
bd.ProxyLastRaw = nil
|
||||||
bd.ProxyText = sel
|
bd.ProxyText = sel
|
||||||
if bd.ProxyDebug then print("[ImGui text proxy] mirror selection", string.format("%q", sel)) end
|
|
||||||
end
|
end
|
||||||
-- keep imgui's selection fully selected in the proxy, so Ctrl+C copies it and typing/Ctrl+V replaces it
|
-- 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
|
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
|
if not used_proxy_text then for _, c in ipairs(pending) do io:AddInputCharacter(c) end end
|
||||||
table.clear(pending)
|
table.clear(pending)
|
||||||
end
|
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.)
|
-- 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
|
local want = io.WantCaptureMouse
|
||||||
@@ -1317,7 +1337,7 @@ ImGui_ImplRoblox = {
|
|||||||
SetRenderScale = ImGui_ImplRoblox_SetRenderScale,
|
SetRenderScale = ImGui_ImplRoblox_SetRenderScale,
|
||||||
SetRenderRate = ImGui_ImplRoblox_SetRenderRate,
|
SetRenderRate = ImGui_ImplRoblox_SetRenderRate,
|
||||||
SetOverlay = ImGui_ImplRoblox_SetOverlay,
|
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,
|
CreateTexture = ImGui_ImplRoblox_CreateTexture,
|
||||||
DestroyTexture = ImGui_ImplRoblox_DestroyTexture,
|
DestroyTexture = ImGui_ImplRoblox_DestroyTexture,
|
||||||
GetRenderScale = function() return ImGui_ImplRoblox_GetBackendData().Scale end,
|
GetRenderScale = function() return ImGui_ImplRoblox_GetBackendData().Scale end,
|
||||||
|
|||||||
@@ -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"], [21288, "imgui_demo.lua"], [29706, "imgui_draw.lua"], [35476, "imgui_h.lua"], [38142, "imgui_impl_roblox.lua"], [39494, "imgui_impl_roblox_fonts.lua"], [45897, "imgui_internal.lua"], [49343, "imgui_tables.lua"], [54338, "imgui_widgets.lua"], [64948, "imstb_rectpack.lua"], [65612, "imstb_textedit.lua"], [67154, "imstb_truetype.lua"], [69942, "imstd_minstdio.lua"]]
|
||||||
+10226
-2640
File diff suppressed because it is too large
Load Diff
@@ -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 @@
|
|||||||
[[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"], [21425, "imgui_demo.lua"], [29843, "imgui_draw.lua"], [35613, "imgui_h.lua"], [38279, "imgui_impl_love.lua"], [38819, "imgui_internal.lua"], [42265, "imgui_tables.lua"], [47260, "imgui_widgets.lua"], [57870, "imstb_rectpack.lua"], [58534, "imstb_textedit.lua"], [60076, "imstb_truetype.lua"], [62864, "imstd_minstdio.lua"]]
|
||||||
+10186
-2621
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
|||||||
"tree": {
|
"tree": {
|
||||||
"$className": "DataModel",
|
"$className": "DataModel",
|
||||||
"ReplicatedStorage": {
|
"ReplicatedStorage": {
|
||||||
"ImGui": { "$path": "build/ImGui.luau" }
|
"ImGui": { "$path": "build/ImGuiTyped.luau", "Impl": { "$path": "build/ImGui.luau" } }
|
||||||
},
|
},
|
||||||
"StarterPlayer": {
|
"StarterPlayer": {
|
||||||
"StarterPlayerScripts": {
|
"StarterPlayerScripts": {
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ KNOWN ISSUES
|
|||||||
|
|
||||||
PERFORMANCE IDEAS (not done)
|
PERFORMANCE IDEAS (not done)
|
||||||
----------------------------
|
----------------------------
|
||||||
- Opt-in cached windows (skip user code for idle windows).
|
|
||||||
- Cheaper visible ColorEdit4 rows; cheaper Begin() for child windows.
|
- Cheaper visible ColorEdit4 rows; cheaper Begin() for child windows.
|
||||||
|
|
||||||
PLANNED EXTENSIONS
|
PLANNED EXTENSIONS
|
||||||
|
|||||||
@@ -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 ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
local RunService = game:GetService("RunService")
|
local RunService = game:GetService("RunService")
|
||||||
|
local AssetService = game:GetService("AssetService")
|
||||||
|
|
||||||
local G = require(ReplicatedStorage:WaitForChild("ImGui"))
|
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
|
local MainScale = 1.0
|
||||||
-- Roblox UI points are multiplied by the OS display scale (e.g. 150%), which makes imgui look large.
|
local RenderScale = 1.0
|
||||||
-- Set this to your display scale to render imgui at physical-pixel resolution (same size as a desktop imgui app).
|
local RenderRate = 60
|
||||||
local render_scale = 1.0
|
local ThumbnailSize = 420
|
||||||
local clear_color = ImVec4(0.45, 0.55, 0.60, 1.00)
|
local FontSize = 13.0
|
||||||
local show_demo_window = true
|
local FontNames = { "Cousine-Regular", "DroidSans", "Karla-Regular", "ProggyTiny", "Roboto-Medium" }
|
||||||
local show_another_window = false
|
|
||||||
local f = 0.0
|
local State = {
|
||||||
local counter = 0
|
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()
|
ImGui.CreateContext()
|
||||||
|
|
||||||
local io = ImGui.GetIO()
|
local IO = ImGui.GetIO()
|
||||||
io.ConfigFlags = bit32.bor(io.ConfigFlags, ImGuiConfigFlags.NavEnableKeyboard, ImGuiConfigFlags.DockingEnable)
|
IO.ConfigFlags = bit32.bor(IO.ConfigFlags, ImGuiConfigFlags.NavEnableKeyboard, ImGuiConfigFlags.DockingEnable)
|
||||||
|
|
||||||
-- Catppuccin Mocha, Mauve accent (https://catppuccin.com/palette)
|
-- catppuccin mocha mauve https://catppuccin.com/palette
|
||||||
local function StyleColorsCatppuccinMocha(style)
|
local function StyleColorsCatppuccinMocha(Style)
|
||||||
local function hex(h, a)
|
local function Hex(Value, Alpha)
|
||||||
return ImVec4(bit32.extract(h, 16, 8) / 255, bit32.extract(h, 8, 8) / 255, bit32.extract(h, 0, 8) / 255, a or 1.0)
|
return ImVec4(
|
||||||
end
|
bit32.extract(Value, 16, 8) / 255,
|
||||||
local crust, mantle, base = 0x11111b, 0x181825, 0x1e1e2e
|
bit32.extract(Value, 8, 8) / 255,
|
||||||
local surface0, surface1, surface2 = 0x313244, 0x45475a, 0x585b70
|
bit32.extract(Value, 0, 8) / 255,
|
||||||
local overlay0, overlay2, text = 0x6c7086, 0x9399b2, 0xcdd6f4
|
Alpha or 1.0
|
||||||
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)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local style = ImGui.GetStyle()
|
local Crust, Mantle, Base = 0x11111b, 0x181825, 0x1e1e2e
|
||||||
StyleColorsCatppuccinMocha(style)
|
local Surface0, Surface1, Surface2 = 0x313244, 0x45475a, 0x585b70
|
||||||
style:ScaleAllSizes(main_scale)
|
local Overlay0, Overlay2, Text = 0x6c7086, 0x9399b2, 0xcdd6f4
|
||||||
style.FontScaleDpi = main_scale
|
local Mauve, Lavender = 0xcba6f7, 0xb4befe
|
||||||
|
|
||||||
-- Fonts: ProggyClean stays the default (added first); the rest show up in Style Editor > Fonts
|
local Entries = {
|
||||||
io.Fonts:AddFontDefault()
|
{ "Text", Text },
|
||||||
for _, name in ipairs({ "Cousine-Regular", "DroidSans", "Karla-Regular", "ProggyTiny", "Roboto-Medium" }) do
|
{ "TextDisabled", Overlay0 },
|
||||||
io.Fonts:AddFontFromFileTTF("fonts/" .. name .. ".ttf", 13.0)
|
|
||||||
|
{ "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
|
end
|
||||||
|
|
||||||
ImGui_ImplRoblox.Init(nil, render_scale)
|
local Style = ImGui.GetStyle()
|
||||||
ImGui_ImplRoblox.SetRenderRate(60) -- tile redraws per second; imgui logic + your code still run every frame
|
StyleColorsCatppuccinMocha(Style)
|
||||||
-- ImGui_ImplRoblox.SetBusyRenderRate(30) -- optional: lower redraw rate while scrolling/resizing/moving windows
|
Style:ScaleAllSizes(MainScale)
|
||||||
ImGui_ImplRoblox.SetProfiling(true) -- MicroProfiler (Ctrl+F6) labels; turn off when not profiling
|
Style.FontScaleDpi = MainScale
|
||||||
|
|
||||||
|
IO.Fonts:AddFontDefault()
|
||||||
|
for _, Name in ipairs(FontNames) do
|
||||||
|
IO.Fonts:AddFontFromFileTTF("fonts/" .. Name .. ".ttf", FontSize)
|
||||||
|
end
|
||||||
|
|
||||||
|
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 = {
|
local EmbedFlags = {
|
||||||
None = 0,
|
None = 0,
|
||||||
NoClip = 1, -- don't clip to the window's content area (still moves/hides/stacks with the window)
|
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
|
NoBackground = 2, -- No FrameBg behind the Frame
|
||||||
NoBorder = 4, -- no style border
|
NoBorder = 4, -- No style border
|
||||||
NoPadding = 8, -- no FramePadding inside the Frame (UIPadding)
|
NoPadding = 8, -- No FramePadding inside the Frame (UIPadding)
|
||||||
NoRounding = 16, -- no FrameRounding corners (UICorner)
|
NoRounding = 16, -- No FrameRounding corners (UICorner)
|
||||||
}
|
}
|
||||||
|
|
||||||
G.ImGuiEmbedFlags = EmbedFlags
|
G.ImGuiEmbedFlags = EmbedFlags
|
||||||
|
|
||||||
local embeds = {} -- [ImGuiID] = { Frame, Padding, Corner }
|
local Embeds = {}
|
||||||
|
|
||||||
function ImGui.Embed(id, size, flags, init)
|
local function HasFlag(Flags, Flag)
|
||||||
flags = flags or EmbedFlags.None
|
return bit32.band(Flags, Flag) ~= 0
|
||||||
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
|
end
|
||||||
|
|
||||||
-- style -> Instances (only written when changed)
|
local function CreateEmbed(Id)
|
||||||
local scale = ImGui_ImplRoblox.GetRenderScale()
|
local Frame = Instance.new("Frame")
|
||||||
local px, py = 0, 0
|
Frame.Name = tostring(Id)
|
||||||
if bit32.band(flags, EmbedFlags.NoPadding) == 0 then px, py = style.FramePadding.x / scale, style.FramePadding.y / scale end
|
Frame.BackgroundTransparency = 1
|
||||||
if e.PX ~= px or e.PY ~= py then
|
Frame.BorderSizePixel = 0
|
||||||
e.PX, e.PY = px, py
|
Frame.Visible = false
|
||||||
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)
|
local Padding = Instance.new("UIPadding")
|
||||||
return e.Frame
|
Padding.Parent = Frame
|
||||||
|
|
||||||
|
local Corner = Instance.new("UICorner")
|
||||||
|
Corner.Parent = Frame
|
||||||
|
|
||||||
|
return { Frame = Frame, Padding = Padding, Corner = Corner }
|
||||||
end
|
end
|
||||||
|
|
||||||
function ImGui.DestroyEmbed(id)
|
function ImGui.Embed(Id, Size, Flags, Init)
|
||||||
local gid = ImGui.GetCurrentWindow():GetID(id)
|
Flags = Flags or EmbedFlags.None
|
||||||
local e = embeds[gid]
|
|
||||||
if e then e.Frame:Destroy(); embeds[gid] = nil end
|
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
|
end
|
||||||
|
|
||||||
-- Example: Decal Viewer. Loads a decal/image asset's pixels into an imgui texture and shows it in the demo's
|
if Window.SkipItems then
|
||||||
-- image viewer (zoom with the wheel, drag to pan, toggleable pixel grid).
|
return Embed.Frame
|
||||||
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 }
|
|
||||||
|
|
||||||
-- 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)"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function LoadDecal(id)
|
local EmbedStyle = ImGui.GetStyle()
|
||||||
decal.loading = true
|
local ItemSize = ImGui.CalcItemSize(
|
||||||
decal.status = "Loading " .. id .. "..."
|
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
|
||||||
|
|
||||||
|
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()
|
task.spawn(function()
|
||||||
local image_id, err = ResolveImageId(id)
|
local Uri = string.format("rbxthumb://type=Asset&id=%d&w=%d&h=%d", Id, ThumbnailSize, ThumbnailSize)
|
||||||
decal.thumb = nil
|
local Success, Result = pcall(AssetService.CreateEditableImageAsync, AssetService, Content.fromUri(Uri))
|
||||||
if image_id then
|
|
||||||
-- 1) the image itself (needs permission), 2) its 420x420 thumbnail (may be allowed when 1 isn't)
|
if Success then
|
||||||
local ok, img = pcall(AssetService.CreateEditableImageAsync, AssetService, Content.fromUri("rbxassetid://" .. image_id))
|
local Size = Result.Size
|
||||||
if not ok then
|
local Pixels = Result:ReadPixelsBuffer(Vector2.zero, Size)
|
||||||
local ok2, img2 = pcall(AssetService.CreateEditableImageAsync, AssetService, Content.fromUri("rbxthumb://type=Asset&id=" .. id .. "&w=420&h=420"))
|
Result:Destroy()
|
||||||
if ok2 then ok, img = true, img2 end
|
|
||||||
|
if Decal.Texture then
|
||||||
|
ImGui_ImplRoblox.DestroyTexture(Decal.Texture)
|
||||||
end
|
end
|
||||||
if ok then
|
|
||||||
local size = img.Size
|
Decal.Texture = ImGui_ImplRoblox.CreateTexture(Pixels, Size.X, Size.Y)
|
||||||
local pixels = img:ReadPixelsBuffer(Vector2.zero, size)
|
Decal.Width, Decal.Height = Size.X, Size.Y
|
||||||
img:Destroy()
|
Decal.Viewer.ViewReset = true
|
||||||
if decal.tex then ImGui_ImplRoblox.DestroyTexture(decal.tex) end
|
Decal.Status = string.format("Thumbnail %d: %dx%d", Id, Size.X, Size.Y)
|
||||||
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
|
else
|
||||||
-- 3) no pixel access: Roblox still displays any asset, so show its thumbnail in an ImageLabel (no grid)
|
Decal.Status = "Failed to load thumbnail: " .. tostring(Result)
|
||||||
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
|
end
|
||||||
else
|
|
||||||
decal.thumb = "rbxthumb://type=Asset&id=" .. id .. "&w=420&h=420"
|
Decal.Loading = false
|
||||||
decal.status = (err or "") .. " - showing thumbnail"
|
|
||||||
end
|
|
||||||
decal.loading = false
|
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function ShowDecalViewer()
|
local function ShowDecalViewer()
|
||||||
if not decal.show then return end
|
if not Decal.Show then
|
||||||
decal.show = ImGui.Begin("Decal Viewer", decal.show)
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
Decal.Show = ImGui.Begin("Decal Viewer", Decal.Show)
|
||||||
|
|
||||||
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 12)
|
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 12)
|
||||||
local enter = ImGui.InputTextWithHint("##id", "decal id", decal.input, 32, G.ImGuiInputTextFlags.CharsDecimal + G.ImGuiInputTextFlags.EnterReturnsTrue)
|
local EnterPressed = ImGui.InputTextWithHint(
|
||||||
|
"##id",
|
||||||
|
"decal id",
|
||||||
|
Decal.Input,
|
||||||
|
32,
|
||||||
|
ImGuiInputTextFlags.CharsDecimal + ImGuiInputTextFlags.EnterReturnsTrue
|
||||||
|
)
|
||||||
|
|
||||||
ImGui.SameLine()
|
ImGui.SameLine()
|
||||||
ImGui.BeginDisabled(decal.loading)
|
ImGui.BeginDisabled(Decal.Loading)
|
||||||
if (ImGui.Button("Load") or enter) and not decal.loading then
|
if (ImGui.Button("Load") or EnterPressed) and not Decal.Loading then
|
||||||
local id = tonumber(ImGui.TextBufferToString(decal.input))
|
local Id = tonumber(ImGui.TextBufferToString(Decal.Input))
|
||||||
if id then LoadDecal(id) else decal.status = "Not a number" end
|
if Id and Id == math.floor(Id) then
|
||||||
|
LoadDecal(Id)
|
||||||
|
else
|
||||||
|
Decal.Status = "Not a valid id"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
ImGui.EndDisabled()
|
ImGui.EndDisabled()
|
||||||
ImGui.SameLine(); ImGui.TextDisabled(decal.status)
|
|
||||||
if decal.thumb then
|
ImGui.SameLine()
|
||||||
local avail = ImGui.GetContentRegionAvail()
|
ImGui.TextDisabled(Decal.Status)
|
||||||
local frame = ImGui.Embed("decal_thumb", G.ImVec2(-1, math.max(avail.y, 50)), G.ImGuiEmbedFlags.None, function(f)
|
|
||||||
local img = Instance.new("ImageLabel")
|
if Decal.Texture then
|
||||||
img.Name = "Image"
|
ExampleImageViewer_DrawOptions(Decal.Viewer)
|
||||||
img.Size = UDim2.fromScale(1, 1)
|
|
||||||
img.BackgroundTransparency = 1
|
local Available = ImGui.GetContentRegionAvail()
|
||||||
img.ScaleType = Enum.ScaleType.Fit
|
ExampleImageViewer_DrawCanvas(
|
||||||
img.Parent = f
|
Decal.Viewer,
|
||||||
end)
|
ImVec2(math.max(Available.x, 50), math.max(Available.y, 50)),
|
||||||
if frame.Image.Image ~= decal.thumb then frame.Image.Image = decal.thumb end
|
Decal.Texture,
|
||||||
elseif decal.tex then
|
Decal.Width,
|
||||||
ExampleImageViewer_DrawOptions(decal.viewer)
|
Decal.Height
|
||||||
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
|
end
|
||||||
|
|
||||||
ImGui.End()
|
ImGui.End()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Example: a ViewportFrame with a spinning part, edited live from imgui widgets
|
local function ShowHelloWindow()
|
||||||
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
|
|
||||||
|
|
||||||
RunService.Stepped:Connect(function()
|
|
||||||
P("ImGui frame")
|
|
||||||
ImGui_ImplRoblox.NewFrame()
|
|
||||||
P("ImGui.NewFrame"); ImGui.NewFrame(); PE()
|
|
||||||
P("ImGui UI")
|
|
||||||
|
|
||||||
-- Dockspace covering the whole screen; the centre stays transparent so the game shows through
|
|
||||||
ImGui.DockSpaceOverViewport(0, nil, G.ImGuiDockNodeFlags.PassthruCentralNode)
|
|
||||||
|
|
||||||
if show_demo_window then
|
|
||||||
ImGui.ShowDemoWindow()
|
|
||||||
end
|
|
||||||
|
|
||||||
do
|
|
||||||
ImGui.Begin("Hello, world!")
|
ImGui.Begin("Hello, world!")
|
||||||
|
|
||||||
ImGui.Text("This is some useful text.")
|
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)
|
|
||||||
|
|
||||||
f = ImGui.SliderFloat("float", f, 0.0, 1.0)
|
local _
|
||||||
ImGui.ColorEdit3("clear color", clear_color)
|
_, 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
|
if ImGui.Button("Button") then
|
||||||
counter = counter + 1
|
State.Counter += 1
|
||||||
end
|
end
|
||||||
ImGui.SameLine()
|
ImGui.SameLine()
|
||||||
ImGui.Text("counter = %d", counter)
|
ImGui.Text("counter = %d", State.Counter)
|
||||||
|
|
||||||
ImGui.Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0 / io.Framerate, io.Framerate)
|
ImGui.Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0 / IO.Framerate, IO.Framerate)
|
||||||
|
|
||||||
ImGui.End()
|
ImGui.End()
|
||||||
end
|
end
|
||||||
|
|
||||||
if show_another_window then
|
local function ShowAnotherWindow()
|
||||||
show_another_window = ImGui.Begin("Another Window", show_another_window)
|
if not State.ShowAnotherWindow then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
State.ShowAnotherWindow = ImGui.Begin("Another Window", State.ShowAnotherWindow)
|
||||||
|
|
||||||
ImGui.Text("Hello from another window!")
|
ImGui.Text("Hello from another window!")
|
||||||
if ImGui.Button("Close Me") then
|
if ImGui.Button("Close Me") then
|
||||||
show_another_window = false
|
State.ShowAnotherWindow = false
|
||||||
end
|
end
|
||||||
|
|
||||||
ImGui.End()
|
ImGui.End()
|
||||||
end
|
end
|
||||||
|
|
||||||
PE()
|
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()
|
||||||
|
ProfileBegin("ImGui frame")
|
||||||
|
ImGui_ImplRoblox.NewFrame()
|
||||||
|
|
||||||
|
ProfileBegin("ImGui.NewFrame")
|
||||||
|
ImGui.NewFrame()
|
||||||
|
ProfileEnd()
|
||||||
|
|
||||||
|
ProfileBegin("ImGui UI")
|
||||||
|
ImGui.DockSpaceOverViewport(0, nil, ImGuiDockNodeFlags.PassthruCentralNode)
|
||||||
|
|
||||||
|
if State.ShowDemoWindow then
|
||||||
|
-- For reference: https://github.com/4DBug/imgui-luau/blob/master/imgui_demo.lua
|
||||||
|
ImGui.ShowDemoWindow()
|
||||||
|
end
|
||||||
|
|
||||||
|
ShowHelloWindow()
|
||||||
|
ShowAnotherWindow()
|
||||||
ShowDecalViewer()
|
ShowDecalViewer()
|
||||||
|
ShowViewportWindow()
|
||||||
|
ProfileEnd()
|
||||||
|
|
||||||
if show_viewport_window then
|
ProfileBegin("ImGui.Render")
|
||||||
show_viewport_window = ImGui.Begin("Viewport", show_viewport_window)
|
ImGui.EndFrame()
|
||||||
spin_speed = ImGui.SliderFloat("speed", spin_speed, 0.0, 5.0)
|
ImGui.Render()
|
||||||
ImGui.ColorEdit3("color", part_color)
|
ProfileEnd()
|
||||||
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())
|
ImGui_ImplRoblox.RenderDrawData(ImGui.GetDrawData())
|
||||||
PE()
|
ProfileEnd()
|
||||||
end)
|
end)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"tree": {
|
"tree": {
|
||||||
"$className": "DataModel",
|
"$className": "DataModel",
|
||||||
"ReplicatedStorage": {
|
"ReplicatedStorage": {
|
||||||
"ImGui": { "$path": "../../build/ImGui.luau" },
|
"ImGui": { "$path": "../../build/ImGuiTyped.luau", "Impl": { "$path": "../../build/ImGui.luau" } },
|
||||||
"SharedApp": { "$path": "app.lua" }
|
"SharedApp": { "$path": "app.lua" }
|
||||||
},
|
},
|
||||||
"StarterPlayer": {
|
"StarterPlayer": {
|
||||||
|
|||||||
+1314
-181
File diff suppressed because it is too large
Load Diff
+632
-100
File diff suppressed because it is too large
Load Diff
+1169
-701
File diff suppressed because it is too large
Load Diff
+889
-615
File diff suppressed because it is too large
Load Diff
+732
-84
File diff suppressed because it is too large
Load Diff
+1153
-298
File diff suppressed because it is too large
Load Diff
+229
-11
@@ -4,6 +4,63 @@
|
|||||||
-- ALL TABLES IN THIS FILE ARE 1-BASED!
|
-- ALL TABLES IN THIS FILE ARE 1-BASED!
|
||||||
-- Avoids table creation when possible
|
-- Avoids table creation when possible
|
||||||
|
|
||||||
|
-- [DEAR IMGUI]
|
||||||
|
-- This is a slightly modified version of stb_rect_pack.h 1.01.
|
||||||
|
-- Grep for [DEAR IMGUI] to find the changes.
|
||||||
|
--
|
||||||
|
-- stb_rect_pack.h - v1.01 - public domain - rectangle packing
|
||||||
|
-- Sean Barrett 2014
|
||||||
|
--
|
||||||
|
-- Useful for e.g. packing rectangular textures into an atlas.
|
||||||
|
-- Does not do rotation.
|
||||||
|
--
|
||||||
|
-- Not necessarily the awesomest packing method, but better than
|
||||||
|
-- the totally naive one in stb_truetype (which is primarily what
|
||||||
|
-- this is meant to replace).
|
||||||
|
--
|
||||||
|
-- Has only had a few tests run, may have issues.
|
||||||
|
--
|
||||||
|
-- More docs to come.
|
||||||
|
--
|
||||||
|
-- Uses table.sort and assert (STBRP_SORT and STBRP_ASSERT below).
|
||||||
|
--
|
||||||
|
-- This library currently uses the Skyline Bottom-Left algorithm.
|
||||||
|
--
|
||||||
|
-- Please note: better rectangle packers are welcome! Please
|
||||||
|
-- implement them to the same API, but with a different init
|
||||||
|
-- function.
|
||||||
|
--
|
||||||
|
-- Credits
|
||||||
|
--
|
||||||
|
-- Library
|
||||||
|
-- Sean Barrett
|
||||||
|
-- Minor features
|
||||||
|
-- Martins Mozeiko
|
||||||
|
-- github:IntellectualKitty
|
||||||
|
--
|
||||||
|
-- Bugfixes / warning fixes
|
||||||
|
-- Jeremy Jaussaud
|
||||||
|
-- Fabian Giesen
|
||||||
|
--
|
||||||
|
-- Version history:
|
||||||
|
--
|
||||||
|
-- 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section
|
||||||
|
-- 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
|
||||||
|
-- 0.99 (2019-02-07) warning fixes
|
||||||
|
-- 0.11 (2017-03-03) return packing success/fail result
|
||||||
|
-- 0.10 (2016-10-25) remove cast-away-const to avoid warnings
|
||||||
|
-- 0.09 (2016-08-27) fix compiler warnings
|
||||||
|
-- 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
|
||||||
|
-- 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
|
||||||
|
-- 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
|
||||||
|
-- 0.05: added STBRP_ASSERT to allow replacing assert
|
||||||
|
-- 0.04: fixed minor bug in STBRP_LARGE_RECTS support
|
||||||
|
-- 0.01: initial release
|
||||||
|
--
|
||||||
|
-- LICENSE
|
||||||
|
--
|
||||||
|
-- See end of file for license information.
|
||||||
|
|
||||||
--- The `prev_link` can be stbrp_context or stbrp_node
|
--- The `prev_link` can be stbrp_context or stbrp_node
|
||||||
--- @diagnostic disable
|
--- @diagnostic disable
|
||||||
|
|
||||||
@@ -13,7 +70,13 @@ local STBRP_SORT = table.sort
|
|||||||
local STBRP_ASSERT = assert
|
local STBRP_ASSERT = assert
|
||||||
local STBRP__NOTUSED = function(_) end
|
local STBRP__NOTUSED = function(_) end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
--
|
||||||
|
-- INCLUDE SECTION
|
||||||
|
--
|
||||||
|
|
||||||
local STBRP__MAXVAL = 0x7fffffff
|
local STBRP__MAXVAL = 0x7fffffff
|
||||||
|
-- Mostly for internal use, but this is the maximum supported coordinate value.
|
||||||
|
|
||||||
--- @enum STBRP_HEURISTIC_Skyline
|
--- @enum STBRP_HEURISTIC_Skyline
|
||||||
local STBRP_HEURISTIC_Skyline = {
|
local STBRP_HEURISTIC_Skyline = {
|
||||||
@@ -44,12 +107,17 @@ local STBRP__PREV_LINK_TYPE = {
|
|||||||
--- @nodiscard
|
--- @nodiscard
|
||||||
local function stbrp_rect()
|
local function stbrp_rect()
|
||||||
return {
|
return {
|
||||||
|
-- reserved for your use:
|
||||||
id = 0,
|
id = 0,
|
||||||
|
|
||||||
|
-- input:
|
||||||
w = 0,
|
w = 0,
|
||||||
h = 0,
|
h = 0,
|
||||||
|
|
||||||
|
-- output:
|
||||||
x = 0,
|
x = 0,
|
||||||
y = 0,
|
y = 0,
|
||||||
was_packed = 0
|
was_packed = 0 -- non-zero if valid packing
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -92,7 +160,7 @@ local function stbrp_context()
|
|||||||
|
|
||||||
active_head = nil,
|
active_head = nil,
|
||||||
free_head = nil,
|
free_head = nil,
|
||||||
extra = {stbrp_node(), stbrp_node()}
|
extra = {stbrp_node(), stbrp_node()} -- we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -113,13 +181,14 @@ local function stbrp__findresult()
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
--------------------------
|
-------------------------------------------------------------------------------
|
||||||
--------------------------
|
--
|
||||||
---
|
-- IMPLEMENTATION SECTION
|
||||||
--- IMPLEMENTATION SECTION
|
--
|
||||||
---
|
|
||||||
---
|
|
||||||
|
|
||||||
|
-- Optionally select which packing heuristic the library should use. Different
|
||||||
|
-- heuristics will produce better/worse results for different data sets.
|
||||||
|
-- If you call init again, this will be reset to the default.
|
||||||
--- @param context stbrp_context
|
--- @param context stbrp_context
|
||||||
--- @param heuristic int
|
--- @param heuristic int
|
||||||
local function stbrp_setup_heuristic(context, heuristic)
|
local function stbrp_setup_heuristic(context, heuristic)
|
||||||
@@ -131,16 +200,49 @@ local function stbrp_setup_heuristic(context, heuristic)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Optionally call this function after init but before doing any packing to
|
||||||
|
-- change the handling of the out-of-temp-memory scenario, described above.
|
||||||
|
-- If you call init again, this will be reset to the default (false).
|
||||||
--- @param context stbrp_context
|
--- @param context stbrp_context
|
||||||
--- @param allow_out_of_mem boolean
|
--- @param allow_out_of_mem boolean
|
||||||
local function stbrp_setup_allow_out_of_mem(context, allow_out_of_mem)
|
local function stbrp_setup_allow_out_of_mem(context, allow_out_of_mem)
|
||||||
if allow_out_of_mem then
|
if allow_out_of_mem then
|
||||||
|
-- if it's ok to run out of memory, then don't bother aligning them;
|
||||||
|
-- this gives better packing, but may fail due to OOM (even though
|
||||||
|
-- the rectangles easily fit). @TODO a smarter approach would be to only
|
||||||
|
-- quantize once we've hit OOM, then we could get rid of this parameter.
|
||||||
context.align = 1
|
context.align = 1
|
||||||
else
|
else
|
||||||
|
-- if it's not ok to run out of memory, then quantize the widths
|
||||||
|
-- so that num_nodes is always enough nodes.
|
||||||
|
--
|
||||||
|
-- I.e. num_nodes * align >= width
|
||||||
|
-- align >= width / num_nodes
|
||||||
|
-- align = ceil(width/num_nodes)
|
||||||
|
|
||||||
context.align = (context.width + context.num_nodes - 1) / context.num_nodes
|
context.align = (context.width + context.num_nodes - 1) / context.num_nodes
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Initialize a rectangle packer to:
|
||||||
|
-- pack a rectangle that is 'width' by 'height' in dimensions
|
||||||
|
-- using temporary storage provided by the array 'nodes', which is 'num_nodes' long
|
||||||
|
--
|
||||||
|
-- You must call this function every time you start packing into a new target.
|
||||||
|
--
|
||||||
|
-- There is no "shutdown" function. The 'nodes' memory must stay valid for
|
||||||
|
-- the following stbrp_pack_rects() call (or calls), but can be freed after
|
||||||
|
-- the call (or calls) finish.
|
||||||
|
--
|
||||||
|
-- Note: to guarantee best results, either:
|
||||||
|
-- 1. make sure 'num_nodes' >= 'width'
|
||||||
|
-- or 2. call stbrp_setup_allow_out_of_mem() defined above with 'allow_out_of_mem = true'
|
||||||
|
--
|
||||||
|
-- If you don't do either of the above things, widths will be quantized to multiples
|
||||||
|
-- of small integers to guarantee the algorithm doesn't run out of temporary storage.
|
||||||
|
--
|
||||||
|
-- If you do #2, then the non-quantized algorithm will be used, but the algorithm
|
||||||
|
-- may run out of temporary storage and be unable to pack some rectangles.
|
||||||
--- @param context stbrp_context
|
--- @param context stbrp_context
|
||||||
--- @param width int
|
--- @param width int
|
||||||
--- @param height int
|
--- @param height int
|
||||||
@@ -160,6 +262,7 @@ function stbrp_init_target(context, width, height, nodes, num_nodes)
|
|||||||
context.num_nodes = num_nodes
|
context.num_nodes = num_nodes
|
||||||
stbrp_setup_allow_out_of_mem(context, false)
|
stbrp_setup_allow_out_of_mem(context, false)
|
||||||
|
|
||||||
|
-- node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
|
||||||
context.extra[1].x = 0
|
context.extra[1].x = 0
|
||||||
context.extra[1].y = 0
|
context.extra[1].y = 0
|
||||||
context.extra[1].next = context.extra[2]
|
context.extra[1].next = context.extra[2]
|
||||||
@@ -168,6 +271,8 @@ function stbrp_init_target(context, width, height, nodes, num_nodes)
|
|||||||
context.extra[2].next = nil
|
context.extra[2].next = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- find minimum y position if it starts at x1
|
||||||
|
-- Returns (min_y, waste_area)
|
||||||
--- @param c stbrp_context
|
--- @param c stbrp_context
|
||||||
--- @param first stbrp_node
|
--- @param first stbrp_node
|
||||||
--- @param x0 int
|
--- @param x0 int
|
||||||
@@ -181,7 +286,7 @@ local function stbrp__skyline_find_min_y(c, first, x0, width)
|
|||||||
|
|
||||||
STBRP_ASSERT(first.x <= x0)
|
STBRP_ASSERT(first.x <= x0)
|
||||||
|
|
||||||
STBRP_ASSERT(node.next.x > x0)
|
STBRP_ASSERT(node.next.x > x0) -- we ended up handling this in the caller for efficiency
|
||||||
|
|
||||||
STBRP_ASSERT(node.x <= x0)
|
STBRP_ASSERT(node.x <= x0)
|
||||||
|
|
||||||
@@ -190,14 +295,19 @@ local function stbrp__skyline_find_min_y(c, first, x0, width)
|
|||||||
local visited_width = 0
|
local visited_width = 0
|
||||||
while node.x < x1 do
|
while node.x < x1 do
|
||||||
if node.y > min_y then
|
if node.y > min_y then
|
||||||
|
-- raise min_y higher.
|
||||||
|
-- we've accounted for all waste up to min_y,
|
||||||
|
-- but we'll now add more waste for everything we've visited
|
||||||
waste_area = waste_area + visited_width * (node.y - min_y)
|
waste_area = waste_area + visited_width * (node.y - min_y)
|
||||||
min_y = node.y
|
min_y = node.y
|
||||||
|
-- the first time through, visited_width might be reduced
|
||||||
if node.x < x0 then
|
if node.x < x0 then
|
||||||
visited_width = visited_width + node.next.x - x0
|
visited_width = visited_width + node.next.x - x0
|
||||||
else
|
else
|
||||||
visited_width = visited_width + node.next.x - node.x
|
visited_width = visited_width + node.next.x - node.x
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
-- add waste area
|
||||||
local under_width = node.next.x - node.x
|
local under_width = node.next.x - node.x
|
||||||
if under_width + visited_width > width then
|
if under_width + visited_width > width then
|
||||||
under_width = width - visited_width
|
under_width = width - visited_width
|
||||||
@@ -223,10 +333,12 @@ local function stbrp__skyline_find_best_pos(c, width, height)
|
|||||||
local best_prev_link = nil
|
local best_prev_link = nil
|
||||||
local best_prev_link_type = nil
|
local best_prev_link_type = nil
|
||||||
|
|
||||||
|
-- align to multiple of c.align
|
||||||
width = width + c.align - 1
|
width = width + c.align - 1
|
||||||
width = width - width % c.align
|
width = width - width % c.align
|
||||||
STBRP_ASSERT(width % c.align == 0)
|
STBRP_ASSERT(width % c.align == 0)
|
||||||
|
|
||||||
|
-- if it can't possibly fit, bail immediately
|
||||||
if width > c.width or height > c.height then
|
if width > c.width or height > c.height then
|
||||||
fr.prev_link = nil
|
fr.prev_link = nil
|
||||||
fr.prev_link_type = nil
|
fr.prev_link_type = nil
|
||||||
@@ -242,7 +354,8 @@ local function stbrp__skyline_find_best_pos(c, width, height)
|
|||||||
while (node.x + width <= c.width) do
|
while (node.x + width <= c.width) do
|
||||||
local y, waste = stbrp__skyline_find_min_y(c, node, node.x, width)
|
local y, waste = stbrp__skyline_find_min_y(c, node, node.x, width)
|
||||||
|
|
||||||
if c.heuristic == STBRP_HEURISTIC_Skyline.BL_sortHeight then
|
if c.heuristic == STBRP_HEURISTIC_Skyline.BL_sortHeight then -- actually just want to test BL
|
||||||
|
-- bottom left
|
||||||
if y < best_y then
|
if y < best_y then
|
||||||
best_y = y
|
best_y = y
|
||||||
best_prev_link = prev_link
|
best_prev_link = prev_link
|
||||||
@@ -250,7 +363,9 @@ local function stbrp__skyline_find_best_pos(c, width, height)
|
|||||||
best_x = node.x
|
best_x = node.x
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
-- best-fit
|
||||||
if y + height <= c.height then
|
if y + height <= c.height then
|
||||||
|
-- can only use it if it first vertically
|
||||||
if y < best_y or (y == best_y and waste < best_waste) then
|
if y < best_y or (y == best_y and waste < best_waste) then
|
||||||
best_y = y
|
best_y = y
|
||||||
best_waste = waste
|
best_waste = waste
|
||||||
@@ -266,12 +381,30 @@ local function stbrp__skyline_find_best_pos(c, width, height)
|
|||||||
node = node.next
|
node = node.next
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- if doing best-fit (BF), we also have to try aligning right edge to each node position
|
||||||
|
--
|
||||||
|
-- e.g, if fitting
|
||||||
|
--
|
||||||
|
-- ____________________
|
||||||
|
-- |____________________|
|
||||||
|
--
|
||||||
|
-- into
|
||||||
|
--
|
||||||
|
-- | |
|
||||||
|
-- | ____________|
|
||||||
|
-- |____________|
|
||||||
|
--
|
||||||
|
-- then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
|
||||||
|
--
|
||||||
|
-- This makes BF take about 2x the time
|
||||||
|
|
||||||
if c.heuristic == STBRP_HEURISTIC_Skyline.BF_sortHeight then
|
if c.heuristic == STBRP_HEURISTIC_Skyline.BF_sortHeight then
|
||||||
local tail = c.active_head
|
local tail = c.active_head
|
||||||
local node_scan = c.active_head
|
local node_scan = c.active_head
|
||||||
local prev_link_scan = c
|
local prev_link_scan = c
|
||||||
local prev_link_scan_type = STBRP__PREV_LINK_TYPE.ACTIVE_HEAD
|
local prev_link_scan_type = STBRP__PREV_LINK_TYPE.ACTIVE_HEAD
|
||||||
|
|
||||||
|
-- find first node that's admissible
|
||||||
while tail and tail.x < width do
|
while tail and tail.x < width do
|
||||||
tail = tail.next
|
tail = tail.next
|
||||||
end
|
end
|
||||||
@@ -280,6 +413,7 @@ local function stbrp__skyline_find_best_pos(c, width, height)
|
|||||||
local xpos = tail.x - width
|
local xpos = tail.x - width
|
||||||
STBRP_ASSERT(xpos >= 0)
|
STBRP_ASSERT(xpos >= 0)
|
||||||
|
|
||||||
|
-- find the left position that matches this
|
||||||
while node_scan.next and node_scan.next.x <= xpos do
|
while node_scan.next and node_scan.next.x <= xpos do
|
||||||
prev_link_scan = node_scan
|
prev_link_scan = node_scan
|
||||||
prev_link_scan_type = STBRP__PREV_LINK_TYPE.NEXT
|
prev_link_scan_type = STBRP__PREV_LINK_TYPE.NEXT
|
||||||
@@ -295,6 +429,7 @@ local function stbrp__skyline_find_best_pos(c, width, height)
|
|||||||
if y <= best_y then
|
if y <= best_y then
|
||||||
if y < best_y or waste < best_waste or (waste == best_waste and xpos < best_x) then
|
if y < best_y or waste < best_waste or (waste == best_waste and xpos < best_x) then
|
||||||
best_x = xpos
|
best_x = xpos
|
||||||
|
-- STBRP_ASSERT(y <= best_y); [DEAR IMGUI]
|
||||||
best_y = y
|
best_y = y
|
||||||
best_waste = waste
|
best_waste = waste
|
||||||
best_prev_link = prev_link_scan
|
best_prev_link = prev_link_scan
|
||||||
@@ -320,19 +455,29 @@ end
|
|||||||
--- @param height int
|
--- @param height int
|
||||||
--- @return stbrp__findresult
|
--- @return stbrp__findresult
|
||||||
local function stbrp__skyline_pack_rectangle(context, width, height)
|
local function stbrp__skyline_pack_rectangle(context, width, height)
|
||||||
|
-- find best position according to heuristic
|
||||||
local res = stbrp__skyline_find_best_pos(context, width, height)
|
local res = stbrp__skyline_find_best_pos(context, width, height)
|
||||||
|
|
||||||
|
-- bail if:
|
||||||
|
-- 1. it failed
|
||||||
|
-- 2. the best node doesn't fit (we don't always check this)
|
||||||
|
-- 3. we're out of memory
|
||||||
if res.prev_link == nil or res.y + height > context.height or context.free_head == nil then
|
if res.prev_link == nil or res.y + height > context.height or context.free_head == nil then
|
||||||
res.prev_link = nil
|
res.prev_link = nil
|
||||||
res.prev_link_type = nil
|
res.prev_link_type = nil
|
||||||
return res
|
return res
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- on success, create new node
|
||||||
local node = context.free_head
|
local node = context.free_head
|
||||||
node.x = res.x
|
node.x = res.x
|
||||||
node.y = res.y + height
|
node.y = res.y + height
|
||||||
context.free_head = node.next
|
context.free_head = node.next
|
||||||
|
|
||||||
|
-- insert the new node into the right starting point, and
|
||||||
|
-- let 'cur' point to the remaining nodes needing to be
|
||||||
|
-- stitched back in
|
||||||
|
|
||||||
local cur
|
local cur
|
||||||
if res.prev_link_type == STBRP__PREV_LINK_TYPE.ACTIVE_HEAD then
|
if res.prev_link_type == STBRP__PREV_LINK_TYPE.ACTIVE_HEAD then
|
||||||
cur = res.prev_link.active_head
|
cur = res.prev_link.active_head
|
||||||
@@ -341,6 +486,7 @@ local function stbrp__skyline_pack_rectangle(context, width, height)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if cur.x < res.x then
|
if cur.x < res.x then
|
||||||
|
-- preserve the existing one, so start testing with the next one
|
||||||
local next = cur.next
|
local next = cur.next
|
||||||
cur.next = node
|
cur.next = node
|
||||||
cur = next
|
cur = next
|
||||||
@@ -352,13 +498,17 @@ local function stbrp__skyline_pack_rectangle(context, width, height)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- from here, traverse cur and free the nodes, until we get to one
|
||||||
|
-- that shouldn't be freed
|
||||||
while cur.next and cur.next.x <= res.x + width do
|
while cur.next and cur.next.x <= res.x + width do
|
||||||
local next = cur.next
|
local next = cur.next
|
||||||
|
-- move the current node to the free list
|
||||||
cur.next = context.free_head
|
cur.next = context.free_head
|
||||||
context.free_head = cur
|
context.free_head = cur
|
||||||
cur = next
|
cur = next
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- stitch the list back in
|
||||||
node.next = cur
|
node.next = cur
|
||||||
|
|
||||||
if cur.x < res.x + width then
|
if cur.x < res.x + width then
|
||||||
@@ -390,6 +540,29 @@ local function rect_original_order(a, b)
|
|||||||
return a.was_packed < b.was_packed
|
return a.was_packed < b.was_packed
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Assign packed locations to rectangles. The rectangles are of type
|
||||||
|
-- 'stbrp_rect' defined above, stored in the array 'rects', and there
|
||||||
|
-- are 'num_rects' many of them.
|
||||||
|
--
|
||||||
|
-- Rectangles which are successfully packed have the 'was_packed' flag
|
||||||
|
-- set to a non-zero value and 'x' and 'y' store the minimum location
|
||||||
|
-- on each axis (i.e. bottom-left in cartesian coordinates, top-left
|
||||||
|
-- if you imagine y increasing downwards). Rectangles which do not fit
|
||||||
|
-- have the 'was_packed' flag set to 0.
|
||||||
|
--
|
||||||
|
-- You should not try to access the 'rects' array from another thread
|
||||||
|
-- while this function is running, as the function temporarily reorders
|
||||||
|
-- the array while it executes.
|
||||||
|
--
|
||||||
|
-- To pack into another rectangle, you need to call stbrp_init_target
|
||||||
|
-- again. To continue packing into the same rectangle, you can call
|
||||||
|
-- this function again. Calling this multiple times with multiple rect
|
||||||
|
-- arrays will probably produce worse packing results than calling it
|
||||||
|
-- a single time with the full rectangle array, but the option is
|
||||||
|
-- available.
|
||||||
|
--
|
||||||
|
-- The function returns 1 if all of the rectangles were successfully
|
||||||
|
-- packed and 0 otherwise.
|
||||||
--- @param context stbrp_context
|
--- @param context stbrp_context
|
||||||
--- @param rects stbrp_rect[]
|
--- @param rects stbrp_rect[]
|
||||||
--- @param num_rects int
|
--- @param num_rects int
|
||||||
@@ -397,15 +570,17 @@ end
|
|||||||
local function stbrp_pack_rects(context, rects, num_rects)
|
local function stbrp_pack_rects(context, rects, num_rects)
|
||||||
local all_rects_packed = 1
|
local all_rects_packed = 1
|
||||||
|
|
||||||
|
-- we use the 'was_packed' field internally to allow sorting/unsorting
|
||||||
for i = 1, num_rects do
|
for i = 1, num_rects do
|
||||||
rects[i].was_packed = (i - 1)
|
rects[i].was_packed = (i - 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- sort according to heuristic
|
||||||
STBRP_SORT(rects, rect_height_compare)
|
STBRP_SORT(rects, rect_height_compare)
|
||||||
|
|
||||||
for i = 1, num_rects do
|
for i = 1, num_rects do
|
||||||
if rects[i].w == 0 or rects[i].h == 0 then
|
if rects[i].w == 0 or rects[i].h == 0 then
|
||||||
rects[i].x = 0
|
rects[i].x = 0 -- empty rect needs no space
|
||||||
rects[i].y = 0
|
rects[i].y = 0
|
||||||
else
|
else
|
||||||
local fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h)
|
local fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h)
|
||||||
@@ -419,8 +594,10 @@ local function stbrp_pack_rects(context, rects, num_rects)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- unsort
|
||||||
STBRP_SORT(rects, rect_original_order)
|
STBRP_SORT(rects, rect_original_order)
|
||||||
|
|
||||||
|
-- set was_packed flags and all_rects_packed status
|
||||||
for i = 1, num_rects do
|
for i = 1, num_rects do
|
||||||
rects[i].was_packed = (rects[i].x == STBRP__MAXVAL and rects[i].y == STBRP__MAXVAL) and 0 or 1
|
rects[i].was_packed = (rects[i].x == STBRP__MAXVAL and rects[i].y == STBRP__MAXVAL) and 0 or 1
|
||||||
if rects[i].was_packed == 0 then
|
if rects[i].was_packed == 0 then
|
||||||
@@ -428,6 +605,7 @@ local function stbrp_pack_rects(context, rects, num_rects)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- return the all_rects_packed status
|
||||||
return all_rects_packed
|
return all_rects_packed
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -441,3 +619,43 @@ return {
|
|||||||
setup_allow_out_of_mem = stbrp_setup_allow_out_of_mem,
|
setup_allow_out_of_mem = stbrp_setup_allow_out_of_mem,
|
||||||
setup_heuristic = stbrp_setup_heuristic
|
setup_heuristic = stbrp_setup_heuristic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- ------------------------------------------------------------------------------
|
||||||
|
-- This software is available under 2 licenses -- choose whichever you prefer.
|
||||||
|
-- ------------------------------------------------------------------------------
|
||||||
|
-- ALTERNATIVE A - MIT License
|
||||||
|
-- Copyright (c) 2017 Sean Barrett
|
||||||
|
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
-- this software and associated documentation files (the "Software"), to deal in
|
||||||
|
-- the Software without restriction, including without limitation the rights to
|
||||||
|
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
-- of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
-- so, subject to the following conditions:
|
||||||
|
-- The above copyright notice and this permission notice shall be included in all
|
||||||
|
-- copies or substantial portions of the Software.
|
||||||
|
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
-- SOFTWARE.
|
||||||
|
-- ------------------------------------------------------------------------------
|
||||||
|
-- ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||||
|
-- This is free and unencumbered software released into the public domain.
|
||||||
|
-- Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||||
|
-- software, either in source code form or as a compiled binary, for any purpose,
|
||||||
|
-- commercial or non-commercial, and by any means.
|
||||||
|
-- In jurisdictions that recognize copyright laws, the author or authors of this
|
||||||
|
-- software dedicate any and all copyright interest in the software to the public
|
||||||
|
-- domain. We make this dedication for the benefit of the public at large and to
|
||||||
|
-- the detriment of our heirs and successors. We intend this dedication to be an
|
||||||
|
-- overt act of relinquishment in perpetuity of all present and future rights to
|
||||||
|
-- this software under copyright law.
|
||||||
|
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
-- AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
-- ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
-- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
-- ------------------------------------------------------------------------------
|
||||||
|
|||||||
+336
-35
@@ -1,6 +1,221 @@
|
|||||||
--- Dear ImGui
|
--- Dear ImGui
|
||||||
-- This is a Lua port of original `imstb_textedit.h`
|
-- This is a Lua port of original `imstb_textedit.h`
|
||||||
|
|
||||||
|
-- [DEAR IMGUI]
|
||||||
|
-- This is a slightly modified version of stb_textedit.h 1.14.
|
||||||
|
-- Those changes would need to be pushed into nothings/stb:
|
||||||
|
-- - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
|
||||||
|
-- - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
|
||||||
|
-- - Added name to struct or it may be forward declared in our code.
|
||||||
|
-- - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925)
|
||||||
|
-- - Changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion.
|
||||||
|
-- Grep for [DEAR IMGUI] to find some changes.
|
||||||
|
-- - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
|
||||||
|
|
||||||
|
-- stb_textedit.h - v1.14 - public domain - Sean Barrett
|
||||||
|
-- Development of this library was sponsored by RAD Game Tools
|
||||||
|
--
|
||||||
|
-- This C header file implements the guts of a multi-line text-editing
|
||||||
|
-- widget; you implement display, word-wrapping, and low-level string
|
||||||
|
-- insertion/deletion, and stb_textedit will map user inputs into
|
||||||
|
-- insertions & deletions, plus updates to the cursor position,
|
||||||
|
-- selection state, and undo state.
|
||||||
|
--
|
||||||
|
-- It is intended for use in games and other systems that need to build
|
||||||
|
-- their own custom widgets and which do not have heavy text-editing
|
||||||
|
-- requirements (this library is not recommended for use for editing large
|
||||||
|
-- texts, as its performance does not scale and it has limited undo).
|
||||||
|
--
|
||||||
|
-- Non-trivial behaviors are modelled after Windows text controls.
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- LICENSE
|
||||||
|
--
|
||||||
|
-- See end of file for license information.
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- DEPENDENCIES
|
||||||
|
--
|
||||||
|
-- Uses the C runtime function 'memmove', which you can override
|
||||||
|
-- by defining IMSTB_TEXTEDIT_memmove before the implementation.
|
||||||
|
-- (In this port: ImStb.TEXTEDIT_memmove.)
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- VERSION HISTORY
|
||||||
|
--
|
||||||
|
-- !!!! (2025-10-23) changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion.
|
||||||
|
-- 1.14 (2021-07-11) page up/down, various fixes
|
||||||
|
-- 1.13 (2019-02-07) fix bug in undo size management
|
||||||
|
-- 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash
|
||||||
|
-- 1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield
|
||||||
|
-- 1.10 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
|
||||||
|
-- 1.9 (2016-08-27) customizable move-by-word
|
||||||
|
-- 1.8 (2016-04-02) better keyboard handling when mouse button is down
|
||||||
|
-- 1.7 (2015-09-13) change y range handling in case baseline is non-0
|
||||||
|
-- 1.6 (2015-04-15) allow STB_TEXTEDIT_memmove
|
||||||
|
-- 1.5 (2014-09-10) add support for secondary keys for OS X
|
||||||
|
-- 1.4 (2014-08-17) fix signed/unsigned warnings
|
||||||
|
-- 1.3 (2014-06-19) fix mouse clicking to round to nearest char boundary
|
||||||
|
-- 1.2 (2014-05-27) fix some RAD types that had crept into the new code
|
||||||
|
-- 1.1 (2013-12-15) move-by-word (requires STB_TEXTEDIT_IS_SPACE )
|
||||||
|
-- 1.0 (2012-07-26) improve documentation, initial public release
|
||||||
|
-- 0.3 (2012-02-24) bugfixes, single-line mode; insert mode
|
||||||
|
-- 0.2 (2011-11-28) fixes to undo/redo
|
||||||
|
-- 0.1 (2010-07-08) initial version
|
||||||
|
--
|
||||||
|
-- ADDITIONAL CONTRIBUTORS
|
||||||
|
--
|
||||||
|
-- Ulf Winklemann: move-by-word in 1.1
|
||||||
|
-- Fabian Giesen: secondary key inputs in 1.5
|
||||||
|
-- Martins Mozeiko: STB_TEXTEDIT_memmove in 1.6
|
||||||
|
-- Louis Schnellbach: page up/down in 1.14
|
||||||
|
--
|
||||||
|
-- Bugfixes:
|
||||||
|
-- Scott Graham
|
||||||
|
-- Daniel Keller
|
||||||
|
-- Omar Cornut
|
||||||
|
-- Dan Thompson
|
||||||
|
--
|
||||||
|
-- USAGE
|
||||||
|
--
|
||||||
|
-- The basic concept is that you provide a "string" object which
|
||||||
|
-- behaves like an array of characters. stb_textedit uses indices to
|
||||||
|
-- refer to positions in the string, implicitly representing positions
|
||||||
|
-- in the displayed textedit. This is true for both plain text and
|
||||||
|
-- rich text; even with rich text stb_truetype interacts with your
|
||||||
|
-- code as if there was an array of all the displayed characters.
|
||||||
|
--
|
||||||
|
-- Symbols you must define for implementation mode (in this port: fields of ImStb, see below):
|
||||||
|
--
|
||||||
|
-- STB_TEXTEDIT_STRING the type of object representing a string being edited,
|
||||||
|
-- typically this is a wrapper object with other data you need
|
||||||
|
--
|
||||||
|
-- STB_TEXTEDIT_STRINGLEN(obj) the length of the string (ideally O(1))
|
||||||
|
-- STB_TEXTEDIT_LAYOUTROW(r,obj,n) returns the results of laying out a line of characters
|
||||||
|
-- starting from character #n (see discussion below)
|
||||||
|
-- STB_TEXTEDIT_GETWIDTH(obj,n,i) returns the pixel delta from the xpos of the i'th character
|
||||||
|
-- to the xpos of the i+1'th char for a line of characters
|
||||||
|
-- starting at character #n (i.e. accounts for kerning
|
||||||
|
-- with previous char)
|
||||||
|
-- STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 1-based in this port
|
||||||
|
-- STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize
|
||||||
|
-- as manually wordwrapping for end-of-line positioning
|
||||||
|
--
|
||||||
|
-- STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i
|
||||||
|
-- STB_TEXTEDIT_INSERTCHARS(obj,i,c,c_offset,n) try to insert n characters at i (taken from array c starting at c_offset)
|
||||||
|
-- returns number of characters actually inserted. [DEAR IMGUI]
|
||||||
|
--
|
||||||
|
-- STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key
|
||||||
|
--
|
||||||
|
-- STB_TEXTEDIT_K_LEFT keyboard input to move cursor left
|
||||||
|
-- STB_TEXTEDIT_K_RIGHT keyboard input to move cursor right
|
||||||
|
-- STB_TEXTEDIT_K_UP keyboard input to move cursor up
|
||||||
|
-- STB_TEXTEDIT_K_DOWN keyboard input to move cursor down
|
||||||
|
-- STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page
|
||||||
|
-- STB_TEXTEDIT_K_PGDOWN keyboard input to move cursor down a page
|
||||||
|
-- STB_TEXTEDIT_K_LINESTART keyboard input to move cursor to start of line -- e.g. HOME
|
||||||
|
-- STB_TEXTEDIT_K_LINEEND keyboard input to move cursor to end of line -- e.g. END
|
||||||
|
-- STB_TEXTEDIT_K_TEXTSTART keyboard input to move cursor to start of text -- e.g. ctrl-HOME
|
||||||
|
-- STB_TEXTEDIT_K_TEXTEND keyboard input to move cursor to end of text -- e.g. ctrl-END
|
||||||
|
-- STB_TEXTEDIT_K_DELETE keyboard input to delete selection or character under cursor
|
||||||
|
-- STB_TEXTEDIT_K_BACKSPACE keyboard input to delete selection or character left of cursor
|
||||||
|
-- STB_TEXTEDIT_K_UNDO keyboard input to perform undo
|
||||||
|
-- STB_TEXTEDIT_K_REDO keyboard input to perform redo
|
||||||
|
--
|
||||||
|
-- STB_TEXTEDIT_MOVEWORDLEFT(obj,i) custom handler for WORDLEFT, returns index to move cursor to
|
||||||
|
-- STB_TEXTEDIT_MOVEWORDRIGHT(obj,i) custom handler for WORDRIGHT, returns index to move cursor to
|
||||||
|
-- STB_TEXTEDIT_K_WORDLEFT keyboard input to move cursor left one word -- e.g. ctrl-LEFT
|
||||||
|
-- STB_TEXTEDIT_K_WORDRIGHT keyboard input to move cursor right one word -- e.g. ctrl-RIGHT
|
||||||
|
--
|
||||||
|
-- To support UTF-8:
|
||||||
|
--
|
||||||
|
-- STB_TEXTEDIT_GETPREVCHARINDEX returns index of previous character
|
||||||
|
-- STB_TEXTEDIT_GETNEXTCHARINDEX returns index of next character
|
||||||
|
-- Do NOT define STB_TEXTEDIT_KEYTOTEXT.
|
||||||
|
-- Instead, call stb_textedit_text() directly for text contents.
|
||||||
|
--
|
||||||
|
-- Keyboard input must be encoded as a single integer value; e.g. a character code
|
||||||
|
-- and some bitflags that represent shift states. to simplify the interface, SHIFT must
|
||||||
|
-- be a bitflag, so we can test the shifted state of cursor movements to allow selection,
|
||||||
|
-- i.e. (STB_TEXTEDIT_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow.
|
||||||
|
--
|
||||||
|
-- STB_TEXTEDIT_LAYOUTROW returns information about the shape of one displayed
|
||||||
|
-- row of characters assuming they start on the i'th character--the width and
|
||||||
|
-- the height and the number of characters consumed. This allows this library
|
||||||
|
-- to traverse the entire layout incrementally. You need to compute word-wrapping
|
||||||
|
-- here.
|
||||||
|
--
|
||||||
|
-- Each textfield keeps its own insert mode state, which is not how normal
|
||||||
|
-- applications work. To keep an app-wide insert mode, update/copy the
|
||||||
|
-- "insert_mode" field of STB_TexteditState before/after calling API functions.
|
||||||
|
--
|
||||||
|
-- API (in this port: fields of the table returned by this file)
|
||||||
|
--
|
||||||
|
-- initialize_state(state, is_single_line)
|
||||||
|
--
|
||||||
|
-- click(str, state, x, y)
|
||||||
|
-- drag(str, state, x, y)
|
||||||
|
-- cut(str, state) -> bool
|
||||||
|
-- paste(str, state, text, len) -> bool
|
||||||
|
-- key(str, state, key)
|
||||||
|
-- text(str, state, text, text_len)
|
||||||
|
--
|
||||||
|
-- Each of these functions potentially updates the string and updates the
|
||||||
|
-- state.
|
||||||
|
--
|
||||||
|
-- initialize_state:
|
||||||
|
-- set the textedit state to a known good default state when initially
|
||||||
|
-- constructing the textedit.
|
||||||
|
--
|
||||||
|
-- click:
|
||||||
|
-- call this with the mouse x,y on a mouse down; it will update the cursor
|
||||||
|
-- and reset the selection start/end to the cursor point. the x,y must
|
||||||
|
-- be relative to the text widget, with (0,0) being the top left.
|
||||||
|
--
|
||||||
|
-- drag:
|
||||||
|
-- call this with the mouse x,y on a mouse drag/up; it will update the
|
||||||
|
-- cursor and the selection end point
|
||||||
|
--
|
||||||
|
-- cut:
|
||||||
|
-- call this to delete the current selection; returns true if there was
|
||||||
|
-- one. you should FIRST copy the current selection to the system paste buffer.
|
||||||
|
-- (To copy, just copy the current selection out of the string yourself.)
|
||||||
|
--
|
||||||
|
-- paste:
|
||||||
|
-- call this to paste text at the current cursor point or over the current
|
||||||
|
-- selection if there is one.
|
||||||
|
--
|
||||||
|
-- key:
|
||||||
|
-- call this for keyboard inputs sent to the textfield. you can use it
|
||||||
|
-- for "key down" events or for "translated" key events.
|
||||||
|
--
|
||||||
|
-- text: (added 2025)
|
||||||
|
-- call this to directly send text input the textfield, which is required
|
||||||
|
-- for UTF-8 support, because stb_textedit_key() + STB_TEXTEDIT_KEYTOTEXT()
|
||||||
|
-- cannot infer text length.
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- When rendering, you can read the cursor position and selection state from
|
||||||
|
-- the STB_TexteditState.
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- Notes:
|
||||||
|
--
|
||||||
|
-- This is designed to be usable in IMGUI, so it allows for the possibility of
|
||||||
|
-- running in an IMGUI that has NOT cached the multi-line layout. For this
|
||||||
|
-- reason, it provides an interface that is compatible with computing the
|
||||||
|
-- layout incrementally--we try to make sure we make as few passes through
|
||||||
|
-- as possible. (For example, to locate the mouse pointer in the text, we
|
||||||
|
-- could define functions that return the X and Y positions of characters
|
||||||
|
-- and binary search Y and then X, but if we're doing dynamic layout this
|
||||||
|
-- will run the layout algorithm many times, so instead we manually search
|
||||||
|
-- forward in one pass. Similar logic applies to e.g. up-arrow and
|
||||||
|
-- down-arrow movement.)
|
||||||
|
--
|
||||||
|
-- If it's run in a widget that *has* cached the layout, then this is less
|
||||||
|
-- efficient, but it's not horrible on modern computers. But you wouldn't
|
||||||
|
-- want to edit million-line files with it.
|
||||||
|
|
||||||
-- ALL TABLES IN THIS FILE ARE 1-BASED!
|
-- ALL TABLES IN THIS FILE ARE 1-BASED!
|
||||||
-- Character Indexing:
|
-- Character Indexing:
|
||||||
-- idx=n, the n'th char (1-based)
|
-- idx=n, the n'th char (1-based)
|
||||||
@@ -13,6 +228,10 @@ local STB_TEXTEDIT_STRINGLEN = ImStb.TEXTEDIT_STRINGLEN
|
|||||||
local STB_TEXTEDIT_GETCHAR = ImStb.TEXTEDIT_GETCHAR
|
local STB_TEXTEDIT_GETCHAR = ImStb.TEXTEDIT_GETCHAR
|
||||||
local STB_TEXTEDIT_GETWIDTH = ImStb.TEXTEDIT_GETWIDTH
|
local STB_TEXTEDIT_GETWIDTH = ImStb.TEXTEDIT_GETWIDTH
|
||||||
local STB_TEXTEDIT_LAYOUTROW = ImStb.TEXTEDIT_LAYOUTROW
|
local STB_TEXTEDIT_LAYOUTROW = ImStb.TEXTEDIT_LAYOUTROW
|
||||||
|
-- [DEAR IMGUI]
|
||||||
|
-- Functions must be implemented for UTF8 support
|
||||||
|
-- Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
|
||||||
|
-- There is not necessarily a '[DEAR IMGUI]' at the usage sites.
|
||||||
local IMSTB_TEXTEDIT_GETNEXTCHARINDEX = ImStb.TEXTEDIT_GETNEXTCHARINDEX
|
local IMSTB_TEXTEDIT_GETNEXTCHARINDEX = ImStb.TEXTEDIT_GETNEXTCHARINDEX
|
||||||
local IMSTB_TEXTEDIT_GETPREVCHARINDEX = ImStb.TEXTEDIT_GETPREVCHARINDEX
|
local IMSTB_TEXTEDIT_GETPREVCHARINDEX = ImStb.TEXTEDIT_GETPREVCHARINDEX
|
||||||
local STB_TEXTEDIT_MOVEWORDLEFT = ImStb.TEXTEDIT_MOVEWORDLEFT
|
local STB_TEXTEDIT_MOVEWORDLEFT = ImStb.TEXTEDIT_MOVEWORDLEFT
|
||||||
@@ -22,34 +241,35 @@ local STB_TEXTEDIT_MOVELINEEND = ImStb.TEXTEDIT_MOVELINEEND
|
|||||||
local STB_TEXTEDIT_DELETECHARS = ImStb.TEXTEDIT_DELETECHARS
|
local STB_TEXTEDIT_DELETECHARS = ImStb.TEXTEDIT_DELETECHARS
|
||||||
local STB_TEXTEDIT_INSERTCHARS = ImStb.TEXTEDIT_INSERTCHARS
|
local STB_TEXTEDIT_INSERTCHARS = ImStb.TEXTEDIT_INSERTCHARS
|
||||||
|
|
||||||
STB_TEXTEDIT_NEWLINE = 10
|
STB_TEXTEDIT_NEWLINE = 10 -- the character returned by _GETCHAR() we recognize as manually wordwrapping for end-of-line positioning
|
||||||
STB_TEXTEDIT_K_LEFT = 0x200000 -- keyboard input to move cursor left
|
STB_TEXTEDIT_K_LEFT = 0x200000 -- keyboard input to move cursor left
|
||||||
STB_TEXTEDIT_K_RIGHT = 0x200001 -- keyboard input to move cursor right
|
STB_TEXTEDIT_K_RIGHT = 0x200001 -- keyboard input to move cursor right
|
||||||
STB_TEXTEDIT_K_UP = 0x200002 -- keyboard input to move cursor up
|
STB_TEXTEDIT_K_UP = 0x200002 -- keyboard input to move cursor up
|
||||||
STB_TEXTEDIT_K_DOWN = 0x200003 -- keyboard input to move cursor down
|
STB_TEXTEDIT_K_DOWN = 0x200003 -- keyboard input to move cursor down
|
||||||
STB_TEXTEDIT_K_LINESTART = 0x200004 -- keyboard input to move cursor to start of line
|
STB_TEXTEDIT_K_LINESTART = 0x200004 -- keyboard input to move cursor to start of line -- e.g. HOME
|
||||||
STB_TEXTEDIT_K_LINEEND = 0x200005 -- keyboard input to move cursor to end of line
|
STB_TEXTEDIT_K_LINEEND = 0x200005 -- keyboard input to move cursor to end of line -- e.g. END
|
||||||
STB_TEXTEDIT_K_TEXTSTART = 0x200006 -- keyboard input to move cursor to start of text
|
STB_TEXTEDIT_K_TEXTSTART = 0x200006 -- keyboard input to move cursor to start of text -- e.g. ctrl-HOME
|
||||||
STB_TEXTEDIT_K_TEXTEND = 0x200007 -- keyboard input to move cursor to end of text
|
STB_TEXTEDIT_K_TEXTEND = 0x200007 -- keyboard input to move cursor to end of text -- e.g. ctrl-END
|
||||||
STB_TEXTEDIT_K_DELETE = 0x200008 -- keyboard input to delete selection or character under cursor
|
STB_TEXTEDIT_K_DELETE = 0x200008 -- keyboard input to delete selection or character under cursor
|
||||||
STB_TEXTEDIT_K_BACKSPACE = 0x200009 -- keyboard input to delete selection or character left of cursor
|
STB_TEXTEDIT_K_BACKSPACE = 0x200009 -- keyboard input to delete selection or character left of cursor
|
||||||
STB_TEXTEDIT_K_UNDO = 0x20000A -- keyboard input to perform undo
|
STB_TEXTEDIT_K_UNDO = 0x20000A -- keyboard input to perform undo
|
||||||
STB_TEXTEDIT_K_REDO = 0x20000B -- keyboard input to perform redo
|
STB_TEXTEDIT_K_REDO = 0x20000B -- keyboard input to perform redo
|
||||||
STB_TEXTEDIT_K_WORDLEFT = 0x20000C -- keyboard input to move cursor left one word
|
STB_TEXTEDIT_K_WORDLEFT = 0x20000C -- keyboard input to move cursor left one word -- e.g. ctrl-LEFT
|
||||||
STB_TEXTEDIT_K_WORDRIGHT = 0x20000D -- keyboard input to move cursor right one word
|
STB_TEXTEDIT_K_WORDRIGHT = 0x20000D -- keyboard input to move cursor right one word -- e.g. ctrl-RIGHT
|
||||||
STB_TEXTEDIT_K_PGUP = 0x20000E -- keyboard input to move cursor up a page
|
STB_TEXTEDIT_K_PGUP = 0x20000E -- keyboard input to move cursor up a page
|
||||||
STB_TEXTEDIT_K_PGDOWN = 0x20000F -- keyboard input to move cursor down a page
|
STB_TEXTEDIT_K_PGDOWN = 0x20000F -- keyboard input to move cursor down a page
|
||||||
STB_TEXTEDIT_K_SHIFT = 0x400000
|
STB_TEXTEDIT_K_SHIFT = 0x400000 -- a power of two that is or'd in to a keyboard input to represent the shift key
|
||||||
|
|
||||||
IMSTB_TEXTEDIT_UNDOSTATECOUNT = 99
|
IMSTB_TEXTEDIT_UNDOSTATECOUNT = 99 -- the number of undo states to allow
|
||||||
local IMSTB_TEXTEDIT_UNDOCHARCOUNT = 999
|
local IMSTB_TEXTEDIT_UNDOCHARCOUNT = 999 -- the number of characters to store in the undo buffer
|
||||||
local IMSTB_TEXTEDIT_memmove = ImStb.TEXTEDIT_memmove
|
local IMSTB_TEXTEDIT_memmove = ImStb.TEXTEDIT_memmove
|
||||||
|
|
||||||
|
-- forward declarations
|
||||||
local stb_text_undo
|
local stb_text_undo
|
||||||
local stb_text_redo
|
local stb_text_redo
|
||||||
|
|
||||||
----------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
----------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
---
|
---
|
||||||
--- STB_TexteditState
|
--- STB_TexteditState
|
||||||
---
|
---
|
||||||
@@ -72,6 +292,7 @@ local stb_text_redo
|
|||||||
--- @nodiscard
|
--- @nodiscard
|
||||||
local function StbUndoRecord()
|
local function StbUndoRecord()
|
||||||
return {
|
return {
|
||||||
|
-- private data
|
||||||
where = 1,
|
where = 1,
|
||||||
insert_length = 0,
|
insert_length = 0,
|
||||||
delete_length = 0,
|
delete_length = 0,
|
||||||
@@ -101,6 +322,7 @@ local function StbUndoState()
|
|||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
-- private data
|
||||||
undo_rec = undo_rec,
|
undo_rec = undo_rec,
|
||||||
undo_char = undo_char,
|
undo_char = undo_char,
|
||||||
|
|
||||||
@@ -131,16 +353,34 @@ end
|
|||||||
--- @nodiscard
|
--- @nodiscard
|
||||||
function STB_TexteditState()
|
function STB_TexteditState()
|
||||||
return {
|
return {
|
||||||
cursor = 0,
|
---------------------
|
||||||
|
--
|
||||||
|
-- public data
|
||||||
|
--
|
||||||
|
|
||||||
select_start = 0,
|
cursor = 0,
|
||||||
|
-- position of the text cursor within the string
|
||||||
|
|
||||||
|
select_start = 0, -- selection start point
|
||||||
select_end = 0,
|
select_end = 0,
|
||||||
|
-- selection start and end point in characters; if equal, no selection.
|
||||||
|
-- note that start may be less than or greater than end (e.g. when
|
||||||
|
-- dragging the mouse, start is where the initial click was, and you
|
||||||
|
-- can drag in either direction)
|
||||||
|
|
||||||
insert_mode = false,
|
insert_mode = false,
|
||||||
|
-- each textfield keeps its own insert mode state. to keep an app-wide
|
||||||
|
-- insert mode, copy this value in/out of the app state
|
||||||
|
|
||||||
row_count_per_page = 0,
|
row_count_per_page = 0,
|
||||||
|
-- page size in number of row.
|
||||||
|
-- this value MUST be set to >0 for pageup or pagedown in multilines documents.
|
||||||
|
|
||||||
cursor_at_end_of_line = false,
|
---------------------
|
||||||
|
--
|
||||||
|
-- private data
|
||||||
|
--
|
||||||
|
cursor_at_end_of_line = false, -- not implemented yet
|
||||||
initialized = false,
|
initialized = false,
|
||||||
has_preferred_x = false,
|
has_preferred_x = false,
|
||||||
single_line = false,
|
single_line = false,
|
||||||
@@ -148,7 +388,7 @@ function STB_TexteditState()
|
|||||||
padding2 = 0,
|
padding2 = 0,
|
||||||
padding3 = 0,
|
padding3 = 0,
|
||||||
|
|
||||||
preferred_x = 0.0,
|
preferred_x = 0.0, -- this determines where the cursor up/down tries to seek to along x
|
||||||
|
|
||||||
undostate = StbUndoState()
|
undostate = StbUndoState()
|
||||||
}
|
}
|
||||||
@@ -159,12 +399,20 @@ local function STB_TEXT_HAS_SELECTION(s)
|
|||||||
return s.select_start ~= s.select_end
|
return s.select_start ~= s.select_end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
---
|
||||||
|
--- StbTexteditRow
|
||||||
|
---
|
||||||
|
--- Result of layout query, used by stb_textedit to determine where
|
||||||
|
--- the text in each row is.
|
||||||
|
|
||||||
|
-- result of layout query
|
||||||
--- @class StbTexteditRow
|
--- @class StbTexteditRow
|
||||||
--- @field x0 float # starting x location
|
--- @field x0 float # starting x location, end x location (allows for align=right, etc)
|
||||||
--- @field x1 float # end x location
|
--- @field x1 float # end x location
|
||||||
--- @field baseline_y_delta float # position of baseline relative to previous row's baseline
|
--- @field baseline_y_delta float # position of baseline relative to previous row's baseline
|
||||||
--- @field ymin float # height of row above baseline
|
--- @field ymin float # height of row above and below baseline
|
||||||
--- @field ymax float # height of row below baseline
|
--- @field ymax float # height of row above and below baseline
|
||||||
--- @field num_chars int
|
--- @field num_chars int
|
||||||
|
|
||||||
--- @return StbTexteditRow
|
--- @return StbTexteditRow
|
||||||
@@ -196,10 +444,15 @@ end
|
|||||||
----------------------------------------
|
----------------------------------------
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
---
|
---
|
||||||
--- Implementation
|
--- Implementation mode
|
||||||
---
|
---
|
||||||
---
|
---
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
---
|
||||||
|
--- Mouse input handling
|
||||||
|
---
|
||||||
|
|
||||||
local stb_text_locate_coord do
|
local stb_text_locate_coord do
|
||||||
|
|
||||||
-- only create once, reuse later
|
-- only create once, reuse later
|
||||||
@@ -351,6 +604,7 @@ end
|
|||||||
---
|
---
|
||||||
---
|
---
|
||||||
|
|
||||||
|
-- forward declarations
|
||||||
local stb_text_makeundo_insert
|
local stb_text_makeundo_insert
|
||||||
local stb_text_makeundo_delete
|
local stb_text_makeundo_delete
|
||||||
local stb_text_makeundo_replace
|
local stb_text_makeundo_replace
|
||||||
@@ -399,8 +653,8 @@ function stb_textedit_find_charpos(find, str, n, single_line)
|
|||||||
local z = STB_TEXTEDIT_STRINGLEN(str)
|
local z = STB_TEXTEDIT_STRINGLEN(str)
|
||||||
local first
|
local first
|
||||||
|
|
||||||
-- special case if it's at the end (may not be needed?)
|
|
||||||
if n == z + 1 and single_line then
|
if n == z + 1 and single_line then
|
||||||
|
-- special case if it's at the end (may not be needed?)
|
||||||
STB_TEXTEDIT_LAYOUTROW(r, str, 1)
|
STB_TEXTEDIT_LAYOUTROW(r, str, 1)
|
||||||
find.y = 0
|
find.y = 0
|
||||||
find.first_char = 1
|
find.first_char = 1
|
||||||
@@ -420,18 +674,18 @@ function stb_textedit_find_charpos(find, str, n, single_line)
|
|||||||
if n < i + r.num_chars - 1 then
|
if n < i + r.num_chars - 1 then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
if str.LastMoveDirectionLR == ImGuiDir.Right and str.Stb.cursor > 1 and str.Stb.cursor == i + r.num_chars and STB_TEXTEDIT_GETCHAR(str, i + r.num_chars - 1) ~= STB_TEXTEDIT_NEWLINE then -- [IMGUI] Wrapping point handling
|
if str.LastMoveDirectionLR == ImGuiDir.Right and str.Stb.cursor > 1 and str.Stb.cursor == i + r.num_chars and STB_TEXTEDIT_GETCHAR(str, i + r.num_chars - 1) ~= STB_TEXTEDIT_NEWLINE then -- [DEAR IMGUI] Wrapping point handling
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
if (i - 1) + r.num_chars == z and z > 0 and STB_TEXTEDIT_GETCHAR(str, z) ~= STB_TEXTEDIT_NEWLINE then -- [IMGUI] special handling for last line
|
if (i - 1) + r.num_chars == z and z > 0 and STB_TEXTEDIT_GETCHAR(str, z) ~= STB_TEXTEDIT_NEWLINE then -- [DEAR IMGUI] special handling for last line
|
||||||
break
|
break -- [DEAR IMGUI]
|
||||||
end
|
end
|
||||||
prev_start = i
|
prev_start = i
|
||||||
i = i + r.num_chars
|
i = i + r.num_chars
|
||||||
find.y = find.y + r.baseline_y_delta
|
find.y = find.y + r.baseline_y_delta
|
||||||
if i == z + 1 then -- [IMGUI]
|
if i == z + 1 then -- [DEAR IMGUI]
|
||||||
r.num_chars = 0
|
r.num_chars = 0 -- [DEAR IMGUI]
|
||||||
break
|
break -- [DEAR IMGUI]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -479,7 +733,7 @@ local function stb_textedit_delete(str, state, where, len)
|
|||||||
state.has_preferred_x = false
|
state.has_preferred_x = false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- delete the selection
|
-- delete the section
|
||||||
--- @param str IMSTB_TEXTEDIT_STRING
|
--- @param str IMSTB_TEXTEDIT_STRING
|
||||||
--- @param state STB_TexteditState
|
--- @param state STB_TexteditState
|
||||||
local function stb_textedit_delete_selection(str, state)
|
local function stb_textedit_delete_selection(str, state)
|
||||||
@@ -498,7 +752,7 @@ local function stb_textedit_delete_selection(str, state)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- canonicalize the selection so start <= end
|
-- canoncialize the selection so start <= end
|
||||||
--- @param state STB_TexteditState
|
--- @param state STB_TexteditState
|
||||||
local function stb_textedit_sortselection(state)
|
local function stb_textedit_sortselection(state)
|
||||||
if state.select_end < state.select_start then
|
if state.select_end < state.select_start then
|
||||||
@@ -579,7 +833,7 @@ local function stb_textedit_paste_internal(str, state, text, len)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- API key: process text input
|
-- API key: process text input
|
||||||
-- [IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
|
-- [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
|
||||||
--- @param str IMSTB_TEXTEDIT_STRING
|
--- @param str IMSTB_TEXTEDIT_STRING
|
||||||
--- @param state STB_TexteditState
|
--- @param state STB_TexteditState
|
||||||
--- @param text IMSTB_TEXTEDIT_CHARTYPE[]
|
--- @param text IMSTB_TEXTEDIT_CHARTYPE[]
|
||||||
@@ -732,7 +986,7 @@ function stb_textedit_key(str, state, key)
|
|||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
-- [IMGUI]
|
-- [DEAR IMGUI]
|
||||||
-- going down while being on the last line shouldn't bring us to that line end
|
-- going down while being on the last line shouldn't bring us to that line end
|
||||||
--if STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) ~= STB_TEXTEDIT_NEWLINE then
|
--if STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) ~= STB_TEXTEDIT_NEWLINE then
|
||||||
-- break
|
-- break
|
||||||
@@ -929,8 +1183,8 @@ end
|
|||||||
-----------------------------------------------------
|
-----------------------------------------------------
|
||||||
---
|
---
|
||||||
--- Undo processing
|
--- Undo processing
|
||||||
--- OPTIMIZE: the undo/redo buffer should be circular
|
|
||||||
---
|
---
|
||||||
|
--- @OPTIMIZE: the undo/redo buffer should be circular
|
||||||
|
|
||||||
--- @param state StbUndoState
|
--- @param state StbUndoState
|
||||||
local function stb_textedit_flush_redo(state)
|
local function stb_textedit_flush_redo(state)
|
||||||
@@ -950,7 +1204,7 @@ local function stb_textedit_discard_undo(state)
|
|||||||
IMSTB_TEXTEDIT_memmove(state.undo_char, 1, state.undo_char, n + 1, state.undo_char_point - 1)
|
IMSTB_TEXTEDIT_memmove(state.undo_char, 1, state.undo_char, n + 1, state.undo_char_point - 1)
|
||||||
for i = 1, state.undo_point do
|
for i = 1, state.undo_point do
|
||||||
if state.undo_rec[i].char_storage >= 0 then
|
if state.undo_rec[i].char_storage >= 0 then
|
||||||
state.undo_rec[i].char_storage = state.undo_rec[i].char_storage - n -- OPTIMIZE: get rid of char_storage and infer it
|
state.undo_rec[i].char_storage = state.undo_rec[i].char_storage - n -- @OPTIMIZE: get rid of char_storage and infer it
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -983,7 +1237,7 @@ local function stb_textedit_discard_redo(state)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- now move all the redo records towards the end of the buffer; the first one is at 'redo_point'
|
-- now move all the redo records towards the end of the buffer; the first one is at 'redo_point'
|
||||||
-- [IMGUI]
|
-- [DEAR IMGUI]
|
||||||
local move_size = IMSTB_TEXTEDIT_UNDOSTATECOUNT - state.redo_point - 1
|
local move_size = IMSTB_TEXTEDIT_UNDOSTATECOUNT - state.redo_point - 1
|
||||||
IMSTB_TEXTEDIT_memmove(state.undo_rec, state.redo_point + 1, state.undo_rec, state.redo_point, move_size)
|
IMSTB_TEXTEDIT_memmove(state.undo_rec, state.redo_point + 1, state.undo_rec, state.redo_point, move_size)
|
||||||
|
|
||||||
@@ -1129,6 +1383,9 @@ function stb_text_redo(str, state)
|
|||||||
local u = s.undo_rec[s.undo_point]
|
local u = s.undo_rec[s.undo_point]
|
||||||
local r = s.undo_rec[s.redo_point]
|
local r = s.undo_rec[s.redo_point]
|
||||||
|
|
||||||
|
-- we KNOW there must be room for the undo record, because the redo record
|
||||||
|
-- was derived from an undo record
|
||||||
|
|
||||||
u.delete_length = r.insert_length
|
u.delete_length = r.insert_length
|
||||||
u.insert_length = r.delete_length
|
u.insert_length = r.delete_length
|
||||||
u.where = r.where
|
u.where = r.where
|
||||||
@@ -1137,6 +1394,7 @@ function stb_text_redo(str, state)
|
|||||||
if r.delete_length > 0 then
|
if r.delete_length > 0 then
|
||||||
-- the redo record requires us to delete characters, so the undo record
|
-- the redo record requires us to delete characters, so the undo record
|
||||||
-- needs to store the characters
|
-- needs to store the characters
|
||||||
|
|
||||||
if s.undo_char_point + u.insert_length > s.redo_char_point then
|
if s.undo_char_point + u.insert_length > s.redo_char_point then
|
||||||
u.insert_length = 0
|
u.insert_length = 0
|
||||||
u.delete_length = 0
|
u.delete_length = 0
|
||||||
@@ -1144,6 +1402,7 @@ function stb_text_redo(str, state)
|
|||||||
u.char_storage = s.undo_char_point
|
u.char_storage = s.undo_char_point
|
||||||
s.undo_char_point = s.undo_char_point + u.insert_length
|
s.undo_char_point = s.undo_char_point + u.insert_length
|
||||||
|
|
||||||
|
-- now save the characters
|
||||||
for i = 0, u.insert_length - 1 do
|
for i = 0, u.insert_length - 1 do
|
||||||
s.undo_char[u.char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u.where + i)
|
s.undo_char[u.char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u.where + i)
|
||||||
end
|
end
|
||||||
@@ -1153,6 +1412,7 @@ function stb_text_redo(str, state)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if r.insert_length ~= 0 then
|
if r.insert_length ~= 0 then
|
||||||
|
-- easy case: need to insert n characters
|
||||||
r.insert_length = STB_TEXTEDIT_INSERTCHARS(str, r.where, s.undo_char, r.char_storage, r.insert_length)
|
r.insert_length = STB_TEXTEDIT_INSERTCHARS(str, r.where, s.undo_char, r.char_storage, r.insert_length)
|
||||||
s.redo_char_point = s.redo_char_point + r.insert_length
|
s.redo_char_point = s.redo_char_point + r.insert_length
|
||||||
end
|
end
|
||||||
@@ -1199,6 +1459,7 @@ function stb_text_makeundo_replace(str, state, where, old_length, new_length)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- reset the state to default
|
||||||
--- @param state STB_TexteditState
|
--- @param state STB_TexteditState
|
||||||
--- @param is_single_line bool
|
--- @param is_single_line bool
|
||||||
local function stb_textedit_clear_state(state, is_single_line)
|
local function stb_textedit_clear_state(state, is_single_line)
|
||||||
@@ -1222,7 +1483,7 @@ return {
|
|||||||
click = stb_textedit_click,
|
click = stb_textedit_click,
|
||||||
drag = stb_textedit_drag,
|
drag = stb_textedit_drag,
|
||||||
createundo = stb_text_createundo,
|
createundo = stb_text_createundo,
|
||||||
initialize_state = stb_textedit_clear_state,
|
initialize_state = stb_textedit_clear_state, -- API initialize
|
||||||
|
|
||||||
text = stb_textedit_text,
|
text = stb_textedit_text,
|
||||||
key = stb_textedit_key,
|
key = stb_textedit_key,
|
||||||
@@ -1236,3 +1497,43 @@ return {
|
|||||||
|
|
||||||
HAS_SELECTION = STB_TEXT_HAS_SELECTION
|
HAS_SELECTION = STB_TEXT_HAS_SELECTION
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- This software is available under 2 licenses -- choose whichever you prefer.
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- ALTERNATIVE A - MIT License
|
||||||
|
-- Copyright (c) 2017 Sean Barrett
|
||||||
|
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
-- this software and associated documentation files (the "Software"), to deal in
|
||||||
|
-- the Software without restriction, including without limitation the rights to
|
||||||
|
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
-- of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
-- so, subject to the following conditions:
|
||||||
|
-- The above copyright notice and this permission notice shall be included in all
|
||||||
|
-- copies or substantial portions of the Software.
|
||||||
|
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
-- SOFTWARE.
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||||
|
-- This is free and unencumbered software released into the public domain.
|
||||||
|
-- Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||||
|
-- software, either in source code form or as a compiled binary, for any purpose,
|
||||||
|
-- commercial or non-commercial, and by any means.
|
||||||
|
-- In jurisdictions that recognize copyright laws, the author or authors of this
|
||||||
|
-- software dedicate any and all copyright interest in the software to the public
|
||||||
|
-- domain. We make this dedication for the benefit of the public at large and to
|
||||||
|
-- the detriment of our heirs and successors. We intend this dedication to be an
|
||||||
|
-- overt act of relinquishment in perpetuity of all present and future rights to
|
||||||
|
-- this software under copyright law.
|
||||||
|
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
-- AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
-- ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
-- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|||||||
+374
-54
@@ -1,6 +1,164 @@
|
|||||||
--- Dear ImGui
|
--- Dear ImGui
|
||||||
-- This is a Lua port of original `imstb_truetype.h`
|
-- This is a Lua port of original `imstb_truetype.h`
|
||||||
|
|
||||||
|
-- [DEAR IMGUI]
|
||||||
|
-- This is a slightly modified version of stb_truetype.h 1.26.
|
||||||
|
-- Mostly fixing for compiler and static analyzer warnings.
|
||||||
|
-- Grep for [DEAR IMGUI] to find the changes.
|
||||||
|
|
||||||
|
-- stb_truetype.h - v1.26 - public domain
|
||||||
|
-- authored from 2009-2021 by Sean Barrett / RAD Game Tools
|
||||||
|
--
|
||||||
|
-- =======================================================================
|
||||||
|
--
|
||||||
|
-- NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES
|
||||||
|
--
|
||||||
|
-- This library does no range checking of the offsets found in the file,
|
||||||
|
-- meaning an attacker can use it to read arbitrary memory.
|
||||||
|
--
|
||||||
|
-- =======================================================================
|
||||||
|
--
|
||||||
|
-- This library processes TrueType files:
|
||||||
|
-- parse files
|
||||||
|
-- extract glyph metrics
|
||||||
|
-- extract glyph shapes
|
||||||
|
-- render glyphs to one-channel bitmaps with antialiasing (box filter)
|
||||||
|
--
|
||||||
|
-- Todo:
|
||||||
|
-- non-MS cmaps
|
||||||
|
-- crashproof on bad data
|
||||||
|
-- hinting? (no longer patented)
|
||||||
|
-- cleartype-style AA?
|
||||||
|
-- optimize: build edge-list directly from curves
|
||||||
|
-- optimize: rasterize directly from curves?
|
||||||
|
--
|
||||||
|
-- ADDITIONAL CONTRIBUTORS
|
||||||
|
--
|
||||||
|
-- Mikko Mononen: compound shape support, more cmap formats
|
||||||
|
-- Tor Andersson: kerning, subpixel rendering
|
||||||
|
-- Dougall Johnson: OpenType / Type 2 font handling
|
||||||
|
-- Daniel Ribeiro Maciel: basic GPOS-based kerning
|
||||||
|
--
|
||||||
|
-- Misc other:
|
||||||
|
-- Ryan Gordon
|
||||||
|
-- Simon Glass
|
||||||
|
-- github:IntellectualKitty
|
||||||
|
-- Imanol Celaya
|
||||||
|
-- Daniel Ribeiro Maciel
|
||||||
|
--
|
||||||
|
-- Bug/warning reports/fixes:
|
||||||
|
-- "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe
|
||||||
|
-- Cass Everitt Martins Mozeiko github:aloucks
|
||||||
|
-- stoiko (Haemimont Games) Cap Petschulat github:oyvindjam
|
||||||
|
-- Brian Hook Omar Cornut github:vassvik
|
||||||
|
-- Walter van Niftrik Ryan Griege
|
||||||
|
-- David Gow Peter LaValle
|
||||||
|
-- David Given Sergey Popov
|
||||||
|
-- Ivan-Assen Ivanov Giumo X. Clanjor
|
||||||
|
-- Anthony Pesch Higor Euripedes
|
||||||
|
-- Johan Duparc Thomas Fields
|
||||||
|
-- Hou Qiming Derek Vinyard
|
||||||
|
-- Rob Loach Cort Stratton
|
||||||
|
-- Kenney Phillis Jr. Brian Costabile
|
||||||
|
-- Ken Voskuil (kaesve)
|
||||||
|
--
|
||||||
|
-- USAGE
|
||||||
|
--
|
||||||
|
-- "Load" a font file from a memory buffer (you have to keep the buffer loaded)
|
||||||
|
-- stbtt_InitFont()
|
||||||
|
-- stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections
|
||||||
|
--
|
||||||
|
-- Character advance/positioning
|
||||||
|
-- stbtt_GetFontVMetrics()
|
||||||
|
--
|
||||||
|
-- Starting with version 1.06, the rasterizer was replaced with a new,
|
||||||
|
-- faster and generally-more-precise rasterizer. The new rasterizer more
|
||||||
|
-- accurately measures pixel coverage for anti-aliasing, except in the case
|
||||||
|
-- where multiple shapes overlap, in which case it overestimates the AA pixel
|
||||||
|
-- coverage. Thus, anti-aliasing of intersecting shapes may look wrong.
|
||||||
|
-- (This port only implements the new rasterizer.)
|
||||||
|
--
|
||||||
|
-- ADDITIONAL DOCUMENTATION
|
||||||
|
--
|
||||||
|
-- Some important concepts to understand to use this library:
|
||||||
|
--
|
||||||
|
-- Codepoint
|
||||||
|
-- Characters are defined by unicode codepoints, e.g. 65 is
|
||||||
|
-- uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is
|
||||||
|
-- the hiragana for "ma".
|
||||||
|
--
|
||||||
|
-- Glyph
|
||||||
|
-- A visual character shape (every codepoint is rendered as
|
||||||
|
-- some glyph)
|
||||||
|
--
|
||||||
|
-- Glyph index
|
||||||
|
-- A font-specific integer ID representing a glyph
|
||||||
|
--
|
||||||
|
-- Baseline
|
||||||
|
-- Glyph shapes are defined relative to a baseline, which is the
|
||||||
|
-- bottom of uppercase characters. Characters extend both above
|
||||||
|
-- and below the baseline.
|
||||||
|
--
|
||||||
|
-- Current Point
|
||||||
|
-- As you draw text to the screen, you keep track of a "current point"
|
||||||
|
-- which is the origin of each character. The current point's vertical
|
||||||
|
-- position is the baseline. Even "baked fonts" use this model.
|
||||||
|
--
|
||||||
|
-- Vertical Font Metrics
|
||||||
|
-- The vertical qualities of the font, used to vertically position
|
||||||
|
-- and space the characters. See docs for stbtt_GetFontVMetrics.
|
||||||
|
--
|
||||||
|
-- Font Size in Pixels or Points
|
||||||
|
-- The preferred interface for specifying font sizes in stb_truetype
|
||||||
|
-- is to specify how tall the font's vertical extent should be in pixels.
|
||||||
|
--
|
||||||
|
-- DETAILED USAGE:
|
||||||
|
--
|
||||||
|
-- Scale:
|
||||||
|
-- Select how high you want the font to be, in pixels.
|
||||||
|
-- Call ScaleForPixelHeight to compute a scale factor SF that will be
|
||||||
|
-- used by all other functions.
|
||||||
|
--
|
||||||
|
-- Current point:
|
||||||
|
-- Set the current point where the first character will appear. The
|
||||||
|
-- first character could extend left of the current point; this is font
|
||||||
|
-- dependent. You can either choose a current point that is the leftmost
|
||||||
|
-- point and hope, or add some padding, or check the bounding box or
|
||||||
|
-- left-side-bearing of the first character to be displayed and set
|
||||||
|
-- the current point based on that.
|
||||||
|
--
|
||||||
|
-- Displaying a character:
|
||||||
|
-- Compute the bounding box of the character. It will contain signed values
|
||||||
|
-- relative to <current_point, baseline>. I.e. if it returns x0,y0,x1,y1,
|
||||||
|
-- then the character should be displayed in the rectangle from
|
||||||
|
-- <current_point+SF*x0, baseline+SF*y0> to <current_point+SF*x1,baseline+SF*y1).
|
||||||
|
--
|
||||||
|
-- Advancing for the next character:
|
||||||
|
-- Call GlyphHMetrics, and compute 'current_point += SF * advance'.
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- ADVANCED USAGE
|
||||||
|
--
|
||||||
|
-- Quality:
|
||||||
|
--
|
||||||
|
-- - Use the functions with Subpixel at the end to allow your characters
|
||||||
|
-- to have subpixel positioning. Since the font is anti-aliased, not
|
||||||
|
-- hinted, this is very import for quality. (This is not possible with
|
||||||
|
-- baked fonts.)
|
||||||
|
--
|
||||||
|
-- Performance:
|
||||||
|
--
|
||||||
|
-- - Convert Unicode codepoints to glyph indexes and operate on the glyphs;
|
||||||
|
-- if you don't do this, stb_truetype is forced to do the conversion on
|
||||||
|
-- every call.
|
||||||
|
--
|
||||||
|
-- NOTES
|
||||||
|
--
|
||||||
|
-- The system uses the raw data found in the .ttf file without changing it
|
||||||
|
-- and without building auxiliary data structures. This is a bit inefficient
|
||||||
|
-- on little-endian systems (the data is big-endian), but assuming you're
|
||||||
|
-- caching the bitmaps or glyph shapes this shouldn't be a big deal.
|
||||||
|
|
||||||
--- STBTT_RASTERIZER_VERSION == 2
|
--- STBTT_RASTERIZER_VERSION == 2
|
||||||
|
|
||||||
local stbtt_fontinfo
|
local stbtt_fontinfo
|
||||||
@@ -21,7 +179,7 @@ local stbtt_FindGlyphIndex
|
|||||||
local STBTT_MAX_OVERSAMPLE = 8
|
local STBTT_MAX_OVERSAMPLE = 8
|
||||||
|
|
||||||
--- @enum STBTT_PLATFORM_ID
|
--- @enum STBTT_PLATFORM_ID
|
||||||
local STBTT_PLATFORM_ID = {
|
local STBTT_PLATFORM_ID = { -- platformID
|
||||||
UNICODE = 0,
|
UNICODE = 0,
|
||||||
MAC = 1,
|
MAC = 1,
|
||||||
ISO = 2,
|
ISO = 2,
|
||||||
@@ -29,7 +187,7 @@ local STBTT_PLATFORM_ID = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
--- @enum STBTT_MS_EID
|
--- @enum STBTT_MS_EID
|
||||||
local STBTT_MS_EID = {
|
local STBTT_MS_EID = { -- encodingID for STBTT_PLATFORM_ID_MICROSOFT
|
||||||
SYMBOL = 0,
|
SYMBOL = 0,
|
||||||
UNICODE_BMP = 1,
|
UNICODE_BMP = 1,
|
||||||
SHIFTJIS = 2,
|
SHIFTJIS = 2,
|
||||||
@@ -75,6 +233,7 @@ local stbtt_int8 = ImS8
|
|||||||
local stbtt_uint8 = ImU8
|
local stbtt_uint8 = ImU8
|
||||||
local unsigned_char = stbtt_uint8
|
local unsigned_char = stbtt_uint8
|
||||||
|
|
||||||
|
-- private structure
|
||||||
--- @class stbtt__buf
|
--- @class stbtt__buf
|
||||||
--- @field data? table # 1-based byte table
|
--- @field data? table # 1-based byte table
|
||||||
--- @field offset int # Every access to `data` will be offset by this
|
--- @field offset int # Every access to `data` will be offset by this
|
||||||
@@ -93,6 +252,8 @@ local function stbtt__buf(initial_offset)
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- The following structure is defined publicly so you can declare one on
|
||||||
|
-- the stack or as a global or etc, but you should treat it as opaque.
|
||||||
--- @class stbtt_fontinfo
|
--- @class stbtt_fontinfo
|
||||||
--- @field userdata any
|
--- @field userdata any
|
||||||
--- @field data table # pointer to .ttf file
|
--- @field data table # pointer to .ttf file
|
||||||
@@ -283,6 +444,7 @@ local function stbtt__new_active(e, off_x, start_point)
|
|||||||
return z
|
return z
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- @TODO: don't expose this structure
|
||||||
--- @class stbtt__bitmap
|
--- @class stbtt__bitmap
|
||||||
--- @field w int
|
--- @field w int
|
||||||
--- @field h int
|
--- @field h int
|
||||||
@@ -496,7 +658,7 @@ end
|
|||||||
-------------------------------------
|
-------------------------------------
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
---
|
---
|
||||||
--- Accessors to parse data from file
|
--- accessors to parse data from file
|
||||||
---
|
---
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -535,14 +697,16 @@ local function stbtt_tag4(p, c0, c1, c2, c3, offset) offset = offset or 0; retur
|
|||||||
local function stbtt_tag(p, str, offset) local c0, c1, c2, c3 = string.byte(str, 1, 4); return stbtt_tag4(p, c0, c1, c2, c3, offset) end
|
local function stbtt_tag(p, str, offset) local c0, c1, c2, c3 = string.byte(str, 1, 4); return stbtt_tag4(p, c0, c1, c2, c3, offset) end
|
||||||
|
|
||||||
local function stbtt__isfont(font)
|
local function stbtt__isfont(font)
|
||||||
if stbtt_tag4(font, 49, 0, 0, 0) then return true end
|
-- check the version number
|
||||||
if stbtt_tag(font, "typ1") then return true end
|
if stbtt_tag4(font, 49, 0, 0, 0) then return true end -- TrueType 1
|
||||||
if stbtt_tag(font, "OTTO") then return true end
|
if stbtt_tag(font, "typ1") then return true end -- TrueType with type 1 font -- we don't support this!
|
||||||
if stbtt_tag4(font, 0, 1, 0, 0) then return true end
|
if stbtt_tag(font, "OTTO") then return true end -- OpenType with CFF
|
||||||
if stbtt_tag(font, "true") then return true end
|
if stbtt_tag4(font, 0, 1, 0, 0) then return true end -- OpenType 1.0
|
||||||
|
if stbtt_tag(font, "true") then return true end -- Apple specification for TrueType fonts
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- @OPTIMIZE: binary search
|
||||||
local function stbtt__find_table(data, font_start, tag)
|
local function stbtt__find_table(data, font_start, tag)
|
||||||
local num_tables = ttUSHORT(data, font_start + 4)
|
local num_tables = ttUSHORT(data, font_start + 4)
|
||||||
local tabledir = font_start + 12
|
local tabledir = font_start + 12
|
||||||
@@ -556,11 +720,14 @@ local function stbtt__find_table(data, font_start, tag)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function stbtt_GetFontOffsetForIndex_internal(font_collection, index)
|
local function stbtt_GetFontOffsetForIndex_internal(font_collection, index)
|
||||||
|
-- if it's just a font, there's only one valid index
|
||||||
if stbtt__isfont(font_collection) then
|
if stbtt__isfont(font_collection) then
|
||||||
if index == 0 then return 0 else return -1 end
|
if index == 0 then return 0 else return -1 end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- check if it's a TTC
|
||||||
if stbtt_tag(font_collection, "ttcf") then
|
if stbtt_tag(font_collection, "ttcf") then
|
||||||
|
-- version 1?
|
||||||
if ttULONG(font_collection, 4) == 0x00010000 or ttULONG(font_collection, 4) == 0x00020000 then
|
if ttULONG(font_collection, 4) == 0x00010000 or ttULONG(font_collection, 4) == 0x00020000 then
|
||||||
local n = ttLONG(font_collection, 8)
|
local n = ttLONG(font_collection, 8)
|
||||||
if index >= n then
|
if index >= n then
|
||||||
@@ -598,23 +765,25 @@ local function stbtt_InitFont_internal(info, data, fontstart)
|
|||||||
info.fontstart = fontstart
|
info.fontstart = fontstart
|
||||||
info.cff = stbtt__new_buf(nil, 0)
|
info.cff = stbtt__new_buf(nil, 0)
|
||||||
|
|
||||||
cmap = stbtt__find_table(data, fontstart, "cmap")
|
cmap = stbtt__find_table(data, fontstart, "cmap") -- required
|
||||||
info.loca = stbtt__find_table(data, fontstart, "loca")
|
info.loca = stbtt__find_table(data, fontstart, "loca") -- required
|
||||||
info.head = stbtt__find_table(data, fontstart, "head")
|
info.head = stbtt__find_table(data, fontstart, "head") -- required
|
||||||
info.glyf = stbtt__find_table(data, fontstart, "glyf")
|
info.glyf = stbtt__find_table(data, fontstart, "glyf") -- required
|
||||||
info.hhea = stbtt__find_table(data, fontstart, "hhea")
|
info.hhea = stbtt__find_table(data, fontstart, "hhea") -- required
|
||||||
info.hmtx = stbtt__find_table(data, fontstart, "hmtx")
|
info.hmtx = stbtt__find_table(data, fontstart, "hmtx") -- required
|
||||||
info.kern = stbtt__find_table(data, fontstart, "kern")
|
info.kern = stbtt__find_table(data, fontstart, "kern") -- not required
|
||||||
info.gpos = stbtt__find_table(data, fontstart, "GPOS")
|
info.gpos = stbtt__find_table(data, fontstart, "GPOS") -- not required
|
||||||
|
|
||||||
if (cmap == 0 or info.head == 0 or info.hhea == 0 or info.hmtx == 0) then
|
if (cmap == 0 or info.head == 0 or info.hhea == 0 or info.hmtx == 0) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
if info.glyf ~= 0 then
|
if info.glyf ~= 0 then
|
||||||
|
-- required for truetype
|
||||||
if info.loca == 0 then
|
if info.loca == 0 then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
-- initialization for CFF / Type2 fonts (OTF)
|
||||||
local cff = stbtt__find_table(data, fontstart, "CFF ")
|
local cff = stbtt__find_table(data, fontstart, "CFF ")
|
||||||
if cff == 0 then
|
if cff == 0 then
|
||||||
return false
|
return false
|
||||||
@@ -623,14 +792,16 @@ local function stbtt_InitFont_internal(info, data, fontstart)
|
|||||||
info.fontdicts = stbtt__new_buf(nil, 0)
|
info.fontdicts = stbtt__new_buf(nil, 0)
|
||||||
info.fdselect = stbtt__new_buf(nil, 0)
|
info.fdselect = stbtt__new_buf(nil, 0)
|
||||||
|
|
||||||
info.cff = stbtt__new_buf(data, 64 * 1024 * 1024, cff) -- TODO: this should use size from table
|
-- @TODO this should use size from table (not 512MB)
|
||||||
|
info.cff = stbtt__new_buf(data, 64 * 1024 * 1024, cff) -- PORT: 64MB here instead of 512MB
|
||||||
local b = info.cff
|
local b = info.cff
|
||||||
|
|
||||||
-- read the header
|
-- read the header
|
||||||
stbtt__buf_skip(b, 2)
|
stbtt__buf_skip(b, 2)
|
||||||
stbtt__buf_seek(b, stbtt__buf_get8(b))
|
stbtt__buf_seek(b, stbtt__buf_get8(b)) -- hdrsize
|
||||||
|
|
||||||
-- TODO: the name INDEX could list multiple fonts, but we just use the first one.
|
-- @TODO the name INDEX could list multiple fonts,
|
||||||
|
-- but we just use the first one.
|
||||||
stbtt__cff_get_index(b) -- name INDEX
|
stbtt__cff_get_index(b) -- name INDEX
|
||||||
local topdictidx = stbtt__cff_get_index(b)
|
local topdictidx = stbtt__cff_get_index(b)
|
||||||
local topdict = stbtt__cff_index_get(topdictidx, 0)
|
local topdict = stbtt__cff_index_get(topdictidx, 0)
|
||||||
@@ -643,6 +814,7 @@ local function stbtt_InitFont_internal(info, data, fontstart)
|
|||||||
local fdselectoff = stbtt__dict_get_ints(topdict, bit32.bor(0x100, 37), 1)
|
local fdselectoff = stbtt__dict_get_ints(topdict, bit32.bor(0x100, 37), 1)
|
||||||
info.subrs = stbtt__get_subrs(b, topdict)
|
info.subrs = stbtt__get_subrs(b, topdict)
|
||||||
|
|
||||||
|
-- we only support Type 2 charstrings
|
||||||
if cstype[1] ~= 2 then
|
if cstype[1] ~= 2 then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
@@ -651,6 +823,7 @@ local function stbtt_InitFont_internal(info, data, fontstart)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if fdarrayoff[1] ~= 0 then
|
if fdarrayoff[1] ~= 0 then
|
||||||
|
-- looks like a CID font
|
||||||
if fdselectoff[1] == 0 then
|
if fdselectoff[1] == 0 then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
@@ -673,19 +846,26 @@ local function stbtt_InitFont_internal(info, data, fontstart)
|
|||||||
|
|
||||||
info.svg = -1
|
info.svg = -1
|
||||||
|
|
||||||
|
-- find a cmap encoding table we understand *now* to avoid searching
|
||||||
|
-- later. (todo: could make this installable)
|
||||||
|
-- the same regardless of glyph.
|
||||||
numTables = ttUSHORT(data, cmap + 2)
|
numTables = ttUSHORT(data, cmap + 2)
|
||||||
info.index_map = 0
|
info.index_map = 0
|
||||||
for i = 0, numTables - 1 do
|
for i = 0, numTables - 1 do
|
||||||
local encoding_record = cmap + 4 + 8 * i
|
local encoding_record = cmap + 4 + 8 * i
|
||||||
|
-- find an encoding we understand:
|
||||||
local platform_id = ttUSHORT(data, encoding_record)
|
local platform_id = ttUSHORT(data, encoding_record)
|
||||||
|
|
||||||
if platform_id == STBTT_PLATFORM_ID.MICROSOFT then
|
if platform_id == STBTT_PLATFORM_ID.MICROSOFT then
|
||||||
local ms_eid = ttUSHORT(data, encoding_record + 2)
|
local ms_eid = ttUSHORT(data, encoding_record + 2)
|
||||||
|
|
||||||
if ms_eid == STBTT_MS_EID.UNICODE_BMP or ms_eid == STBTT_MS_EID.UNICODE_FULL then
|
if ms_eid == STBTT_MS_EID.UNICODE_BMP or ms_eid == STBTT_MS_EID.UNICODE_FULL then
|
||||||
|
-- MS/Unicode
|
||||||
info.index_map = cmap + ttULONG(data, encoding_record + 4)
|
info.index_map = cmap + ttULONG(data, encoding_record + 4)
|
||||||
end
|
end
|
||||||
elseif platform_id == STBTT_PLATFORM_ID.UNICODE then
|
elseif platform_id == STBTT_PLATFORM_ID.UNICODE then
|
||||||
|
-- Mac/iOS has these
|
||||||
|
-- all the encodingIDs are unicode, so we don't bother to check it
|
||||||
info.index_map = cmap + ttULONG(data, encoding_record + 4)
|
info.index_map = cmap + ttULONG(data, encoding_record + 4)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -697,6 +877,11 @@ local function stbtt_InitFont_internal(info, data, fontstart)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- If you're going to perform multiple operations on the same character
|
||||||
|
-- and you want a speed-up, call this function with the character you're
|
||||||
|
-- going to process, then use glyph-based functions instead of the
|
||||||
|
-- codepoint-based functions.
|
||||||
|
-- Returns 0 if the character codepoint is not defined in the font.
|
||||||
--- @param info stbtt_fontinfo
|
--- @param info stbtt_fontinfo
|
||||||
--- @param unicode_codepoint int
|
--- @param unicode_codepoint int
|
||||||
function stbtt_FindGlyphIndex(info, unicode_codepoint)
|
function stbtt_FindGlyphIndex(info, unicode_codepoint)
|
||||||
@@ -704,7 +889,7 @@ function stbtt_FindGlyphIndex(info, unicode_codepoint)
|
|||||||
local index_map = info.index_map
|
local index_map = info.index_map
|
||||||
|
|
||||||
local format = ttUSHORT(data, index_map + 0)
|
local format = ttUSHORT(data, index_map + 0)
|
||||||
if format == 0 then
|
if format == 0 then -- apple byte encoding
|
||||||
local bytes = ttUSHORT(data, index_map + 2)
|
local bytes = ttUSHORT(data, index_map + 2)
|
||||||
if unicode_codepoint < bytes - 6 then
|
if unicode_codepoint < bytes - 6 then
|
||||||
return ttBYTE(data, index_map + 6 + unicode_codepoint)
|
return ttBYTE(data, index_map + 6 + unicode_codepoint)
|
||||||
@@ -718,14 +903,15 @@ function stbtt_FindGlyphIndex(info, unicode_codepoint)
|
|||||||
end
|
end
|
||||||
return 0
|
return 0
|
||||||
elseif format == 2 then
|
elseif format == 2 then
|
||||||
STBTT_assert(false) -- TODO: high-byte mapping for japanese/chinese/korean
|
STBTT_assert(false) -- @TODO: high-byte mapping for japanese/chinese/korean
|
||||||
return 0
|
return 0
|
||||||
elseif format == 4 then
|
elseif format == 4 then -- standard mapping for windows fonts: binary search collection of ranges
|
||||||
local segcount = bit32.rshift(ttUSHORT(data, index_map + 6), 1)
|
local segcount = bit32.rshift(ttUSHORT(data, index_map + 6), 1)
|
||||||
local searchRange = bit32.rshift(ttUSHORT(data, index_map + 8), 1)
|
local searchRange = bit32.rshift(ttUSHORT(data, index_map + 8), 1)
|
||||||
local entrySelector = ttUSHORT(data, index_map + 10)
|
local entrySelector = ttUSHORT(data, index_map + 10)
|
||||||
local rangeShift = bit32.rshift(ttUSHORT(data, index_map + 12), 1)
|
local rangeShift = bit32.rshift(ttUSHORT(data, index_map + 12), 1)
|
||||||
|
|
||||||
|
-- do a binary search of the segments
|
||||||
local endCount = index_map + 14
|
local endCount = index_map + 14
|
||||||
local search = endCount
|
local search = endCount
|
||||||
|
|
||||||
@@ -733,10 +919,13 @@ function stbtt_FindGlyphIndex(info, unicode_codepoint)
|
|||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- they lie from endCount .. endCount + segCount
|
||||||
|
-- but searchRange is the nearest power of two, so...
|
||||||
if unicode_codepoint >= ttUSHORT(data, search + rangeShift * 2) then
|
if unicode_codepoint >= ttUSHORT(data, search + rangeShift * 2) then
|
||||||
search = search + rangeShift * 2
|
search = search + rangeShift * 2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- now decrement to bias correctly to find smallest
|
||||||
search = search - 2
|
search = search - 2
|
||||||
while entrySelector ~= 0 do
|
while entrySelector ~= 0 do
|
||||||
searchRange = bit32.rshift(searchRange, 1)
|
searchRange = bit32.rshift(searchRange, 1)
|
||||||
@@ -768,8 +957,9 @@ function stbtt_FindGlyphIndex(info, unicode_codepoint)
|
|||||||
local ngroups = ttULONG(data, index_map + 12)
|
local ngroups = ttULONG(data, index_map + 12)
|
||||||
local low = 0
|
local low = 0
|
||||||
local high = ngroups
|
local high = ngroups
|
||||||
|
-- Binary search the right group.
|
||||||
while low < high do
|
while low < high do
|
||||||
local mid = low + bit32.rshift(high - low, 1)
|
local mid = low + bit32.rshift(high - low, 1) -- rounds down, so low <= mid < high
|
||||||
local start_char = ttULONG(data, index_map + 16 + mid * 12)
|
local start_char = ttULONG(data, index_map + 16 + mid * 12)
|
||||||
local end_char = ttULONG(data, index_map + 16 + mid * 12 + 4)
|
local end_char = ttULONG(data, index_map + 16 + mid * 12 + 4)
|
||||||
if unicode_codepoint < start_char then
|
if unicode_codepoint < start_char then
|
||||||
@@ -785,10 +975,10 @@ function stbtt_FindGlyphIndex(info, unicode_codepoint)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return 0
|
return 0 -- not found
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TODO:
|
-- @TODO
|
||||||
STBTT_assert(false)
|
STBTT_assert(false)
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
@@ -810,8 +1000,8 @@ end
|
|||||||
local function stbtt__GetGlyfOffset(info, glyph_index)
|
local function stbtt__GetGlyfOffset(info, glyph_index)
|
||||||
STBTT_assert(info.cff.size == 0)
|
STBTT_assert(info.cff.size == 0)
|
||||||
|
|
||||||
if glyph_index >= info.numGlyphs then return -1 end
|
if glyph_index >= info.numGlyphs then return -1 end -- glyph index out of range
|
||||||
if info.indexToLocFormat >= 2 then return -1 end
|
if info.indexToLocFormat >= 2 then return -1 end -- unknown index->glyph map format
|
||||||
|
|
||||||
local g1, g2
|
local g1, g2
|
||||||
if info.indexToLocFormat == 0 then
|
if info.indexToLocFormat == 0 then
|
||||||
@@ -822,9 +1012,10 @@ local function stbtt__GetGlyfOffset(info, glyph_index)
|
|||||||
g2 = info.glyf + ttULONG(info.data, info.loca + glyph_index * 4 + 4)
|
g2 = info.glyf + ttULONG(info.data, info.loca + glyph_index * 4 + 4)
|
||||||
end
|
end
|
||||||
|
|
||||||
if g1 == g2 then return -1 else return g1 end
|
if g1 == g2 then return -1 else return g1 end -- if length is 0, return -1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Gets the bounding box of the visible part of the glyph, in unscaled coordinates
|
||||||
--- @return integer, integer?, integer?, integer?, integer?
|
--- @return integer, integer?, integer?, integer?, integer?
|
||||||
local function stbtt_GetGlyphBox(info, glyph_index)
|
local function stbtt_GetGlyphBox(info, glyph_index)
|
||||||
local n, x0, y0, x1, y1
|
local n, x0, y0, x1, y1
|
||||||
@@ -896,7 +1087,7 @@ local function stbtt__GetGlyphShapeTT(info, glyph_index)
|
|||||||
local points = (endPtsOfContours + numberOfContours * 2 + 2 + ins) + 1 -- 1-based idx
|
local points = (endPtsOfContours + numberOfContours * 2 + 2 + ins) + 1 -- 1-based idx
|
||||||
|
|
||||||
local n = 1 + ttUSHORT(data, endPtsOfContours + numberOfContours * 2 - 2)
|
local n = 1 + ttUSHORT(data, endPtsOfContours + numberOfContours * 2 - 2)
|
||||||
local m = n + 2 * numberOfContours
|
local m = n + 2 * numberOfContours -- a loose bound on how many vertices we might need
|
||||||
|
|
||||||
vertices = {}
|
vertices = {}
|
||||||
for i = 1, m do vertices[i] = stbtt_vertex() end
|
for i = 1, m do vertices[i] = stbtt_vertex() end
|
||||||
@@ -907,7 +1098,11 @@ local function stbtt__GetGlyphShapeTT(info, glyph_index)
|
|||||||
local next_move = 0
|
local next_move = 0
|
||||||
local flagcount = 0
|
local flagcount = 0
|
||||||
|
|
||||||
local off = m - n
|
-- in first pass, we load uninterpreted data into the allocated array
|
||||||
|
-- above, shifted to the end of the array so we won't overwrite it when
|
||||||
|
-- we create our final data starting from the front
|
||||||
|
|
||||||
|
local off = m - n -- starting offset for uninterpreted data, regardless of how m ends up being calculated
|
||||||
|
|
||||||
-- first load flags
|
-- first load flags
|
||||||
local flags = 0
|
local flags = 0
|
||||||
@@ -934,7 +1129,7 @@ local function stbtt__GetGlyphShapeTT(info, glyph_index)
|
|||||||
if bit32.band(flags, 2) ~= 0 then
|
if bit32.band(flags, 2) ~= 0 then
|
||||||
local dx = data[points]
|
local dx = data[points]
|
||||||
points = points + 1
|
points = points + 1
|
||||||
if bit32.band(flags, 16) ~= 0 then
|
if bit32.band(flags, 16) ~= 0 then -- ???
|
||||||
x = x + dx
|
x = x + dx
|
||||||
else
|
else
|
||||||
x = x - dx
|
x = x - dx
|
||||||
@@ -956,7 +1151,7 @@ local function stbtt__GetGlyphShapeTT(info, glyph_index)
|
|||||||
if bit32.band(flags, 4) ~= 0 then
|
if bit32.band(flags, 4) ~= 0 then
|
||||||
local dy = data[points]
|
local dy = data[points]
|
||||||
points = points + 1
|
points = points + 1
|
||||||
if bit32.band(flags, 32) ~= 0 then
|
if bit32.band(flags, 32) ~= 0 then -- ???
|
||||||
y = y + dy
|
y = y + dy
|
||||||
else
|
else
|
||||||
y = y - dy
|
y = y - dy
|
||||||
@@ -987,15 +1182,19 @@ local function stbtt__GetGlyphShapeTT(info, glyph_index)
|
|||||||
-- now start the new one
|
-- now start the new one
|
||||||
start_off = (bit32.band(flags, 1) == 0)
|
start_off = (bit32.band(flags, 1) == 0)
|
||||||
if start_off then
|
if start_off then
|
||||||
|
-- if we start off with an off-curve point, then when we need to find a point on the curve
|
||||||
|
-- where we can start, and we need to save some state for when we wraparound.
|
||||||
scx = x
|
scx = x
|
||||||
scy = y
|
scy = y
|
||||||
if bit32.band(vertices[off + i + 1].type, 1) == 0 then
|
if bit32.band(vertices[off + i + 1].type, 1) == 0 then
|
||||||
|
-- next point is also a curve point, so interpolate an on-point curve
|
||||||
sx = bit32.rshift(x + stbtt_int32(vertices[off + i + 1].x), 1)
|
sx = bit32.rshift(x + stbtt_int32(vertices[off + i + 1].x), 1)
|
||||||
sy = bit32.rshift(y + stbtt_int32(vertices[off + i + 1].y), 1)
|
sy = bit32.rshift(y + stbtt_int32(vertices[off + i + 1].y), 1)
|
||||||
else
|
else
|
||||||
|
-- otherwise just use the next point as our start point
|
||||||
sx = stbtt_int32(vertices[off + i + 1].x)
|
sx = stbtt_int32(vertices[off + i + 1].x)
|
||||||
sy = stbtt_int32(vertices[off + i + 1].y)
|
sy = stbtt_int32(vertices[off + i + 1].y)
|
||||||
i = i + 1
|
i = i + 1 -- we're using point i+1 as the starting point, so skip it
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
sx = x
|
sx = x
|
||||||
@@ -1007,8 +1206,8 @@ local function stbtt__GetGlyphShapeTT(info, glyph_index)
|
|||||||
next_move = 1 + ttUSHORT(data, endPtsOfContours + j * 2)
|
next_move = 1 + ttUSHORT(data, endPtsOfContours + j * 2)
|
||||||
j = j + 1
|
j = j + 1
|
||||||
else
|
else
|
||||||
if bit32.band(flags, 1) == 0 then
|
if bit32.band(flags, 1) == 0 then -- if it's a curve
|
||||||
if was_off then
|
if was_off then -- two off-curve control points in a row means interpolate an on-curve midpoint
|
||||||
stbtt_setvertex(vertices[num_vertices + 1], STBTT_VTX.CURVE, bit32.rshift(cx + x, 1), bit32.rshift(cy + y, 1), cx, cy)
|
stbtt_setvertex(vertices[num_vertices + 1], STBTT_VTX.CURVE, bit32.rshift(cx + x, 1), bit32.rshift(cy + y, 1), cx, cy)
|
||||||
num_vertices = num_vertices + 1
|
num_vertices = num_vertices + 1
|
||||||
end
|
end
|
||||||
@@ -1032,6 +1231,7 @@ local function stbtt__GetGlyphShapeTT(info, glyph_index)
|
|||||||
|
|
||||||
num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx, sy, scx, scy, cx, cy)
|
num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx, sy, scx, scy, cx, cy)
|
||||||
elseif numberOfContours < 0 then
|
elseif numberOfContours < 0 then
|
||||||
|
-- Compound shapes.
|
||||||
local more = 1
|
local more = 1
|
||||||
local comp = g + 10
|
local comp = g + 10
|
||||||
num_vertices = 0
|
num_vertices = 0
|
||||||
@@ -1047,8 +1247,8 @@ local function stbtt__GetGlyphShapeTT(info, glyph_index)
|
|||||||
flags = ttSHORT(data, comp) comp = comp + 2
|
flags = ttSHORT(data, comp) comp = comp + 2
|
||||||
gidx = ttSHORT(data, comp) comp = comp + 2
|
gidx = ttSHORT(data, comp) comp = comp + 2
|
||||||
|
|
||||||
if bit32.band(flags, 2) ~= 0 then
|
if bit32.band(flags, 2) ~= 0 then -- XY values
|
||||||
if bit32.band(flags, 1) ~= 0 then
|
if bit32.band(flags, 1) ~= 0 then -- shorts
|
||||||
mtx[5] = ttSHORT(data, comp) comp = comp + 2
|
mtx[5] = ttSHORT(data, comp) comp = comp + 2
|
||||||
mtx[6] = ttSHORT(data, comp) comp = comp + 2
|
mtx[6] = ttSHORT(data, comp) comp = comp + 2
|
||||||
else
|
else
|
||||||
@@ -1056,7 +1256,7 @@ local function stbtt__GetGlyphShapeTT(info, glyph_index)
|
|||||||
mtx[6] = ttCHAR(data, comp) comp = comp + 1
|
mtx[6] = ttCHAR(data, comp) comp = comp + 1
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- TODO: handle matching point
|
-- @TODO handle matching point
|
||||||
STBTT_assert(false)
|
STBTT_assert(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1083,11 +1283,14 @@ local function stbtt__GetGlyphShapeTT(info, glyph_index)
|
|||||||
mtx[4] = ttSHORT(data, comp) / 16384.0 comp = comp + 2
|
mtx[4] = ttSHORT(data, comp) / 16384.0 comp = comp + 2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Find transformation scales.
|
||||||
m = STBTT_sqrt(mtx[1] * mtx[1] + mtx[2] * mtx[2])
|
m = STBTT_sqrt(mtx[1] * mtx[1] + mtx[2] * mtx[2])
|
||||||
n = STBTT_sqrt(mtx[3] * mtx[3] + mtx[4] * mtx[4])
|
n = STBTT_sqrt(mtx[3] * mtx[3] + mtx[4] * mtx[4])
|
||||||
|
|
||||||
|
-- Get indexed glyph.
|
||||||
comp_num_vertices, comp_verts = stbtt_GetGlyphShape(info, gidx)
|
comp_num_vertices, comp_verts = stbtt_GetGlyphShape(info, gidx)
|
||||||
if comp_num_vertices > 0 then
|
if comp_num_vertices > 0 then
|
||||||
|
-- Transform vertices.
|
||||||
for i = 1, comp_num_vertices do
|
for i = 1, comp_num_vertices do
|
||||||
local v = comp_verts[i]
|
local v = comp_verts[i]
|
||||||
local x = v.x
|
local x = v.x
|
||||||
@@ -1100,6 +1303,7 @@ local function stbtt__GetGlyphShapeTT(info, glyph_index)
|
|||||||
v.cy = stbtt_int16(n * (mtx[2] * x + mtx[4] * y + mtx[6]))
|
v.cy = stbtt_int16(n * (mtx[2] * x + mtx[4] * y + mtx[6]))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Append vertices.
|
||||||
if num_vertices == 0 then -- LUA: No malloc
|
if num_vertices == 0 then -- LUA: No malloc
|
||||||
vertices = comp_verts
|
vertices = comp_verts
|
||||||
else
|
else
|
||||||
@@ -1111,6 +1315,7 @@ local function stbtt__GetGlyphShapeTT(info, glyph_index)
|
|||||||
num_vertices = num_vertices + comp_num_vertices
|
num_vertices = num_vertices + comp_num_vertices
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- More components ?
|
||||||
more = bit32.band(flags, bit32.lshift(1, 5))
|
more = bit32.band(flags, bit32.lshift(1, 5))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1266,7 +1471,7 @@ local function stbtt__run_charstring(info, glyph_index, c)
|
|||||||
clear_stack = 1
|
clear_stack = 1
|
||||||
b0 = stbtt__buf_get8(b)
|
b0 = stbtt__buf_get8(b)
|
||||||
|
|
||||||
-- TODO: implement hinting
|
-- @TODO implement hinting
|
||||||
if b0 == 0x13 or b0 == 0x14 then -- hintmask or cntrmask
|
if b0 == 0x13 or b0 == 0x14 then -- hintmask or cntrmask
|
||||||
if in_header then
|
if in_header then
|
||||||
maskbits = maskbits + trunc(sp / 2) -- implicit "vstem"
|
maskbits = maskbits + trunc(sp / 2) -- implicit "vstem"
|
||||||
@@ -1415,6 +1620,8 @@ local function stbtt__run_charstring(info, glyph_index, c)
|
|||||||
elseif b0 == 0x0C then -- two-byte escape
|
elseif b0 == 0x0C then -- two-byte escape
|
||||||
local b1 = stbtt__buf_get8(b)
|
local b1 = stbtt__buf_get8(b)
|
||||||
|
|
||||||
|
-- @TODO These "flex" implementations ignore the flex-depth and resolution,
|
||||||
|
-- and always draw beziers.
|
||||||
if b1 == 0x22 then -- hflex
|
if b1 == 0x22 then -- hflex
|
||||||
if sp < 7 then return STBTT__CSERR("hflex stack") end
|
if sp < 7 then return STBTT__CSERR("hflex stack") end
|
||||||
local dx1 = s[0]
|
local dx1 = s[0]
|
||||||
@@ -1547,6 +1754,15 @@ function stbtt__GetGlyphInfoT2(info, glyph_index)
|
|||||||
return ((r ~= 0) and c.num_vertices or 0), x0, y0, x1, y1
|
return ((r ~= 0) and c.num_vertices or 0), x0, y0, x1, y1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- returns # of vertices and fills *vertices with the pointer to them
|
||||||
|
-- these are expressed in "unscaled" coordinates
|
||||||
|
--
|
||||||
|
-- The shape is a series of contours. Each one starts with
|
||||||
|
-- a STBTT_moveto, then consists of a series of mixed
|
||||||
|
-- STBTT_lineto and STBTT_curveto segments. A lineto
|
||||||
|
-- draws a line from previous endpoint to its x,y; a curveto
|
||||||
|
-- draws a quadratic bezier from previous endpoint to
|
||||||
|
-- its x,y, using cx,cy as the bezier control point.
|
||||||
--- @param info stbtt_fontinfo
|
--- @param info stbtt_fontinfo
|
||||||
--- @param glyph_index int
|
--- @param glyph_index int
|
||||||
function stbtt_GetGlyphShape(info, glyph_index)
|
function stbtt_GetGlyphShape(info, glyph_index)
|
||||||
@@ -1557,6 +1773,9 @@ function stbtt_GetGlyphShape(info, glyph_index)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- leftSideBearing is the offset from the current horizontal position to the left edge of the character
|
||||||
|
-- advanceWidth is the offset from the current horizontal position to the next horizontal position
|
||||||
|
-- these are expressed in unscaled coordinates
|
||||||
--- @param info stbtt_fontinfo
|
--- @param info stbtt_fontinfo
|
||||||
--- @param glyph_index int
|
--- @param glyph_index int
|
||||||
function stbtt_GetGlyphHMetrics(info, glyph_index)
|
function stbtt_GetGlyphHMetrics(info, glyph_index)
|
||||||
@@ -1574,6 +1793,12 @@ function stbtt_GetGlyphHMetrics(info, glyph_index)
|
|||||||
return advanceWidth, leftSideBearing
|
return advanceWidth, leftSideBearing
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- ascent is the coordinate above the baseline the font extends; descent
|
||||||
|
-- is the coordinate below the baseline the font extends (i.e. it is typically negative)
|
||||||
|
-- lineGap is the spacing between one row's descent and the next row's ascent...
|
||||||
|
-- so you should advance the vertical position by "ascent - descent + lineGap"
|
||||||
|
-- these are expressed in unscaled coordinates, so you must multiply by
|
||||||
|
-- the scale factor for a given size
|
||||||
--- @param info stbtt_fontinfo
|
--- @param info stbtt_fontinfo
|
||||||
function stbtt_GetFontVMetrics(info)
|
function stbtt_GetFontVMetrics(info)
|
||||||
local ascent = ttSHORT(info.data, info.hhea + 4)
|
local ascent = ttSHORT(info.data, info.hhea + 4)
|
||||||
@@ -1583,6 +1808,12 @@ function stbtt_GetFontVMetrics(info)
|
|||||||
return ascent, descent, lineGap
|
return ascent, descent, lineGap
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- computes a scale factor to produce a font whose "height" is 'pixels' tall.
|
||||||
|
-- Height is measured as the distance from the highest ascender to the lowest
|
||||||
|
-- descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics
|
||||||
|
-- and computing:
|
||||||
|
-- scale = pixels / (ascent - descent)
|
||||||
|
-- so if you prefer to measure height by the ascent only, use a similar calculation.
|
||||||
--- @param info stbtt_fontinfo
|
--- @param info stbtt_fontinfo
|
||||||
--- @param height float
|
--- @param height float
|
||||||
function stbtt_ScaleForPixelHeight(info, height)
|
function stbtt_ScaleForPixelHeight(info, height)
|
||||||
@@ -1593,10 +1824,12 @@ end
|
|||||||
------------------------------------
|
------------------------------------
|
||||||
------------------------------------
|
------------------------------------
|
||||||
---
|
---
|
||||||
--- Antialiasing software rasterizer
|
--- antialiasing software rasterizer
|
||||||
---
|
---
|
||||||
---
|
---
|
||||||
|
|
||||||
|
-- same as stbtt_GetGlyphBitmapBox, but you can specify a subpixel
|
||||||
|
-- shift for the character
|
||||||
--- @param font stbtt_fontinfo
|
--- @param font stbtt_fontinfo
|
||||||
--- @param glyph int
|
--- @param glyph int
|
||||||
--- @param scale_x float
|
--- @param scale_x float
|
||||||
@@ -1624,6 +1857,11 @@ function stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y, shift_x,
|
|||||||
return ix0, iy0, ix1, iy1
|
return ix0, iy0, ix1, iy1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- get the bbox of the bitmap centered around the glyph origin; so the
|
||||||
|
-- bitmap width is ix1-ix0, height is iy1-iy0, and location to place
|
||||||
|
-- the bitmap top left is (leftSideBearing*scale,iy0).
|
||||||
|
-- (Note that the bitmap uses y-increases-down, but the shape uses
|
||||||
|
-- y-increases-up, so GlyphBitmapBox and GlyphBox are inverted.)
|
||||||
--- @param font stbtt_fontinfo
|
--- @param font stbtt_fontinfo
|
||||||
--- @param glyph int
|
--- @param glyph int
|
||||||
--- @param scale_x float
|
--- @param scale_x float
|
||||||
@@ -1646,6 +1884,8 @@ end
|
|||||||
--- stbtt_MakeGlyphBitmap
|
--- stbtt_MakeGlyphBitmap
|
||||||
--- stbtt_GetGlyphBitmapSubpixel
|
--- stbtt_GetGlyphBitmapSubpixel
|
||||||
|
|
||||||
|
-- the edge passed in here does not cross the vertical line at x or the vertical line at x+1
|
||||||
|
-- (i.e. it has already been clipped to those)
|
||||||
--- @param scanline float[]
|
--- @param scanline float[]
|
||||||
--- @param x int
|
--- @param x int
|
||||||
--- @param e stbtt__active_edge
|
--- @param e stbtt__active_edge
|
||||||
@@ -1687,7 +1927,7 @@ local function stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x1, y1)
|
|||||||
-- do nothing
|
-- do nothing
|
||||||
else
|
else
|
||||||
STBTT_assert(x0 >= x and x0 <= x + 1 and x1 >= x and x1 <= x + 1)
|
STBTT_assert(x0 >= x and x0 <= x + 1 and x1 >= x and x1 <= x + 1)
|
||||||
scanline[x] = scanline[x] + e.direction * (y1 - y0) * (1 - ((x0 - x) + (x1 - x)) / 2)
|
scanline[x] = scanline[x] + e.direction * (y1 - y0) * (1 - ((x0 - x) + (x1 - x)) / 2) -- coverage = 1 - average x position
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1725,6 +1965,7 @@ local function stbtt__fill_active_edges_new(scanline, scanline_fill, len, e, y_t
|
|||||||
|
|
||||||
while e do
|
while e do
|
||||||
-- brute force every pixel
|
-- brute force every pixel
|
||||||
|
|
||||||
-- compute intersection points with top & bottom
|
-- compute intersection points with top & bottom
|
||||||
STBTT_assert(e.ey >= y_top)
|
STBTT_assert(e.ey >= y_top)
|
||||||
|
|
||||||
@@ -1768,8 +2009,8 @@ local function stbtt__fill_active_edges_new(scanline, scanline_fill, len, e, y_t
|
|||||||
if x_top >= 0 and x_bottom >= 0 and x_top < len and x_bottom < len then
|
if x_top >= 0 and x_bottom >= 0 and x_top < len and x_bottom < len then
|
||||||
-- from here on, we don't have to range check x values
|
-- from here on, we don't have to range check x values
|
||||||
if trunc(x_top) == trunc(x_bottom) then
|
if trunc(x_top) == trunc(x_bottom) then
|
||||||
-- simple case, only spans one pixel
|
|
||||||
local height
|
local height
|
||||||
|
-- simple case, only spans one pixel
|
||||||
local x = trunc(x_top)
|
local x = trunc(x_top)
|
||||||
height = (sy1 - sy0) * e.direction
|
height = (sy1 - sy0) * e.direction
|
||||||
STBTT_assert(x >= 0 and x < len)
|
STBTT_assert(x >= 0 and x < len)
|
||||||
@@ -1801,8 +2042,25 @@ local function stbtt__fill_active_edges_new(scanline, scanline_fill, len, e, y_t
|
|||||||
-- compute intersection with y axis at x2
|
-- compute intersection with y axis at x2
|
||||||
y_final = y_top + dy * (x2 - x0)
|
y_final = y_top + dy * (x2 - x0)
|
||||||
|
|
||||||
|
-- x1 x_top x2 x_bottom
|
||||||
|
-- y_top +------|-----+------------+------------+--------|---+------------+
|
||||||
|
-- | | | | | |
|
||||||
|
-- | | | | | |
|
||||||
|
-- sy0 | Txxxxx|............|............|............|............|
|
||||||
|
-- y_crossing | *xxxxx.......|............|............|............|
|
||||||
|
-- | | xxxxx..|............|............|............|
|
||||||
|
-- | | /- xx*xxxx........|............|............|
|
||||||
|
-- | | dy < | xxxxxx..|............|............|
|
||||||
|
-- y_final | | \- | xx*xxx.........|............|
|
||||||
|
-- sy1 | | | | xxxxxB...|............|
|
||||||
|
-- | | | | | |
|
||||||
|
-- | | | | | |
|
||||||
|
-- y_bottom +------------+------------+------------+------------+------------+
|
||||||
|
--
|
||||||
|
-- goal is to measure the area covered by '.' in each pixel
|
||||||
|
|
||||||
-- if x2 is right at the right edge of x1, y_crossing can blow up, github #1057
|
-- if x2 is right at the right edge of x1, y_crossing can blow up, github #1057
|
||||||
-- TODO: maybe test against sy1 rather than y_bottom?
|
-- @TODO: maybe test against sy1 rather than y_bottom?
|
||||||
if y_crossing > y_bottom then
|
if y_crossing > y_bottom then
|
||||||
y_crossing = y_bottom
|
y_crossing = y_bottom
|
||||||
end
|
end
|
||||||
@@ -1824,6 +2082,16 @@ local function stbtt__fill_active_edges_new(scanline, scanline_fill, len, e, y_t
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- in second pixel, area covered by line segment found in first pixel
|
||||||
|
-- is always a rectangle 1 wide * the height of that line segment; this
|
||||||
|
-- is exactly what the variable 'area' stores. it also gets a contribution
|
||||||
|
-- from the line segment within it. the THIRD pixel will get the first
|
||||||
|
-- pixel's rectangle contribution, the second pixel's rectangle contribution,
|
||||||
|
-- and its own contribution. the 'own contribution' is the same in every pixel except
|
||||||
|
-- the leftmost and rightmost, a trapezoid that slides down in each pixel.
|
||||||
|
-- the second pixel's contribution to the third pixel will be the
|
||||||
|
-- rectangle 1 wide times the height change in the second pixel, which is dy.
|
||||||
|
|
||||||
step = sign * dy * 1 -- dy is dy/dx, change in y for every 1 change in x,
|
step = sign * dy * 1 -- dy is dy/dx, change in y for every 1 change in x,
|
||||||
-- which multiplied by 1-pixel-width is how much pixel area changes for each step in x
|
-- which multiplied by 1-pixel-width is how much pixel area changes for each step in x
|
||||||
-- so the area advances by 'step' every time
|
-- so the area advances by 'step' every time
|
||||||
@@ -1843,7 +2111,27 @@ local function stbtt__fill_active_edges_new(scanline, scanline_fill, len, e, y_t
|
|||||||
scanline_fill[x2 + 1] = scanline_fill[x2 + 1] + sign * (sy1 - sy0)
|
scanline_fill[x2 + 1] = scanline_fill[x2 + 1] + sign * (sy1 - sy0)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
-- if edge goes outside of box we're drawing, we require
|
||||||
|
-- clipping logic. since this does not match the intended use
|
||||||
|
-- of this library, we use a different, very slow brute
|
||||||
|
-- force implementation
|
||||||
|
-- note though that this does happen some of the time because
|
||||||
|
-- x_top and x_bottom can be extrapolated at the top & bottom of
|
||||||
|
-- the shape and actually lie outside the bounding box
|
||||||
for x = 0, len - 1 do
|
for x = 0, len - 1 do
|
||||||
|
-- cases:
|
||||||
|
--
|
||||||
|
-- there can be up to two intersections with the pixel. any intersection
|
||||||
|
-- with left or right edges can be handled by splitting into two (or three)
|
||||||
|
-- regions. intersections with top & bottom do not necessitate case-wise logic.
|
||||||
|
--
|
||||||
|
-- the old way of doing this found the intersections with the left & right edges,
|
||||||
|
-- then used some simple logic to produce up to three segments in sorted order
|
||||||
|
-- from top-to-bottom. however, this had a problem: if an x edge was epsilon
|
||||||
|
-- across the x border, then the corresponding y position might not be distinct
|
||||||
|
-- from the other y segment, and it might ignored as an empty segment. to avoid
|
||||||
|
-- that, we need to explicitly produce segments based on x positions.
|
||||||
|
|
||||||
-- rename variables to clearly-defined pairs
|
-- rename variables to clearly-defined pairs
|
||||||
local y0 = y_top
|
local y0 = y_top
|
||||||
local x1 = x
|
local x1 = x
|
||||||
@@ -1887,6 +2175,7 @@ local function stbtt__fill_active_edges_new(scanline, scanline_fill, len, e, y_t
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- directly AA rasterize edges w/o supersampling
|
||||||
--- @param result stbtt__bitmap
|
--- @param result stbtt__bitmap
|
||||||
--- @param e stbtt__edge
|
--- @param e stbtt__edge
|
||||||
--- @param n int
|
--- @param n int
|
||||||
@@ -1914,28 +2203,33 @@ local function stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, of
|
|||||||
for i = 0, result.w - 1 do scanline[i] = 0 end
|
for i = 0, result.w - 1 do scanline[i] = 0 end
|
||||||
for i = 0, result.w do scanline2[i] = 0 end
|
for i = 0, result.w do scanline2[i] = 0 end
|
||||||
|
|
||||||
|
-- update all active edges;
|
||||||
|
-- remove all active edges that terminate before the top of this scanline
|
||||||
local prev = nil
|
local prev = nil
|
||||||
local curr = active
|
local curr = active
|
||||||
while curr do
|
while curr do
|
||||||
local next = curr.next
|
local next = curr.next
|
||||||
if curr.ey <= scan_y_top then
|
if curr.ey <= scan_y_top then
|
||||||
if prev then prev.next = next
|
if prev then prev.next = next -- delete from list
|
||||||
else active = next end
|
else active = next end
|
||||||
curr.direction = 0
|
curr.direction = 0
|
||||||
else
|
else
|
||||||
prev = curr
|
prev = curr -- advance through list
|
||||||
end
|
end
|
||||||
curr = next
|
curr = next
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- insert all edges that start before the bottom of this scanline
|
||||||
local edge = e[p]
|
local edge = e[p]
|
||||||
while edge.y0 <= scan_y_bottom do
|
while edge.y0 <= scan_y_bottom do
|
||||||
if edge.y0 ~= edge.y1 then
|
if edge.y0 ~= edge.y1 then
|
||||||
local z = stbtt__new_active(edge, off_x, scan_y_top)
|
local z = stbtt__new_active(edge, off_x, scan_y_top)
|
||||||
if j == 0 and off_y ~= 0 and z.ey < scan_y_top then
|
if j == 0 and off_y ~= 0 and z.ey < scan_y_top then
|
||||||
|
-- this can happen due to subpixel positioning and some kind of fp rounding error i think
|
||||||
z.ey = scan_y_top
|
z.ey = scan_y_top
|
||||||
end
|
end
|
||||||
STBTT_assert(z.ey >= scan_y_top)
|
STBTT_assert(z.ey >= scan_y_top) -- if we get really unlucky a tiny bit of an edge can be out of bounds
|
||||||
|
-- insert at front
|
||||||
z.next = active
|
z.next = active
|
||||||
active = z
|
active = z
|
||||||
end
|
end
|
||||||
@@ -1943,6 +2237,7 @@ local function stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, of
|
|||||||
edge = e[p]
|
edge = e[p]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- now process all active edges
|
||||||
if active then
|
if active then
|
||||||
stbtt__fill_active_edges_new(scanline, scanline2, result.w, active, scan_y_top)
|
stbtt__fill_active_edges_new(scanline, scanline2, result.w, active, scan_y_top)
|
||||||
end
|
end
|
||||||
@@ -1961,10 +2256,11 @@ local function stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, of
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- advance all the edges
|
||||||
step = active
|
step = active
|
||||||
while step do
|
while step do
|
||||||
step.fx = step.fx + step.fdx
|
step.fx = step.fx + step.fdx -- advance to position for current scanline
|
||||||
step = step.next
|
step = step.next -- advance through list
|
||||||
end
|
end
|
||||||
|
|
||||||
y = y + 1
|
y = y + 1
|
||||||
@@ -1999,6 +2295,7 @@ end
|
|||||||
local function stbtt__rasterize(result, pts, wcount, windings, scale_x, scale_y, shift_x, shift_y, off_x, off_y, invert)
|
local function stbtt__rasterize(result, pts, wcount, windings, scale_x, scale_y, shift_x, shift_y, off_x, off_y, invert)
|
||||||
local y_scale_inv = (invert) and -scale_y or scale_y
|
local y_scale_inv = (invert) and -scale_y or scale_y
|
||||||
local vsubsample = 1 -- STBTT_RASTERIZER_VERSION == 2
|
local vsubsample = 1 -- STBTT_RASTERIZER_VERSION == 2
|
||||||
|
-- vsubsample should divide 255 evenly; otherwise we won't reach full opacity
|
||||||
|
|
||||||
-- now we have to blow out the windings into explicit edge lists
|
-- now we have to blow out the windings into explicit edge lists
|
||||||
local n = 0
|
local n = 0
|
||||||
@@ -2066,8 +2363,7 @@ local function stbtt__add_point(points, n, x, y)
|
|||||||
points[n + 1].y = y
|
points[n + 1].y = y
|
||||||
end
|
end
|
||||||
|
|
||||||
-- tessellate until threshold p is happy...
|
-- tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching
|
||||||
-- TODO: warped to compensate for non-linear stretching
|
|
||||||
--- @param points? stbtt__point[]
|
--- @param points? stbtt__point[]
|
||||||
--- @param num_points int
|
--- @param num_points int
|
||||||
--- @param x0 float
|
--- @param x0 float
|
||||||
@@ -2116,7 +2412,7 @@ end
|
|||||||
--- @param n int
|
--- @param n int
|
||||||
--- @return int num_points # updated num_points passed in
|
--- @return int num_points # updated num_points passed in
|
||||||
local function stbtt__tesselate_cubic(points, num_points, x0, y0, x1, y1, x2, y2, x3, y3, objspace_flatness_squared, n)
|
local function stbtt__tesselate_cubic(points, num_points, x0, y0, x1, y1, x2, y2, x3, y3, objspace_flatness_squared, n)
|
||||||
-- TODO: this "flatness" calculation is just made-up nonsense that seems to work well enough
|
-- @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough
|
||||||
local dx0 = x1 - x0
|
local dx0 = x1 - x0
|
||||||
local dy0 = y1 - y0
|
local dy0 = y1 - y0
|
||||||
local dx1 = x2 - x1
|
local dx1 = x2 - x1
|
||||||
@@ -2159,6 +2455,7 @@ local function stbtt__tesselate_cubic(points, num_points, x0, y0, x1, y1, x2, y2
|
|||||||
return num_points
|
return num_points
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- returns number of contours
|
||||||
--- @param vertices stbtt_vertex[]
|
--- @param vertices stbtt_vertex[]
|
||||||
--- @param num_verts int
|
--- @param num_verts int
|
||||||
--- @param objspace_flatness float
|
--- @param objspace_flatness float
|
||||||
@@ -2235,6 +2532,7 @@ local function stbtt_FlattenCurves(vertices, num_verts, objspace_flatness)
|
|||||||
return points, num_contours, contour_lengths
|
return points, num_contours, contour_lengths
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- rasterize a shape with quadratic beziers into a bitmap
|
||||||
--- @param result stbtt__bitmap
|
--- @param result stbtt__bitmap
|
||||||
--- @param flatness_in_pixels float
|
--- @param flatness_in_pixels float
|
||||||
--- @param vertices stbtt_vertex[]
|
--- @param vertices stbtt_vertex[]
|
||||||
@@ -2258,6 +2556,11 @@ local function stbtt_Rasterize(result, flatness_in_pixels, vertices, num_verts,
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- renders the glyph into storage you pass in for the bitmap,
|
||||||
|
-- in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap
|
||||||
|
-- is clipped to out_w/out_h bytes. Call stbtt_GetGlyphBitmapBox to get the
|
||||||
|
-- width and height and positioning info for it first. You can also specify
|
||||||
|
-- a subpixel shift for the character.
|
||||||
--- @param info stbtt_fontinfo
|
--- @param info stbtt_fontinfo
|
||||||
--- @param output unsigned_char[]
|
--- @param output unsigned_char[]
|
||||||
--- @param out_w int
|
--- @param out_w int
|
||||||
@@ -2286,7 +2589,7 @@ end
|
|||||||
-----------------
|
-----------------
|
||||||
-----------------
|
-----------------
|
||||||
---
|
---
|
||||||
--- Bitmap baking
|
--- bitmap baking
|
||||||
---
|
---
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -2298,7 +2601,7 @@ end
|
|||||||
---------------------------
|
---------------------------
|
||||||
---------------------------
|
---------------------------
|
||||||
---
|
---
|
||||||
--- Bitmap baking / packing
|
--- bitmap baking / packing
|
||||||
---
|
---
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -2380,9 +2683,16 @@ local function stbtt__oversample_shift(oversample)
|
|||||||
if oversample == 0 then
|
if oversample == 0 then
|
||||||
return 0.0
|
return 0.0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- The prefilter is a box filter of width "oversample",
|
||||||
|
-- which shifts phase by (oversample - 1)/2 pixels in
|
||||||
|
-- oversampled space. We want to shift in the opposite
|
||||||
|
-- direction to counter this.
|
||||||
return -(oversample - 1) / (2.0 * oversample)
|
return -(oversample - 1) / (2.0 * oversample)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- same as stbtt_MakeGlyphBitmapSubpixel, but prefiltering
|
||||||
|
-- is performed. Returns (sub_x, sub_y).
|
||||||
--- @param info stbtt_fontinfo
|
--- @param info stbtt_fontinfo
|
||||||
--- @param output unsigned_char[]
|
--- @param output unsigned_char[]
|
||||||
--- @param out_w int
|
--- @param out_w int
|
||||||
@@ -2415,7 +2725,7 @@ end
|
|||||||
-------------------
|
-------------------
|
||||||
-------------------
|
-------------------
|
||||||
---
|
---
|
||||||
--- SDF computation
|
--- sdf computation
|
||||||
---
|
---
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -2441,10 +2751,20 @@ end
|
|||||||
--- stbtt__matchpair
|
--- stbtt__matchpair
|
||||||
--- stbtt__matches
|
--- stbtt__matches
|
||||||
|
|
||||||
|
-- Each .ttf/.ttc file may have more than one font. Each font has a sequential
|
||||||
|
-- index number starting from 0. Call this function to get the font offset for
|
||||||
|
-- a given index; it returns -1 if the index is out of range. A regular .ttf
|
||||||
|
-- file will only define one font and it always be at offset 0, so it will
|
||||||
|
-- return '0' for index 0, and -1 for all other indices.
|
||||||
--- @param data table
|
--- @param data table
|
||||||
--- @param index int
|
--- @param index int
|
||||||
function stbtt_GetFontOffsetForIndex(data, index) return stbtt_GetFontOffsetForIndex_internal(data, index) end
|
function stbtt_GetFontOffsetForIndex(data, index) return stbtt_GetFontOffsetForIndex_internal(data, index) end
|
||||||
|
|
||||||
|
-- Given an offset into the file that defines a font, this function builds
|
||||||
|
-- the necessary cached info for the rest of the system. You must allocate
|
||||||
|
-- the stbtt_fontinfo yourself (see stbtt_fontinfo()), and stbtt_InitFont will fill it out. You don't
|
||||||
|
-- need to do anything special to free it, because the contents are pure
|
||||||
|
-- value data with no additional data structures. Returns false on failure.
|
||||||
--- @param info stbtt_fontinfo
|
--- @param info stbtt_fontinfo
|
||||||
--- @param data table
|
--- @param data table
|
||||||
--- @param offset int
|
--- @param offset int
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
# Why: ModuleScripts don't share globals, and the setfenv() alternative turns off Luau's fast paths / native codegen.
|
# 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.
|
# 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, ...).
|
# 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
|
import os, re
|
||||||
|
|
||||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
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")
|
open(os.path.join(OUT, NAME), "w").write("\n".join(out) + "\n")
|
||||||
import json
|
import json
|
||||||
json.dump(linemap, open(os.path.join(OUT, NAME.rsplit(".", 1)[0] + ".linemap.json"), "w"))
|
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)))
|
print("wrote build/%s (%d files, %d exports)" % (NAME, len(FILES), len(names)))
|
||||||
|
|||||||
@@ -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))
|
||||||
+107
@@ -0,0 +1,107 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# Minifies a Lua/Luau file: drops comments, indentation, blank lines and spaces that don't separate tokens.
|
||||||
|
# Kept: strings (verbatim), "--!" directive lines (--!optimize, --!native, ...), @native attributes, and one newline
|
||||||
|
# wherever the source had one (so no ambiguous-call issues, and error line numbers stay roughly meaningful).
|
||||||
|
# python3 tools/minify.py IN OUT # write minified IN to OUT
|
||||||
|
# python3 tools/minify.py --same-code F.. # check F.. have the same code tokens as `git show HEAD:F` (comment-only edits)
|
||||||
|
# python3 tools/minify.py --same-code-as OLD NEW # same check between two files
|
||||||
|
# python3 tools/minify.py --test # self-check
|
||||||
|
import re, subprocess, sys
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
TOKEN = re.compile(r"""
|
||||||
|
(?P<ws>[ \t\r\n\f\v]+)
|
||||||
|
| (?P<lcom>--\[(?P<ceq>=*)\[.*?\](?P=ceq)\])
|
||||||
|
| (?P<com>--[^\n]*)
|
||||||
|
| (?P<lstr>\[(?P<seq>=*)\[.*?\](?P=seq)\])
|
||||||
|
| (?P<str>"(?:\\.|\\\n|[^"\\\n])*"|'(?:\\.|\\\n|[^'\\\n])*'|`(?:\\.|\\\n|[^`\\])*`)
|
||||||
|
| (?P<num>0[xX][0-9a-fA-F_]*(?:\.[0-9a-fA-F_]*)?(?:[pP][+-]?\d+)?[uUlLi]*|0[bB][01_]+|(?:\d[\d_]*(?:\.[\d_]*)?|\.\d[\d_]*)(?:[eE][+-]?\d+)?[uUlLi]*)
|
||||||
|
| (?P<name>@?[A-Za-z_]\w*)
|
||||||
|
| (?P<op>\.\.\.|\.\.=|//=|\.\.|==|~=|<=|>=|//|::|->|[-+*/%^]=|.)
|
||||||
|
""", re.S | re.X)
|
||||||
|
|
||||||
|
def tokenize(src):
|
||||||
|
"""[(kind, text, newline_before)]; comments/whitespace folded into newline_before ("--!" lines kept as 'dir')."""
|
||||||
|
out, nl, pos = [], False, 0
|
||||||
|
while pos < len(src):
|
||||||
|
m = TOKEN.match(src, pos)
|
||||||
|
kind, text = m.lastgroup, m.group(0)
|
||||||
|
if kind in ("ceq", "seq"): kind = "lcom" if text.startswith("--") else "lstr"
|
||||||
|
pos = m.end()
|
||||||
|
if kind == "ws" or kind == "lcom" or (kind == "com" and not text.startswith("--!")):
|
||||||
|
nl = nl or "\n" in text
|
||||||
|
continue
|
||||||
|
if kind == "com": kind = "dir"
|
||||||
|
if kind == "lstr" or kind == "str": kind = "str"
|
||||||
|
out.append((kind, text, nl))
|
||||||
|
nl = False
|
||||||
|
return out
|
||||||
|
|
||||||
|
@lru_cache(maxsize=None)
|
||||||
|
def glue_ok(a, b):
|
||||||
|
"""True if `a..b` with no space still tokenizes as exactly a, b."""
|
||||||
|
if re.match(r"\w", a[-1]) and re.match(r"\w", b[0]): return False # real lexers read `0then`/`x1` as one token
|
||||||
|
return [t for _, t, _ in tokenize(a + b)] == [a, b]
|
||||||
|
|
||||||
|
def minify(src):
|
||||||
|
toks, out = tokenize(src), []
|
||||||
|
prev = None
|
||||||
|
for kind, text, nl in toks:
|
||||||
|
if prev is not None:
|
||||||
|
if nl or prev[0] == "dir" or kind == "dir": out.append("\n")
|
||||||
|
elif kind == "str" or prev[0] == "str": # strings are self-delimiting; only `[` + `[[` would fuse
|
||||||
|
if kind == "str" and text[0] == "[" and prev[1].endswith("["): out.append(" ")
|
||||||
|
elif not glue_ok(prev[1], text):
|
||||||
|
out.append(" ")
|
||||||
|
out.append(text)
|
||||||
|
prev = (kind, text)
|
||||||
|
return "".join(out) + "\n"
|
||||||
|
|
||||||
|
def code(src):
|
||||||
|
return [(k, t) for k, t, _ in tokenize(src)]
|
||||||
|
|
||||||
|
def test():
|
||||||
|
cases = {
|
||||||
|
"local a = 1 -- c\nlocal b = a - -1\n": "local a=1\nlocal b=a- -1\n",
|
||||||
|
"x = 1 .. 2": "x=1 ..2\n",
|
||||||
|
"x = t[ [[s]] ]": "x=t[ [[s]]]\n",
|
||||||
|
"x = t[ [==[s]==] ]": "x=t[ [==[s]==]]\n",
|
||||||
|
"--!optimize 2\n--[[ long\ncomment ]] local s = 'a -- b' .. \"c\\\"d\"": "--!optimize 2\nlocal s='a -- b'..\"c\\\"d\"\n",
|
||||||
|
"@native\nfunction f(...) return ... end": "@native\nfunction f(...)return...end\n",
|
||||||
|
"a = b == c and d ~= e or f <= g": "a=b==c and d~=e or f<=g\n",
|
||||||
|
"x = 0x10 + 1e-5 + .5": "x=0x10+1e-5+.5\n",
|
||||||
|
"f(a)\n\n\n(g)(b)": "f(a)\n(g)(b)\n",
|
||||||
|
"s = [[a\n-- not a comment\n]]": "s=[[a\n-- not a comment\n]]\n",
|
||||||
|
"x = a / / b": "x=a/ /b\n",
|
||||||
|
"x = a . . b": "x=a. .b\n",
|
||||||
|
"if a == 0 then return 1 end": "if a==0 then return 1 end\n",
|
||||||
|
"x = 1 .. y": "x=1 ..y\n",
|
||||||
|
}
|
||||||
|
for src, want in cases.items():
|
||||||
|
got = minify(src)
|
||||||
|
assert got == want, (src, got, want)
|
||||||
|
assert code(minify(src)) == code(src), src
|
||||||
|
print("minify OK")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if sys.argv[1] == "--test":
|
||||||
|
test()
|
||||||
|
elif sys.argv[1] in ("--same-code", "--same-code-as"):
|
||||||
|
bad = 0
|
||||||
|
pairs = [(sys.argv[2], sys.argv[3])] if sys.argv[1] == "--same-code-as" else [(None, f) for f in sys.argv[2:]]
|
||||||
|
for ref, f in pairs:
|
||||||
|
old = open(ref).read() if ref else subprocess.run(["git", "show", "HEAD:" + f], capture_output=True, text=True, check=True).stdout
|
||||||
|
a, b = code(old), code(open(f).read())
|
||||||
|
if a != b:
|
||||||
|
bad += 1
|
||||||
|
i = next((i for i, (x, y) in enumerate(zip(a, b)) if x != y), min(len(a), len(b)))
|
||||||
|
print("%s: CODE CHANGED at token %d: %r -> %r" % (f, i, a[i:i + 6], b[i:i + 6]))
|
||||||
|
else:
|
||||||
|
print("%s: same code" % f)
|
||||||
|
sys.exit(1 if bad else 0)
|
||||||
|
else:
|
||||||
|
src = open(sys.argv[1], encoding="utf-8").read()
|
||||||
|
dst = minify(src)
|
||||||
|
assert code(dst) == code(src), "minify changed code tokens"
|
||||||
|
open(sys.argv[2], "w", encoding="utf-8").write(dst)
|
||||||
|
print("wrote %s (%d -> %d bytes, %.0f%%)" % (sys.argv[2], len(src.encode()), len(dst.encode()), 100.0 * len(dst) / len(src)))
|
||||||
@@ -16,6 +16,8 @@ G = "game, workspace, Instance, Enum, Vector2, Vector3, UDim2, Color3, Content,
|
|||||||
|
|
||||||
def build_run(args, flags):
|
def build_run(args, flags):
|
||||||
subprocess.run([sys.executable, os.path.join(ROOT, "tools", "bundle.py"), TMP] + (["--no-demo"] if os.environ.get("NO_DEMO") else []), check=True, stdout=subprocess.DEVNULL)
|
subprocess.run([sys.executable, os.path.join(ROOT, "tools", "bundle.py"), TMP] + (["--no-demo"] if os.environ.get("NO_DEMO") else []), check=True, stdout=subprocess.DEVNULL)
|
||||||
|
if os.environ.get("MINIFY"): # test the minified bundle instead (error line numbers won't map back to sources)
|
||||||
|
subprocess.run([sys.executable, os.path.join(ROOT, "tools", "minify.py"), os.path.join(TMP, "ImGui.luau"), os.path.join(TMP, "ImGui.luau")], check=True, stdout=subprocess.DEVNULL)
|
||||||
out = ["--" if args.roblox else "--!native", "--!optimize 2", flags, "local SOURCE_FNS = {}"]
|
out = ["--" if args.roblox else "--!native", "--!optimize 2", flags, "local SOURCE_FNS = {}"]
|
||||||
def add(key, path):
|
def add(key, path):
|
||||||
s = "\n".join(l for l in open(path).read().split("\n") if not l.startswith("--!"))
|
s = "\n".join(l for l in open(path).read().split("\n") if not l.startswith("--!"))
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
-- Regressions vs upstream: clipboard paste filter (InputTextEx), ImParseFormatTrimDecorations (Ctrl+click temp input), ShrinkWidths tie order
|
||||||
|
local G = require(game:GetService("ReplicatedStorage"):WaitForChild("ImGui"))
|
||||||
|
local ImGui, ImVec2 = G.ImGui, G.ImVec2
|
||||||
|
ImGui.CreateContext(); G.ImGui_ImplRoblox.Init()
|
||||||
|
local io = ImGui.GetIO()
|
||||||
|
local failed = {}
|
||||||
|
local function check(ok, msg) if not ok then table.insert(failed, msg); print("FAIL " .. msg) end end
|
||||||
|
|
||||||
|
-- ShrinkWidths: equal widths sort by Index descending (upstream ShrinkWidthItemComparer), so the rounding remainder goes to the highest Index first
|
||||||
|
local items = {}
|
||||||
|
for i = 1, 4 do local it = G.ImGuiShrinkWidthItem(); it.Index = i - 1; it.Width = 100; it.InitialWidth = 100; items[i] = it end
|
||||||
|
ImGui.ShrinkWidths(items, 1, 4, 2.0, 10.0)
|
||||||
|
local w = {}
|
||||||
|
for _, it in items do w[it.Index] = it.Width end
|
||||||
|
check(w[3] == 100 and w[2] == 100 and w[1] == 99 and w[0] == 99, ("ShrinkWidths widths %s %s %s %s"):format(w[0], w[1], w[2], w[3]))
|
||||||
|
|
||||||
|
local function edit_text()
|
||||||
|
local st = ImGui.GetCurrentContext().InputTextState
|
||||||
|
return G.ImGui._ByteArrayToString(st.TextA.Data, 1, st.TextLen + 1)
|
||||||
|
end
|
||||||
|
local buf = ImGui.NewTextBuffer("", 64)
|
||||||
|
local f, sf, di = 0, 45.0, 50
|
||||||
|
local rects = {}
|
||||||
|
local function rect() local a, b = ImGui.GetItemRectMin(), ImGui.GetItemRectMax(); return { ImVec2(a.x, a.y), ImVec2(b.x, b.y) } end -- copies: GetItemRectMin() returns the live LastItemData.Rect.Min
|
||||||
|
local function click(name, ctrl)
|
||||||
|
local r = rects[name]
|
||||||
|
io:AddMousePosEvent((r[1].x + r[2].x) / 2, (r[1].y + r[2].y) / 2)
|
||||||
|
if ctrl then io:AddKeyEvent(G.ImGuiMod_Ctrl, true) end
|
||||||
|
io:AddMouseButtonEvent(0, true)
|
||||||
|
end
|
||||||
|
local function release() io:AddMouseButtonEvent(0, false); io:AddKeyEvent(G.ImGuiMod_Ctrl, false) end
|
||||||
|
local function key(k) io:AddKeyEvent(k, true); io:AddKeyEvent(k, false) end
|
||||||
|
|
||||||
|
game:GetService("RunService").Stepped:Connect(function()
|
||||||
|
f += 1
|
||||||
|
G.ImGui_ImplRoblox.NewFrame()
|
||||||
|
if f == 3 then click("t") elseif f == 4 then release() end
|
||||||
|
-- clipboard: tab is filtered out (single line, no AllowTabInput), é is 2 bytes
|
||||||
|
if f == 6 then ImGui.SetClipboardText("ab\tc\u{e9}"); io:AddKeyEvent(G.ImGuiMod_Ctrl, true); io:AddKeyEvent(G.ImGuiKey.V, true) end
|
||||||
|
if f == 7 then io:AddKeyEvent(G.ImGuiKey.V, false); io:AddKeyEvent(G.ImGuiMod_Ctrl, false) end
|
||||||
|
if f == 9 then key(G.ImGuiKey.Escape) end
|
||||||
|
if f == 11 then click("s", true) elseif f == 12 then release() end
|
||||||
|
if f == 14 then key(G.ImGuiKey.Escape) end
|
||||||
|
if f == 16 then click("d", true) elseif f == 17 then release() end
|
||||||
|
ImGui.NewFrame()
|
||||||
|
ImGui.SetNextWindowPos(ImVec2(10, 10)); ImGui.SetNextWindowSize(ImVec2(300, 200))
|
||||||
|
ImGui.Begin("T")
|
||||||
|
ImGui.InputText("##t", buf, 64); rects.t = rect()
|
||||||
|
sf = ImGui.SliderFloat("##s", sf, 0.0, 100.0, "%.0f deg"); rects.s = rect()
|
||||||
|
di = ImGui.DragInt("##d", di, 1, 0, 100, "%d%%"); rects.d = rect()
|
||||||
|
ImGui.End()
|
||||||
|
ImGui.Render(); G.ImGui_ImplRoblox.RenderDrawData(ImGui.GetDrawData())
|
||||||
|
if f == 8 then check(edit_text() == "abc\u{e9}", "paste: '" .. edit_text() .. "'") end
|
||||||
|
if f == 13 then check(edit_text() == "45", "SliderFloat temp input: '" .. edit_text() .. "'") end
|
||||||
|
if f == 18 then check(edit_text() == "50", "DragInt temp input: '" .. edit_text() .. "'") end
|
||||||
|
if f == 20 and #failed == 0 then print("FIXES OK") end
|
||||||
|
end)
|
||||||
@@ -124,12 +124,24 @@ local function MyTool()
|
|||||||
end
|
end
|
||||||
-- [/snippet]
|
-- [/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
|
local frame = 0
|
||||||
RunService.Stepped:Connect(function()
|
RunService.Stepped:Connect(function()
|
||||||
frame += 1
|
frame += 1
|
||||||
G.ImGui_ImplRoblox.NewFrame()
|
G.ImGui_ImplRoblox.NewFrame()
|
||||||
ImGui.NewFrame()
|
ImGui.NewFrame()
|
||||||
MyFirstTool(); MyTool(); Conventions(); MenusAndTable(); Plot(); CustomDrawing()
|
MyFirstTool(); MyTool(); Conventions(); MenusAndTable(); Plot(); CustomDrawing(); StatsWindow()
|
||||||
ImGui.Render()
|
ImGui.Render()
|
||||||
G.ImGui_ImplRoblox.RenderDrawData(ImGui.GetDrawData())
|
G.ImGui_ImplRoblox.RenderDrawData(ImGui.GetDrawData())
|
||||||
if frame == 20 then
|
if frame == 20 then
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
local G = require(game:GetService("ReplicatedStorage"):WaitForChild("ImGui"))
|
local G = require(game:GetService("ReplicatedStorage"):WaitForChild("ImGui"))
|
||||||
local ImGui, ImVec2 = G.ImGui, G.ImVec2
|
local ImGui, ImVec2 = G.ImGui, G.ImVec2
|
||||||
local UIS = game:GetService("UserInputService")
|
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 io = ImGui.GetIO()
|
||||||
local buf = ImGui.NewTextBuffer("", 64)
|
local buf = ImGui.NewTextBuffer("", 64)
|
||||||
local proxy
|
local proxy
|
||||||
|
|||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "4dbug/imgui-luau"
|
||||||
|
version = "0.1.4"
|
||||||
|
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]
|
||||||
Reference in New Issue
Block a user