Compare commits
8
Commits
95fdf94912
..
v0.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff86ea5aa2 | ||
|
|
4ac57cb571 | ||
|
|
15dc766d82 | ||
|
|
ed53d0683e | ||
|
|
b71e29ee35 | ||
|
|
bcf383a079 | ||
|
|
ce82582e37 | ||
|
|
090525076f |
+4
-3
@@ -116,7 +116,7 @@ The Style Editor (`ImGui.ShowStyleEditor()`) and the Metrics/Debugger (`ImGui.Sh
|
|||||||
|
|
||||||
### Getting Started & Integration (Roblox)
|
### Getting Started & Integration (Roblox)
|
||||||
|
|
||||||
With [Wally](https://wally.run) ([4dbug/imgui-luau](https://wally.run/package/4dbug/imgui-luau)): add `ImGui = "4dbug/imgui-luau@0.1.3"` under `[dependencies]` in your `wally.toml`, run `wally install`, then `require(ReplicatedStorage.Packages.ImGui)`. Or from source:
|
With [Wally](https://wally.run) ([4dbug/imgui-luau](https://wally.run/package/4dbug/imgui-luau)): add `ImGui = "4dbug/imgui-luau@0.2.0"` 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.
|
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`.
|
||||||
@@ -151,7 +151,8 @@ Backend options:
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `ImGui_ImplRoblox.Init(parent, render_scale)` | `render_scale` = your OS display scale renders at physical-pixel resolution (crisper, same size as desktop imgui). |
|
| `ImGui_ImplRoblox.Init(parent, render_scale)` | `render_scale` = your OS display scale renders at physical-pixel resolution (crisper, same size as desktop imgui). |
|
||||||
| `ImGui_ImplRoblox.SetRenderScale(scale)` | Change it at runtime. |
|
| `ImGui_ImplRoblox.SetRenderScale(scale)` | Change it at runtime. |
|
||||||
| `ImGui_ImplRoblox.SetRenderRate(hz)` | Max tile redraws per second (default 60, `0` = every frame). Your code still runs every frame. |
|
| `ImGui_ImplRoblox.SetRenderRate(hz)` | Max tile redraws per second (default `0` = every frame). Your code still runs every frame. |
|
||||||
|
| `ImGui_ImplRoblox.SetRedrawBudget(ms)` | Max milliseconds per frame spent redrawing changed tiles (opt-in; default `0` = no limit, every change is drawn the same frame). With a budget, tiles over it keep their old pixels for a few frames, so big changes are spread over frames instead of one long frame (parts of the UI may update a frame or two apart). |
|
||||||
| `ImGui_ImplRoblox.SetBusyRenderRate(hz)` | Optional lower redraw rate while scrolling, resizing or moving windows (default off). |
|
| `ImGui_ImplRoblox.SetBusyRenderRate(hz)` | Optional lower redraw rate while scrolling, resizing or moving windows (default off). |
|
||||||
| `ImGui_ImplRoblox.SetProfiling(true)` | MicroProfiler (Ctrl+F6) labels for every backend stage. |
|
| `ImGui_ImplRoblox.SetProfiling(true)` | MicroProfiler (Ctrl+F6) labels for every backend stage. |
|
||||||
| `ImGui_ImplRoblox.AddFile(path, bytes)` | Register a file (e.g. a `.ttf`) so `io.Fonts:AddFontFromFileTTF(path, size)` can load it. |
|
| `ImGui_ImplRoblox.AddFile(path, bytes)` | Register a file (e.g. a `.ttf`) so `io.Fonts:AddFontFromFileTTF(path, size)` can load it. |
|
||||||
@@ -178,7 +179,7 @@ The cost is CPU time in Luau, so the backend is built around **not drawing**:
|
|||||||
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.
|
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.
|
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.
|
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.
|
6. **Redraw budget.** Changed tiles are redrawn in the frame they change. Optionally, `SetRedrawBudget(ms)` caps the redraw time per frame; tiles over it keep their old pixels and follow on the next frames. Optionally capped with `SetRenderRate(hz)` / `SetBusyRenderRate(hz)`. 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).
|
**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).
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ jobs:
|
|||||||
unzip -q luau.zip -d luau && chmod +x luau/* && echo "$PWD/luau" >> "$GITHUB_PATH"
|
unzip -q luau.zip -d luau && chmod +x luau/* && echo "$PWD/luau" >> "$GITHUB_PATH"
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
|
env:
|
||||||
|
IMGUI_BUNDLE_STRICT: "1" # fail if enum folding (needs the luau CLI above) can't run
|
||||||
run: |
|
run: |
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
python3 tools/bundle.py dist/roblox # ImGui.luau (library + demo)
|
python3 tools/bundle.py dist/roblox # ImGui.luau (library + demo)
|
||||||
@@ -61,6 +63,8 @@ jobs:
|
|||||||
grep -q "CACHED OK" /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
|
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
|
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 | tee /tmp/h.txt
|
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
|
cp build/imgui_love.lua /tmp/imgui_love.lua && cp dist/imgui_love.min.lua build/imgui_love.lua
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ The Style Editor (`ImGui.ShowStyleEditor()`) and the Metrics/Debugger (`ImGui.Sh
|
|||||||
|
|
||||||
### Getting Started & Integration (Roblox)
|
### Getting Started & Integration (Roblox)
|
||||||
|
|
||||||
With [Wally](https://wally.run) ([4dbug/imgui-luau](https://wally.run/package/4dbug/imgui-luau)): add `ImGui = "4dbug/imgui-luau@0.1.3"` under `[dependencies]` in your `wally.toml`, run `wally install`, then `require(ReplicatedStorage.Packages.ImGui)`. Or from source:
|
With [Wally](https://wally.run) ([4dbug/imgui-luau](https://wally.run/package/4dbug/imgui-luau)): add `ImGui = "4dbug/imgui-luau@0.2.0"` 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.
|
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`.
|
||||||
@@ -148,7 +148,8 @@ Backend options:
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `ImGui_ImplRoblox.Init(parent, render_scale)` | `render_scale` = your OS display scale renders at physical-pixel resolution (crisper, same size as desktop imgui). |
|
| `ImGui_ImplRoblox.Init(parent, render_scale)` | `render_scale` = your OS display scale renders at physical-pixel resolution (crisper, same size as desktop imgui). |
|
||||||
| `ImGui_ImplRoblox.SetRenderScale(scale)` | Change it at runtime. |
|
| `ImGui_ImplRoblox.SetRenderScale(scale)` | Change it at runtime. |
|
||||||
| `ImGui_ImplRoblox.SetRenderRate(hz)` | Max tile redraws per second (default 60, `0` = every frame). Your code still runs every frame. |
|
| `ImGui_ImplRoblox.SetRenderRate(hz)` | Max tile redraws per second (default `0` = every frame). Your code still runs every frame. |
|
||||||
|
| `ImGui_ImplRoblox.SetRedrawBudget(ms)` | Max milliseconds per frame spent redrawing changed tiles (opt-in; default `0` = no limit, every change is drawn the same frame). With a budget, tiles over it keep their old pixels for a few frames, so big changes are spread over frames instead of one long frame (parts of the UI may update a frame or two apart). |
|
||||||
| `ImGui_ImplRoblox.SetBusyRenderRate(hz)` | Optional lower redraw rate while scrolling, resizing or moving windows (default off). |
|
| `ImGui_ImplRoblox.SetBusyRenderRate(hz)` | Optional lower redraw rate while scrolling, resizing or moving windows (default off). |
|
||||||
| `ImGui_ImplRoblox.SetProfiling(true)` | MicroProfiler (Ctrl+F6) labels for every backend stage. |
|
| `ImGui_ImplRoblox.SetProfiling(true)` | MicroProfiler (Ctrl+F6) labels for every backend stage. |
|
||||||
| `ImGui_ImplRoblox.AddFile(path, bytes)` | Register a file (e.g. a `.ttf`) so `io.Fonts:AddFontFromFileTTF(path, size)` can load it. |
|
| `ImGui_ImplRoblox.AddFile(path, bytes)` | Register a file (e.g. a `.ttf`) so `io.Fonts:AddFontFromFileTTF(path, size)` can load it. |
|
||||||
@@ -175,7 +176,7 @@ The cost is CPU time in Luau, so the backend is built around **not drawing**:
|
|||||||
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.
|
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.
|
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.
|
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.
|
6. **Redraw budget.** Changed tiles are redrawn in the frame they change. Optionally, `SetRedrawBudget(ms)` caps the redraw time per frame; tiles over it keep their old pixels and follow on the next frames. Optionally capped with `SetRenderRate(hz)` / `SetBusyRenderRate(hz)`. 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).
|
**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).
|
||||||
|
|
||||||
|
|||||||
+357
-61
@@ -142,6 +142,7 @@ end
|
|||||||
---------------------------------------------------------
|
---------------------------------------------------------
|
||||||
|
|
||||||
-- Copies a region of ImTextureData pixels (1-based byte table) into our RGBA8 buffer
|
-- Copies a region of ImTextureData pixels (1-based byte table) into our RGBA8 buffer
|
||||||
|
@native
|
||||||
local function CopyTexturePixels(tex, dst, x, y, w, h)
|
local function CopyTexturePixels(tex, dst, x, y, w, h)
|
||||||
local bpp, tex_w = tex.BytesPerPixel, tex.Width
|
local bpp, tex_w = tex.BytesPerPixel, tex.Width
|
||||||
for row = 0, h - 1 do
|
for row = 0, h - 1 do
|
||||||
@@ -208,6 +209,8 @@ end
|
|||||||
-- Blending is non-premultiplied "over" with a transparent (alpha 0) background, so the game stays visible behind ImGui.
|
-- Blending is non-premultiplied "over" with a transparent (alpha 0) background, so the game stays visible behind ImGui.
|
||||||
|
|
||||||
local buf, buf_w -- buffer/width of the tile being drawn
|
local buf, buf_w -- buffer/width of the tile being drawn
|
||||||
|
local rect_cols = table.create(256, 0) -- scratch: texel column offsets for DrawRect
|
||||||
|
local sub_bufs, n_sub_bufs = {}, 0 -- scratch: partial tile upload buffers by size
|
||||||
local TILE_SIZE -- Vector2, set below
|
local TILE_SIZE -- Vector2, set below
|
||||||
|
|
||||||
-- Blends one pixel (r, g, b, a are 0..255). One u32 read/write, one division in the general case.
|
-- Blends one pixel (r, g, b, a are 0..255). One u32 read/write, one division in the general case.
|
||||||
@@ -337,18 +340,30 @@ local function DrawRect(x0, y0, x1, y1, u0, v0, u1, v1, col, t, cx0, cy0, cx1, c
|
|||||||
local du, dv = (u1 - u0) / (x1 - x0), (v1 - v0) / (y1 - y0)
|
local du, dv = (u1 - u0) / (x1 - x0), (v1 - v0) / (y1 - y0)
|
||||||
local tw, th, tbuf = t.Width, t.Height, t.Buffer
|
local tw, th, tbuf = t.Width, t.Height, t.Buffer
|
||||||
local r255, g255, b255, a255 = r / 255, g / 255, b / 255, a / 255
|
local r255, g255, b255, a255 = r / 255, g / 255, b / 255, a / 255
|
||||||
|
-- The texel column only depends on x: compute it once per column instead of once per pixel
|
||||||
|
local ncol = px1 - px0
|
||||||
|
local cols = rect_cols
|
||||||
|
for c = 1, ncol do
|
||||||
|
local tx = floor((u0 + (px0 + c - 1 + 0.5 - x0) * du) * tw)
|
||||||
|
if tx < 0 then tx = 0 elseif tx >= tw then tx = tw - 1 end
|
||||||
|
cols[c] = tx * 4
|
||||||
|
end
|
||||||
for y = py0, py1 - 1 do
|
for y = py0, py1 - 1 do
|
||||||
local ty = floor((v0 + (y + 0.5 - y0) * dv) * th)
|
local ty = floor((v0 + (y + 0.5 - y0) * dv) * th)
|
||||||
if ty < 0 then ty = 0 elseif ty >= th then ty = th - 1 end
|
if ty < 0 then ty = 0 elseif ty >= th then ty = th - 1 end
|
||||||
local trow = ty * tw
|
local trow4 = ty * tw * 4
|
||||||
local o = (y * buf_w + px0) * 4
|
local o = (y * buf_w + px0) * 4
|
||||||
for x = px0, px1 - 1 do
|
for c = 1, ncol do
|
||||||
local tx = floor((u0 + (x + 0.5 - x0) * du) * tw)
|
local to = trow4 + cols[c]
|
||||||
if tx < 0 then tx = 0 elseif tx >= tw then tx = tw - 1 end
|
|
||||||
local to = (trow + tx) * 4
|
|
||||||
local ta = readu8(tbuf, to + 3)
|
local ta = readu8(tbuf, to + 3)
|
||||||
if ta > 0 then
|
if ta > 0 then
|
||||||
BlendPixel(o, readu8(tbuf, to) * r255, readu8(tbuf, to + 1) * g255, readu8(tbuf, to + 2) * b255, ta * a255)
|
local sa = ta * a255
|
||||||
|
local sr, sg, sb = readu8(tbuf, to) * r255, readu8(tbuf, to + 1) * g255, readu8(tbuf, to + 2) * b255
|
||||||
|
if sa >= 255 then -- (BlendPixel's opaque case, inlined: most glyph texels)
|
||||||
|
writeu32(buf, o, floor(sr + 0.5) + floor(sg + 0.5) * 256 + floor(sb + 0.5) * 65536 + 4278190080)
|
||||||
|
else
|
||||||
|
BlendPixel(o, sr, sg, sb, sa)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
o = o + 4
|
o = o + 4
|
||||||
end
|
end
|
||||||
@@ -442,6 +457,7 @@ local P1, P2 = 2147483647, 2147483629 -- two independent 31 bit hashes per tile
|
|||||||
local KEY_W = 64 -- tile key = ty * KEY_W + tx (layers up to 16384 px wide)
|
local KEY_W = 64 -- tile key = ty * KEY_W + tx (layers up to 16384 px wide)
|
||||||
local CLIP_PAD = 2048 -- content up to this far off screen is kept, so dragging a window past the edge doesn't redraw it
|
local CLIP_PAD = 2048 -- content up to this far off screen is kept, so dragging a window past the edge doesn't redraw it
|
||||||
local LAYER_KEEP_FRAMES = 120 -- unused layers are destroyed after this many rendered frames
|
local LAYER_KEEP_FRAMES = 120 -- unused layers are destroyed after this many rendered frames
|
||||||
|
local MAX_DEFER_FRAMES = 8 -- a tile is redrawn at the latest this many frames after it changed, even over the redraw budget
|
||||||
|
|
||||||
-- Primitive arrays (reused between frames, no per-frame allocation)
|
-- Primitive arrays (reused between frames, no per-frame allocation)
|
||||||
local pr_a, pr_b, pr_c, pr_d, pr_t, pr_g = {}, {}, {}, {}, {}, {} -- vertices (pr_d == false -> triangle), texture, gradient kind
|
local pr_a, pr_b, pr_c, pr_d, pr_t, pr_g = {}, {}, {}, {}, {}, {} -- vertices (pr_d == false -> triangle), texture, gradient kind
|
||||||
@@ -451,6 +467,7 @@ local pr_tex = {} -- texture id
|
|||||||
local pr_vb = {} -- vertex buffer (draw list VtxBuffer.Buf)
|
local pr_vb = {} -- vertex buffer (draw list VtxBuffer.Buf)
|
||||||
local pr_k = {} -- layer kind (1..3)
|
local pr_k = {} -- layer kind (1..3)
|
||||||
local plist1, plist2, plist3 = {}, {}, {} -- primitive indices per layer kind
|
local plist1, plist2, plist3 = {}, {}, {} -- primitive indices per layer kind
|
||||||
|
local cm_free, cm_kind, cm_x0, cm_y0, cm_x1, cm_y1 = {}, {}, {}, {}, {}, {} -- per draw command (collect -> Snapshot)
|
||||||
|
|
||||||
@native
|
@native
|
||||||
local function HashVertex(h1, h2, vbuf, v, ox, oy)
|
local function HashVertex(h1, h2, vbuf, v, ox, oy)
|
||||||
@@ -540,7 +557,7 @@ local function GetLayer(bd, draw_list, kind)
|
|||||||
else
|
else
|
||||||
frame = NewFrame(bd.Gui, false); root = frame
|
frame = NewFrame(bd.Gui, false); root = frame
|
||||||
end
|
end
|
||||||
layer = { Root = root, Frame = frame, Tiles = {}, TH1 = {}, TH2 = {}, TN = {}, TL = {}, TX = {}, TY = {}, Touched = {}, NTouched = 0,
|
layer = { Root = root, Frame = frame, Tiles = {}, TH1 = {}, TH2 = {}, TN = {}, TL = {}, TL1 = {}, TL2 = {}, TLF = {}, TX = {}, TY = {}, Touched = {}, NTouched = 0,
|
||||||
Visible = false, X = false, Y = false, Z = -1, CX0 = false, CY0 = false, CX1 = false, CY1 = false }
|
Visible = false, X = false, Y = false, Z = -1, CX0 = false, CY0 = false, CX1 = false, CY1 = false }
|
||||||
set[kind] = layer
|
set[kind] = layer
|
||||||
return layer
|
return layer
|
||||||
@@ -567,7 +584,8 @@ local function GetTile(bd, layer, key, tx, ty)
|
|||||||
label.Visible = false
|
label.Visible = false
|
||||||
label.Active = bd.TilesActive
|
label.Active = bd.TilesActive
|
||||||
label.Parent = layer.Frame
|
label.Parent = layer.Frame
|
||||||
tile = { Image = img, Label = label, Buffer = buffer.create(TILE * TILE * 4), H1 = -1, H2 = -1, Visible = false, BX0 = TILE, BY0 = TILE, BX1 = 0, BY1 = 0 }
|
tile = { Image = img, Label = label, Buffer = buffer.create(TILE * TILE * 4), H1 = -1, H2 = -1, Visible = false, BX0 = TILE, BY0 = TILE, BX1 = 0, BY1 = 0,
|
||||||
|
ON = -1, OH1 = {}, OH2 = {}, OF = {}, OB0 = {}, OB1 = {}, OB2 = {}, OB3 = {} } -- last drawn primitive list (hashes, bounds in tile)
|
||||||
layer.Tiles[key] = tile
|
layer.Tiles[key] = tile
|
||||||
return tile
|
return tile
|
||||||
end
|
end
|
||||||
@@ -587,31 +605,129 @@ local function DestroyTiles(bd)
|
|||||||
bd.Layers = {}
|
bd.Layers = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Exact signature of everything in a draw list that affects its layers (nil = can't skip: user callbacks)
|
-- Snapshot of everything in a draw list that affects its layers, compared exactly with the next frame's list:
|
||||||
local sig_buf = buffer.create(4096)
|
-- 1 = identical (only restack), 2 = everything moved by the same whole pixels (dx, dy) (only move the layers: a window
|
||||||
|
-- being dragged), 0 = changed (full path). Compared in place (no strings built): early exit at the first difference.
|
||||||
|
local writef64 = buffer.writef64
|
||||||
|
local CMD_STRIDE = 70 -- clip (4 f64), ElemCount, IdxOffset, VtxOffset (u32), tex id (f64), kind (u8), free (u8), bounds of its primitives (4 x i32)
|
||||||
|
|
||||||
@native
|
@native
|
||||||
local function DrawListSignature(draw_list, ox, oy)
|
local function Snapshot(set, draw_list, ox, oy, sw, sh)
|
||||||
|
local nv = draw_list.VtxBuffer.Size * VTX_STRIDE
|
||||||
local idx, ni = draw_list.IdxBuffer.Data, draw_list.IdxBuffer.Size
|
local idx, ni = draw_list.IdxBuffer.Data, draw_list.IdxBuffer.Size
|
||||||
local ncmd = draw_list.CmdBuffer.Size
|
local ncmd = draw_list.CmdBuffer.Size
|
||||||
local need = ni * 4 + ncmd * 64 + 64
|
local sv, si, sc = set.SV, set.SI, set.SC
|
||||||
if buffer.len(sig_buf) < need then sig_buf = buffer.create(need * 2) end
|
if not sv or buffer.len(sv) < nv then sv = buffer.create(max(nv, 64) * 2); set.SV = sv end
|
||||||
local b, o = sig_buf, 0
|
if not si or buffer.len(si) < ni * 4 then si = buffer.create(max(ni, 16) * 8); set.SI = si end
|
||||||
for i = 1, ni do writeu32(b, o, idx[i]); o += 4 end
|
if not sc or buffer.len(sc) < ncmd * CMD_STRIDE then sc = buffer.create(max(ncmd, 4) * CMD_STRIDE * 2); set.SC = sc end
|
||||||
|
buffer.copy(sv, 0, draw_list.VtxBuffer.Buf, 0, nv)
|
||||||
|
for i = 1, ni do writeu32(si, (i - 1) * 4, idx[i]) end
|
||||||
|
local o, ci = 0, 0
|
||||||
for _, pcmd in draw_list.CmdBuffer:iter() do
|
for _, pcmd in draw_list.CmdBuffer:iter() do
|
||||||
if pcmd.UserCallback ~= nil then return nil end
|
if pcmd.UserCallback ~= nil then set.SnapOK = false; return end
|
||||||
local c = pcmd.ClipRect
|
local c = pcmd.ClipRect
|
||||||
buffer.writef64(b, o, c.x); buffer.writef64(b, o + 8, c.y); buffer.writef64(b, o + 16, c.z); buffer.writef64(b, o + 24, c.w)
|
writef64(sc, o, c.x); writef64(sc, o + 8, c.y); writef64(sc, o + 16, c.z); writef64(sc, o + 24, c.w)
|
||||||
writeu32(b, o + 32, pcmd.ElemCount); writeu32(b, o + 36, pcmd.IdxOffset); writeu32(b, o + 40, pcmd.VtxOffset)
|
writeu32(sc, o + 32, pcmd.ElemCount); writeu32(sc, o + 36, pcmd.IdxOffset); writeu32(sc, o + 40, pcmd.VtxOffset)
|
||||||
buffer.writef64(b, o + 44, pcmd:GetTexID())
|
writef64(sc, o + 44, pcmd:GetTexID())
|
||||||
o += 52
|
ci += 1
|
||||||
|
buffer.writeu8(sc, o + 52, cm_kind[ci] or 0); buffer.writeu8(sc, o + 53, cm_free[ci] and 1 or 0)
|
||||||
|
if cm_free[ci] then
|
||||||
|
buffer.writei32(sc, o + 54, cm_x0[ci]); buffer.writei32(sc, o + 58, cm_y0[ci]); buffer.writei32(sc, o + 62, cm_x1[ci]); buffer.writei32(sc, o + 66, cm_y1[ci])
|
||||||
|
end
|
||||||
|
o += CMD_STRIDE
|
||||||
|
end
|
||||||
|
set.SVN, set.SIN, set.SCN = nv, ni, ncmd
|
||||||
|
set.SOX, set.SOY, set.SSW, set.SSH = ox, oy, sw, sh
|
||||||
|
set.SCC = draw_list._ContentClipX0 ~= nil
|
||||||
|
set.SCX0, set.SCY0, set.SCX1, set.SCY1 = draw_list._ContentClipX0 or 0, draw_list._ContentClipY0 or 0, draw_list._ContentClipX1 or 0, draw_list._ContentClipY1 or 0
|
||||||
|
set.SSX, set.SSY = draw_list._ScrollOriginX or 0, draw_list._ScrollOriginY or 0
|
||||||
|
set.SnapOK = true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- a + d == b exactly, with no rounding in a + d (so every later "x - origin" gives the same result as before the move)
|
||||||
|
@native
|
||||||
|
local function ShiftedExact(a, d, b)
|
||||||
|
local s = a + d
|
||||||
|
if s ~= b then return false end
|
||||||
|
local bb = s - a
|
||||||
|
return (a - (s - bb)) + (d - bb) == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
@native
|
||||||
|
local function CompareDrawList(set, draw_list, ox, oy, sw, sh)
|
||||||
|
if not set or not set.SnapOK then return 0 end
|
||||||
|
local nv = draw_list.VtxBuffer.Size * VTX_STRIDE
|
||||||
|
local ni, ncmd = draw_list.IdxBuffer.Size, draw_list.CmdBuffer.Size
|
||||||
|
if nv ~= set.SVN or ni ~= set.SIN or ncmd ~= set.SCN or ox ~= set.SOX or oy ~= set.SOY or sw ~= set.SSW or sh ~= set.SSH then return 0 end
|
||||||
|
if (draw_list._ContentClipX0 ~= nil) ~= set.SCC then return 0 end
|
||||||
|
local vb, sv = draw_list.VtxBuffer.Buf, set.SV
|
||||||
|
if nv == 0 then return 1 end
|
||||||
|
-- translation from the first vertex: whole pixels only, and only with DisplayPos at 0 (so it stays exact)
|
||||||
|
local dx, dy = readf64(vb, 0) - readf64(sv, 0), readf64(vb, 8) - readf64(sv, 8)
|
||||||
|
if dx ~= dx or dy ~= dy then return 0 end
|
||||||
|
if dx ~= 0 or dy ~= 0 then
|
||||||
|
if dx ~= floor(dx) or dy ~= floor(dy) or ox ~= 0 or oy ~= 0 or not set.SLimFree then return 0 end
|
||||||
|
-- everything must stay as far from the clip limits as before (nothing newly clamped)
|
||||||
|
if set.SEX0 + dx < -CLIP_PAD or set.SEY0 + dy < -CLIP_PAD or set.SEX1 + dx > sw + CLIP_PAD or set.SEY1 + dy > sh + CLIP_PAD then return 0 end
|
||||||
|
end
|
||||||
|
local idx, si = draw_list.IdxBuffer.Data, set.SI
|
||||||
|
for i = 1, ni do if idx[i] ~= readu32(si, (i - 1) * 4) then return 0 end end
|
||||||
|
local sc, o = set.SC, 0
|
||||||
|
local ccx0, ccy0, ccx1, ccy1 = draw_list._ContentClipX0, draw_list._ContentClipY0, draw_list._ContentClipX1, draw_list._ContentClipY1
|
||||||
|
local seen_content = false
|
||||||
|
for _, pcmd in draw_list.CmdBuffer:iter() do
|
||||||
|
if pcmd.UserCallback ~= nil then return 0 end
|
||||||
|
if pcmd.ElemCount ~= readu32(sc, o + 32) or pcmd.IdxOffset ~= readu32(sc, o + 36) or pcmd.VtxOffset ~= readu32(sc, o + 40) or pcmd:GetTexID() ~= readf64(sc, o + 44) then return 0 end
|
||||||
|
local c = pcmd.ClipRect
|
||||||
|
if dx == 0 and dy == 0 then
|
||||||
|
if c.x ~= readf64(sc, o) or c.y ~= readf64(sc, o + 8) or c.z ~= readf64(sc, o + 16) or c.w ~= readf64(sc, o + 24) then return 0 end
|
||||||
|
elseif not (ShiftedExact(readf64(sc, o), dx, c.x) and ShiftedExact(readf64(sc, o + 8), dy, c.y)
|
||||||
|
and ShiftedExact(readf64(sc, o + 16), dx, c.z) and ShiftedExact(readf64(sc, o + 24), dy, c.w)) then
|
||||||
|
-- An unchanged clip rect (e.g. the viewport) is fine if it clipped none of this command's primitives before
|
||||||
|
-- the move and still clips none after it
|
||||||
|
if c.x ~= readf64(sc, o) or c.y ~= readf64(sc, o + 8) or c.z ~= readf64(sc, o + 16) or c.w ~= readf64(sc, o + 24)
|
||||||
|
or buffer.readu8(sc, o + 53) == 0 then return 0 end
|
||||||
|
if buffer.readi32(sc, o + 54) + dx < floor(c.x) or buffer.readi32(sc, o + 58) + dy < floor(c.y)
|
||||||
|
or buffer.readi32(sc, o + 62) + dx > floor(c.z) or buffer.readi32(sc, o + 66) + dy > floor(c.w) then return 0 end
|
||||||
|
end
|
||||||
|
-- same layer kind as before (same rule as the collect pass)
|
||||||
|
if pcmd.ElemCount > 0 then
|
||||||
|
local kind = 1
|
||||||
|
if ccx0 and c.x >= ccx0 and c.y >= ccy0 and c.z <= ccx1 and c.w <= ccy1 then kind = 2; seen_content = true
|
||||||
|
elseif seen_content then kind = 3 end
|
||||||
|
if kind ~= buffer.readu8(sc, o + 52) then return 0 end
|
||||||
|
end
|
||||||
|
o += CMD_STRIDE
|
||||||
|
end
|
||||||
|
if dx == 0 and dy == 0 then
|
||||||
|
if set.SCC and (draw_list._ContentClipX0 ~= set.SCX0 or draw_list._ContentClipY0 ~= set.SCY0 or draw_list._ContentClipX1 ~= set.SCX1
|
||||||
|
or draw_list._ContentClipY1 ~= set.SCY1 or draw_list._ScrollOriginX ~= set.SSX or draw_list._ScrollOriginY ~= set.SSY) then return 0 end
|
||||||
|
for v = 0, nv - VTX_STRIDE, VTX_STRIDE do
|
||||||
|
if readf64(vb, v) ~= readf64(sv, v) or readf64(vb, v + 8) ~= readf64(sv, v + 8) or readf64(vb, v + 16) ~= readf64(sv, v + 16)
|
||||||
|
or readf64(vb, v + 24) ~= readf64(sv, v + 24) or readu32(vb, v + 32) ~= readu32(sv, v + 32) then return 0 end
|
||||||
|
end
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
if set.SCC and not (ShiftedExact(set.SCX0, dx, draw_list._ContentClipX0) and ShiftedExact(set.SCY0, dy, draw_list._ContentClipY0)
|
||||||
|
and ShiftedExact(set.SCX1, dx, draw_list._ContentClipX1) and ShiftedExact(set.SCY1, dy, draw_list._ContentClipY1)
|
||||||
|
and ShiftedExact(set.SSX, dx, draw_list._ScrollOriginX) and ShiftedExact(set.SSY, dy, draw_list._ScrollOriginY)) then return 0 end
|
||||||
|
for v = 0, nv - VTX_STRIDE, VTX_STRIDE do
|
||||||
|
if not ShiftedExact(readf64(sv, v), dx, readf64(vb, v)) or not ShiftedExact(readf64(sv, v + 8), dy, readf64(vb, v + 8))
|
||||||
|
or readf64(vb, v + 16) ~= readf64(sv, v + 16) or readf64(vb, v + 24) ~= readf64(sv, v + 24) or readu32(vb, v + 32) ~= readu32(sv, v + 32) then return 0 end
|
||||||
|
end
|
||||||
|
return 2, dx, dy
|
||||||
|
end
|
||||||
|
|
||||||
|
-- A window moved by whole pixels: its tiles are unchanged, only the layer frames move
|
||||||
|
local function ShiftLayer(layer, dx, dy, scale)
|
||||||
|
layer.X += dx; layer.Y += dy
|
||||||
|
local root = layer.Root
|
||||||
|
if root ~= layer.Frame then
|
||||||
|
layer.CX0 += dx; layer.CY0 += dy; layer.CX1 += dx; layer.CY1 += dy
|
||||||
|
root.Position = UDim2.fromOffset(layer.CX0 / scale, layer.CY0 / scale)
|
||||||
|
else
|
||||||
|
root.Position = UDim2.fromOffset(layer.X / scale, layer.Y / scale)
|
||||||
end
|
end
|
||||||
buffer.writef64(b, o, ox); buffer.writef64(b, o + 8, oy)
|
|
||||||
buffer.writef64(b, o + 16, draw_list._ContentClipX0 or -1); buffer.writef64(b, o + 24, draw_list._ContentClipY0 or -1)
|
|
||||||
buffer.writef64(b, o + 32, draw_list._ContentClipX1 or -1); buffer.writef64(b, o + 40, draw_list._ContentClipY1 or -1)
|
|
||||||
buffer.writef64(b, o + 48, draw_list._ContentClipX0 and draw_list._ScrollOriginX or -1)
|
|
||||||
buffer.writef64(b, o + 56, draw_list._ContentClipX0 and draw_list._ScrollOriginY or -1)
|
|
||||||
o += 64
|
|
||||||
return readstring(draw_list.VtxBuffer.Buf, 0, draw_list.VtxBuffer.Size * VTX_STRIDE) .. readstring(b, 0, o)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Overlays: user Instances (e.g. a Frame holding a ViewportFrame) placed inside a window, registered every frame with
|
-- Overlays: user Instances (e.g. a Frame holding a ViewportFrame) placed inside a window, registered every frame with
|
||||||
@@ -690,9 +806,11 @@ local function PE() if profiling then profileend() end end
|
|||||||
@native
|
@native
|
||||||
local function DrawLayer(bd, layer, plist, np, lx0, ly0, ox, oy, force, z, clip_x0, clip_y0, clip_x1, clip_y1)
|
local function DrawLayer(bd, layer, plist, np, lx0, ly0, ox, oy, force, z, clip_x0, clip_y0, clip_x1, clip_y1)
|
||||||
local scale = bd.Scale
|
local scale = bd.Scale
|
||||||
|
local deferred = false
|
||||||
-- Pass 2: hash primitives into layer-relative tiles
|
-- Pass 2: hash primitives into layer-relative tiles
|
||||||
PB("ImGui tile hash")
|
PB("ImGui tile hash")
|
||||||
local th1, th2, tn, tl, touched = layer.TH1, layer.TH2, layer.TN, layer.TL, layer.Touched
|
local th1, th2, tn, tl, touched = layer.TH1, layer.TH2, layer.TN, layer.TL, layer.Touched
|
||||||
|
local tl1, tl2, tlf = layer.TL1, layer.TL2, layer.TLF -- per-tile list of the primitives' hashes (for partial redraws)
|
||||||
for k = 1, layer.NTouched do tn[touched[k]] = 0 end
|
for k = 1, layer.NTouched do tn[touched[k]] = 0 end
|
||||||
local nt = 0
|
local nt = 0
|
||||||
for pi = 1, np do
|
for pi = 1, np do
|
||||||
@@ -701,23 +819,55 @@ local function DrawLayer(bd, layer, plist, np, lx0, ly0, ox, oy, force, z, clip_
|
|||||||
-- The clip rect only affects pixels inside the primitive's bounds, so hash the clipped bounds (relative)
|
-- The clip rect only affects pixels inside the primitive's bounds, so hash the clipped bounds (relative)
|
||||||
-- instead of the clip itself: a window clipped by the (fixed) viewport rect stays hash-stable when moved.
|
-- instead of the clip itself: a window clipped by the (fixed) viewport rect stays hash-stable when moved.
|
||||||
local cx0, cy0, cx1, cy1 = pr_bx0[p] - lx0, pr_by0[p] - ly0, pr_bx1[p] - lx0, pr_by1[p] - ly0
|
local cx0, cy0, cx1, cy1 = pr_bx0[p] - lx0, pr_by0[p] - ly0, pr_bx1[p] - lx0, pr_by1[p] - ly0
|
||||||
local h1 = ((cx0 * 8191 + cy0) * 8191 + cx1) % P1
|
|
||||||
local h2 = ((cy1 * 8191 + cx1) * 8191 + cy0 + tex_id) % P2
|
|
||||||
local vax, vay = lx0 + ox, ly0 + oy
|
|
||||||
local vbuf = pr_vb[p]
|
local vbuf = pr_vb[p]
|
||||||
h1, h2 = HashVertex(h1, h2, vbuf, pr_a[p], vax, vay)
|
local va, vd = pr_a[p], pr_d[p]
|
||||||
h1, h2 = HashVertex(h1, h2, vbuf, pr_b[p], vax, vay)
|
-- Flat rect (single colour, no gradient, constant uv): its pixels in a tile are exactly its clipped pixel rect
|
||||||
h1, h2 = HashVertex(h1, h2, vbuf, pr_c[p], vax, vay)
|
-- inside that tile filled with one colour, so hash that per tile instead of the vertices. Resizing a window
|
||||||
local vd = pr_d[p]
|
-- then only changes the tiles its edges cross, not every tile its background covers.
|
||||||
if vd then h1, h2 = HashVertex(h1, h2, vbuf, vd, vax, vay) end
|
local flat_rect, rcol, ruv = false, 0, 0
|
||||||
|
if vd and not pr_g[p] then
|
||||||
|
local u0, v0 = readf64(vbuf, va + 16), readf64(vbuf, va + 24)
|
||||||
|
local vc = pr_c[p]
|
||||||
|
if not pr_t[p] or (u0 == readf64(vbuf, vc + 16) and v0 == readf64(vbuf, vc + 24)) then
|
||||||
|
flat_rect, rcol, ruv = true, readu32(vbuf, va + 32), u0 * 8192 + v0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local h1, h2 = 0, 0
|
||||||
|
local fsig = -1 -- flat rect: colour/uv/texture without the geometry (-1: not a flat rect)
|
||||||
|
if flat_rect then fsig = ((rcol * 8191 + ruv * 4096) % P1 * 64 + tex_id) % P1 end
|
||||||
|
if not flat_rect then
|
||||||
|
h1 = ((cx0 * 8191 + cy0) * 8191 + cx1) % P1
|
||||||
|
h2 = ((cy1 * 8191 + cx1) * 8191 + cy0 + tex_id) % P2
|
||||||
|
local vax, vay = lx0 + ox, ly0 + oy
|
||||||
|
if vd then
|
||||||
|
-- Axis aligned quad: the collect pass checked that TR/BL (positions, uvs, colours) follow from TL/BR
|
||||||
|
-- (and the gradient kind), so TL + BR + kind determine its pixels
|
||||||
|
h1, h2 = HashVertex(h1, h2, vbuf, va, vax, vay)
|
||||||
|
h1, h2 = HashVertex(h1, h2, vbuf, pr_c[p], vax, vay)
|
||||||
|
local gk = pr_g[p]
|
||||||
|
if gk then h1 = (h1 * 48271 + gk) % P1; h2 = (h2 * 16807 + gk * 3) % P2 end
|
||||||
|
else
|
||||||
|
h1, h2 = HashVertex(h1, h2, vbuf, va, vax, vay)
|
||||||
|
h1, h2 = HashVertex(h1, h2, vbuf, pr_b[p], vax, vay)
|
||||||
|
h1, h2 = HashVertex(h1, h2, vbuf, pr_c[p], vax, vay)
|
||||||
|
end
|
||||||
|
end
|
||||||
for ty = floor((pr_by0[p] - ly0) / TILE), floor((pr_by1[p] - 1 - ly0) / TILE) do
|
for ty = floor((pr_by0[p] - ly0) / TILE), floor((pr_by1[p] - 1 - ly0) / TILE) do
|
||||||
for tx = floor((pr_bx0[p] - lx0) / TILE), floor((pr_bx1[p] - 1 - lx0) / TILE) do
|
for tx = floor((pr_bx0[p] - lx0) / TILE), floor((pr_bx1[p] - 1 - lx0) / TILE) do
|
||||||
|
if flat_rect then
|
||||||
|
local tx0, ty0 = tx * TILE, ty * TILE
|
||||||
|
local ix0, iy0 = max(cx0 - tx0, 0), max(cy0 - ty0, 0)
|
||||||
|
local ix1, iy1 = min(cx1 - tx0, TILE), min(cy1 - ty0, TILE)
|
||||||
|
local r = ((ix0 * 131 + iy0) * 131 + ix1) * 131 + iy1
|
||||||
|
h1 = ((r * 48271 + rcol) % P1 * 48271 + ruv * 4096 + 7) % P1
|
||||||
|
h2 = ((r * 16807 + rcol + tex_id) % P2 * 16807 + ruv * 65521 + 11) % P2
|
||||||
|
end
|
||||||
local key = ty * KEY_W + tx
|
local key = ty * KEY_W + tx
|
||||||
local k = tn[key]
|
local k = tn[key]
|
||||||
if not k or k == 0 then
|
if not k or k == 0 then
|
||||||
k = 0
|
k = 0
|
||||||
th1[key], th2[key] = 0, 0
|
th1[key], th2[key] = 0, 0
|
||||||
if not tl[key] then tl[key] = {} end
|
if not tl[key] then tl[key] = {}; tl1[key] = {}; tl2[key] = {}; tlf[key] = {} end
|
||||||
nt = nt + 1
|
nt = nt + 1
|
||||||
touched[nt] = key
|
touched[nt] = key
|
||||||
layer.TX[key], layer.TY[key] = tx, ty
|
layer.TX[key], layer.TY[key] = tx, ty
|
||||||
@@ -725,6 +875,7 @@ local function DrawLayer(bd, layer, plist, np, lx0, ly0, ox, oy, force, z, clip_
|
|||||||
k = k + 1
|
k = k + 1
|
||||||
tn[key] = k
|
tn[key] = k
|
||||||
tl[key][k] = p
|
tl[key][k] = p
|
||||||
|
tl1[key][k], tl2[key][k], tlf[key][k] = h1, h2, fsig
|
||||||
th1[key] = (th1[key] * 48271 + h1) % P1
|
th1[key] = (th1[key] * 48271 + h1) % P1
|
||||||
th2[key] = (th2[key] * 16807 + h2 + k) % P2
|
th2[key] = (th2[key] * 16807 + h2 + k) % P2
|
||||||
end
|
end
|
||||||
@@ -758,7 +909,7 @@ local function DrawLayer(bd, layer, plist, np, lx0, ly0, ox, oy, force, z, clip_
|
|||||||
PB("ImGui raster+upload")
|
PB("ImGui raster+upload")
|
||||||
for key, tile in pairs(layer.Tiles) do
|
for key, tile in pairs(layer.Tiles) do
|
||||||
if not tn[key] or tn[key] == 0 then
|
if not tn[key] or tn[key] == 0 then
|
||||||
if tile.Visible then tile.Visible = false; tile.Label.Visible = false; tile.H1 = -1 end
|
if tile.Visible then tile.Visible = false; tile.Label.Visible = false; tile.H1 = -1; tile.ON = -1 end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
for kk = 1, nt do
|
for kk = 1, nt do
|
||||||
@@ -766,14 +917,27 @@ local function DrawLayer(bd, layer, plist, np, lx0, ly0, ox, oy, force, z, clip_
|
|||||||
local count = tn[key]
|
local count = tn[key]
|
||||||
local tx, ty = layer.TX[key], layer.TY[key]
|
local tx, ty = layer.TX[key], layer.TY[key]
|
||||||
local tile = GetTile(bd, layer, key, tx, ty)
|
local tile = GetTile(bd, layer, key, tx, ty)
|
||||||
if force or tile.H1 ~= th1[key] or tile.H2 ~= th2[key] then
|
local dirty = force or tile.H1 ~= th1[key] or tile.H2 ~= th2[key]
|
||||||
|
if dirty and bd.RedrawStarted and (tile.Deferred or 0) < MAX_DEFER_FRAMES
|
||||||
|
and ((bd.RedrawBudget > 0 and os.clock() - bd.FrameT0 >= bd.RedrawBudget) or (bd.TileLimit and bd.FrameTiles >= bd.TileLimit)) then -- (TileLimit: test hook, tools/test/scripts/t_pixels.luau)
|
||||||
|
-- Over this frame's redraw budget: keep showing the old pixels, redraw on a following frame. The tile's
|
||||||
|
-- stored hashes/list still describe what it shows, so the next comparison is against the real content.
|
||||||
|
tile.Deferred = (tile.Deferred or 0) + 1
|
||||||
|
if force then tile.H1 = -1; tile.ON = -1 end -- full redraw later (force doesn't persist)
|
||||||
|
deferred = true
|
||||||
|
dirty = false
|
||||||
|
end
|
||||||
|
if dirty then
|
||||||
|
bd.RedrawStarted = true
|
||||||
|
bd.FrameTiles += 1
|
||||||
|
tile.Deferred = 0
|
||||||
tile.H1, tile.H2 = th1[key], th2[key]
|
tile.H1, tile.H2 = th1[key], th2[key]
|
||||||
local x0, y0 = lx0 + tx * TILE, ly0 + ty * TILE
|
local x0, y0 = lx0 + tx * TILE, ly0 + ty * TILE
|
||||||
local x1, y1 = x0 + TILE, y0 + TILE
|
local x1, y1 = x0 + TILE, y0 + TILE
|
||||||
buf, buf_w = tile.Buffer, TILE
|
buf, buf_w = tile.Buffer, TILE
|
||||||
local list = tl[key]
|
local list, lh1, lh2 = tl[key], tl1[key], tl2[key]
|
||||||
-- Dirty region = old content bounds + new content bounds. Outside it the tile is already
|
local oh1, oh2, ob0, ob1, ob2, ob3, on = tile.OH1, tile.OH2, tile.OB0, tile.OB1, tile.OB2, tile.OB3, tile.ON
|
||||||
-- transparent, so only this region is cleared and uploaded (edge tiles are often mostly empty).
|
-- New content bounds (union of the primitives' bounds in this tile)
|
||||||
local nx0, ny0, nx1, ny1 = TILE, TILE, 0, 0
|
local nx0, ny0, nx1, ny1 = TILE, TILE, 0, 0
|
||||||
for k = 1, count do
|
for k = 1, count do
|
||||||
local p = list[k]
|
local p = list[k]
|
||||||
@@ -785,8 +949,63 @@ local function DrawLayer(bd, layer, plist, np, lx0, ly0, ox, oy, force, z, clip_
|
|||||||
if d > ny1 then ny1 = d end
|
if d > ny1 then ny1 = d end
|
||||||
end
|
end
|
||||||
nx0, ny0, nx1, ny1 = max(nx0, 0), max(ny0, 0), min(nx1, TILE), min(ny1, TILE)
|
nx0, ny0, nx1, ny1 = max(nx0, 0), max(ny0, 0), min(nx1, TILE), min(ny1, TILE)
|
||||||
local rx0, ry0, rx1, ry1 = min(nx0, tile.BX0), min(ny0, tile.BY0), max(nx1, tile.BX1), max(ny1, tile.BY1)
|
local rx0, ry0, rx1, ry1
|
||||||
|
if force or on < 0 then
|
||||||
|
-- Dirty region = old content bounds + new content bounds. Outside it the tile is already
|
||||||
|
-- transparent, so only this region is cleared and uploaded (edge tiles are often mostly empty).
|
||||||
|
rx0, ry0, rx1, ry1 = min(nx0, tile.BX0), min(ny0, tile.BY0), max(nx1, tile.BX1), max(ny1, tile.BY1)
|
||||||
|
else
|
||||||
|
-- Partial redraw: the primitives before the first and after the last changed one are the same as last
|
||||||
|
-- time. A pixel that no changed primitive (old or new) touches is covered by the same primitives in the
|
||||||
|
-- same order, so it is already correct: only the bounds of the changed ones are cleared and redrawn.
|
||||||
|
local i, lim = 1, min(on, count)
|
||||||
|
while i <= lim and oh1[i] == lh1[i] and oh2[i] == lh2[i] do i += 1 end
|
||||||
|
local j = 0
|
||||||
|
while j <= lim - i and oh1[on - j] == lh1[count - j] and oh2[on - j] == lh2[count - j] do j += 1 end
|
||||||
|
rx0, ry0, rx1, ry1 = TILE, TILE, 0, 0
|
||||||
|
local of, lf = tile.OF, tlf[key]
|
||||||
|
local paired = on - j - i == count - j - i
|
||||||
|
for k = i, count - j do
|
||||||
|
local p = list[k]
|
||||||
|
local a, b, c, d = max(pr_bx0[p] - x0, 0), max(pr_by0[p] - y0, 0), min(pr_bx1[p] - x0, TILE), min(pr_by1[p] - y0, TILE)
|
||||||
|
local oa, ob, oc, od = ob0[k], ob1[k], ob2[k], ob3[k]
|
||||||
|
if paired and lf[k] >= 0 and lf[k] == of[k] then
|
||||||
|
-- Same flat rect (colour/uv/texture) with new bounds: pixels inside both are unchanged, only the
|
||||||
|
-- strips between the old and new edges can differ (e.g. a window background growing on resize)
|
||||||
|
local uy0, uy1 = min(b, ob), max(d, od)
|
||||||
|
local ux0, ux1 = min(a, oa), max(c, oc)
|
||||||
|
if a ~= oa then ux0 = min(a, oa); local e = max(a, oa); rx0 = min(rx0, ux0); rx1 = max(rx1, e); ry0 = min(ry0, uy0); ry1 = max(ry1, uy1) end
|
||||||
|
if c ~= oc then local e = min(c, oc); rx0 = min(rx0, e); rx1 = max(rx1, max(c, oc)); ry0 = min(ry0, uy0); ry1 = max(ry1, uy1) end
|
||||||
|
if b ~= ob then ry0 = min(ry0, min(b, ob)); ry1 = max(ry1, max(b, ob)); rx0 = min(rx0, ux0); rx1 = max(rx1, ux1) end
|
||||||
|
if d ~= od then ry0 = min(ry0, min(d, od)); ry1 = max(ry1, max(d, od)); rx0 = min(rx0, ux0); rx1 = max(rx1, ux1) end
|
||||||
|
else
|
||||||
|
if a < rx0 then rx0 = a end
|
||||||
|
if b < ry0 then ry0 = b end
|
||||||
|
if c > rx1 then rx1 = c end
|
||||||
|
if d > ry1 then ry1 = d end
|
||||||
|
if k <= on - j then
|
||||||
|
if oa < rx0 then rx0 = oa end
|
||||||
|
if ob < ry0 then ry0 = ob end
|
||||||
|
if oc > rx1 then rx1 = oc end
|
||||||
|
if od > ry1 then ry1 = od end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for k = count - j + 1, on - j do -- old middle longer than the new one
|
||||||
|
if ob0[k] < rx0 then rx0 = ob0[k] end
|
||||||
|
if ob1[k] < ry0 then ry0 = ob1[k] end
|
||||||
|
if ob2[k] > rx1 then rx1 = ob2[k] end
|
||||||
|
if ob3[k] > ry1 then ry1 = ob3[k] end
|
||||||
|
end
|
||||||
|
end
|
||||||
tile.BX0, tile.BY0, tile.BX1, tile.BY1 = nx0, ny0, nx1, ny1
|
tile.BX0, tile.BY0, tile.BX1, tile.BY1 = nx0, ny0, nx1, ny1
|
||||||
|
-- Remember this list for the next partial redraw
|
||||||
|
for k = 1, count do
|
||||||
|
local p = list[k]
|
||||||
|
oh1[k], oh2[k], tile.OF[k] = lh1[k], lh2[k], tlf[key][k]
|
||||||
|
ob0[k], ob1[k], ob2[k], ob3[k] = max(pr_bx0[p] - x0, 0), max(pr_by0[p] - y0, 0), min(pr_bx1[p] - x0, TILE), min(pr_by1[p] - y0, TILE)
|
||||||
|
end
|
||||||
|
tile.ON = count
|
||||||
local rw = rx1 - rx0
|
local rw = rx1 - rx0
|
||||||
if rw == TILE then
|
if rw == TILE then
|
||||||
buffer.fill(buf, ry0 * TILE * 4, 0, (ry1 - ry0) * TILE * 4)
|
buffer.fill(buf, ry0 * TILE * 4, 0, (ry1 - ry0) * TILE * 4)
|
||||||
@@ -796,8 +1015,10 @@ local function DrawLayer(bd, layer, plist, np, lx0, ly0, ox, oy, force, z, clip_
|
|||||||
local tox, toy = ox + x0, oy + y0
|
local tox, toy = ox + x0, oy + y0
|
||||||
for k = 1, count do
|
for k = 1, count do
|
||||||
local p = list[k]
|
local p = list[k]
|
||||||
local cx0, cy0 = max(pr_cx0[p], x0) - x0, max(pr_cy0[p], y0) - y0
|
-- clip to the dirty region (only those pixels are redrawn)
|
||||||
local cx1, cy1 = min(pr_cx1[p], x1) - x0, min(pr_cy1[p], y1) - y0
|
local cx0, cy0 = max(pr_cx0[p] - x0, rx0), max(pr_cy0[p] - y0, ry0)
|
||||||
|
local cx1, cy1 = min(pr_cx1[p] - x0, rx1), min(pr_cy1[p] - y0, ry1)
|
||||||
|
if cx0 >= cx1 or cy0 >= cy1 or pr_bx0[p] - x0 >= rx1 or pr_by0[p] - y0 >= ry1 or pr_bx1[p] - x0 <= rx0 or pr_by1[p] - y0 <= ry0 then continue end
|
||||||
local va, vc, vd, vbuf = pr_a[p], pr_c[p], pr_d[p], pr_vb[p]
|
local va, vc, vd, vbuf = pr_a[p], pr_c[p], pr_d[p], pr_vb[p]
|
||||||
if vd then
|
if vd then
|
||||||
local ax, ay, cx, cy = readf64(vbuf, va) - tox, readf64(vbuf, va + 8) - toy, readf64(vbuf, vc) - tox, readf64(vbuf, vc + 8) - toy
|
local ax, ay, cx, cy = readf64(vbuf, va) - tox, readf64(vbuf, va + 8) - toy, readf64(vbuf, vc) - tox, readf64(vbuf, vc + 8) - toy
|
||||||
@@ -816,31 +1037,42 @@ local function DrawLayer(bd, layer, plist, np, lx0, ly0, ox, oy, force, z, clip_
|
|||||||
tile.Image:WritePixelsBuffer(Vector2.zero, TILE_SIZE, buf)
|
tile.Image:WritePixelsBuffer(Vector2.zero, TILE_SIZE, buf)
|
||||||
elseif rw > 0 and ry1 > ry0 then
|
elseif rw > 0 and ry1 > ry0 then
|
||||||
local rh = ry1 - ry0
|
local rh = ry1 - ry0
|
||||||
local sub = buffer.create(rw * rh * 4)
|
local sk = rw * 1024 + rh -- partial upload buffers are reused (WritePixelsBuffer needs the exact size)
|
||||||
|
local sub = sub_bufs[sk]
|
||||||
|
if not sub then
|
||||||
|
if n_sub_bufs >= 256 then table.clear(sub_bufs); n_sub_bufs = 0 end
|
||||||
|
sub = buffer.create(rw * rh * 4); sub_bufs[sk] = sub; n_sub_bufs += 1
|
||||||
|
end
|
||||||
for y = 0, rh - 1 do buffer.copy(sub, y * rw * 4, buf, ((ry0 + y) * TILE + rx0) * 4, rw * 4) end
|
for y = 0, rh - 1 do buffer.copy(sub, y * rw * 4, buf, ((ry0 + y) * TILE + rx0) * 4, rw * 4) end
|
||||||
tile.Image:WritePixelsBuffer(Vector2.new(rx0, ry0), Vector2.new(rw, rh), sub)
|
tile.Image:WritePixelsBuffer(Vector2.new(rx0, ry0), Vector2.new(rw, rh), sub)
|
||||||
end
|
end
|
||||||
PE()
|
PE()
|
||||||
end
|
end
|
||||||
if not tile.Visible then
|
if not tile.Visible and (tile.Deferred or 0) == 0 then -- (a deferred tile that was hidden holds old pixels: stays hidden until drawn)
|
||||||
tile.Visible = true
|
tile.Visible = true
|
||||||
tile.Label.Visible = true
|
tile.Label.Visible = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
PE()
|
PE()
|
||||||
|
return deferred
|
||||||
end
|
end
|
||||||
|
|
||||||
@native
|
@native
|
||||||
function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
||||||
local bd = ImGui_ImplRoblox_GetBackendData()
|
local bd = ImGui_ImplRoblox_GetBackendData()
|
||||||
PB("ImGui RenderDrawData")
|
PB("ImGui RenderDrawData")
|
||||||
|
bd.FrameT0 = os.clock()
|
||||||
|
bd.FrameTiles = 0
|
||||||
|
bd.RedrawStarted = false -- the first dirty tile of a frame is always redrawn (progress), the rest within the budget
|
||||||
|
bd.Stale = nil
|
||||||
|
|
||||||
PB("ImGui textures")
|
PB("ImGui textures")
|
||||||
if draw_data.Textures ~= nil then
|
if draw_data.Textures ~= nil then
|
||||||
for _, tex in draw_data.Textures:iter() do
|
for _, tex in draw_data.Textures:iter() do
|
||||||
if tex.Status ~= ImTextureStatus.OK then
|
if tex.Status ~= ImTextureStatus.OK then
|
||||||
|
-- No redraw needed for WantUpdates: it only writes texture portions which have never been used before
|
||||||
|
-- (new glyphs/rects), so no existing tile samples them. Created textures get a new (never reused) id.
|
||||||
ImGui_ImplRoblox_UpdateTexture(tex)
|
ImGui_ImplRoblox_UpdateTexture(tex)
|
||||||
bd.ForceRedraw = true -- texture contents changed: tiles sampling it must be redrawn
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -878,20 +1110,27 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
|||||||
|
|
||||||
local n = 0
|
local n = 0
|
||||||
local z = 0
|
local z = 0
|
||||||
|
local any_deferred = false
|
||||||
for _, draw_list in draw_data.CmdLists:iter() do
|
for _, draw_list in draw_data.CmdLists:iter() do
|
||||||
-- Unchanged draw list (same vertex bytes, indices, commands, origin and scroll): its layers are already
|
-- Unchanged draw list (same vertices, indices, commands, origin and scroll): its layers are already correct,
|
||||||
-- correct, only restack them. Static windows then cost one byte compare instead of collect + hash.
|
-- only restack them. Moved by whole pixels (a dragged window): only move its layers. Either way no collect + hash.
|
||||||
PB("ImGui unchanged check")
|
PB("ImGui unchanged check")
|
||||||
local sig = DrawListSignature(draw_list, ox, oy)
|
|
||||||
local set = bd.Layers[draw_list]
|
local set = bd.Layers[draw_list]
|
||||||
if sig and set and not force and set.Sig == sig then
|
local same, sdx, sdy = 0, 0, 0
|
||||||
|
if not force then same, sdx, sdy = CompareDrawList(set, draw_list, ox, oy, sw, sh) end
|
||||||
|
if same ~= 0 then
|
||||||
for kind = 1, 3 do
|
for kind = 1, 3 do
|
||||||
local layer = set[kind]
|
local layer = set[kind]
|
||||||
if layer and layer.Visible then
|
if layer and layer.Visible then
|
||||||
|
if same == 2 then ShiftLayer(layer, sdx, sdy, bd.Scale) end
|
||||||
z += 1
|
z += 1
|
||||||
if layer.Z ~= z then layer.Z = z; layer.Root.ZIndex = z end
|
if layer.Z ~= z then layer.Z = z; layer.Root.ZIndex = z end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if same == 2 then
|
||||||
|
Snapshot(set, draw_list, ox, oy, sw, sh)
|
||||||
|
set.SEX0 += sdx; set.SEY0 += sdy; set.SEX1 += sdx; set.SEY1 += sdy
|
||||||
|
end
|
||||||
set.LastUsed = frame_no
|
set.LastUsed = frame_no
|
||||||
z = PlaceOverlays(bd, draw_list, z, ox, oy)
|
z = PlaceOverlays(bd, draw_list, z, ox, oy)
|
||||||
PE()
|
PE()
|
||||||
@@ -909,13 +1148,30 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
|||||||
content_x1, content_y1 = min(floor(ccx1 - ox), lim_x1), min(floor(ccy1 - oy), lim_y1)
|
content_x1, content_y1 = min(floor(ccx1 - ox), lim_x1), min(floor(ccy1 - oy), lim_y1)
|
||||||
end
|
end
|
||||||
local seen_content = false
|
local seen_content = false
|
||||||
|
-- extents of everything clamped by the clip limits below (lim_free: nothing was), for the move fast path
|
||||||
|
local ex0, ey0, ex1, ey1 = math.huge, math.huge, -math.huge, -math.huge
|
||||||
|
local lim_free = true
|
||||||
|
if ccx0 then
|
||||||
|
local a, b, c, d = floor(ccx0 - ox), floor(ccy0 - oy), floor(ccx1 - ox), floor(ccy1 - oy)
|
||||||
|
if a < lim_x0 or b < lim_y0 or c > lim_x1 or d > lim_y1 then lim_free = false end
|
||||||
|
ex0, ey0, ex1, ey1 = min(ex0, a), min(ey0, b), max(ex1, c), max(ey1, d)
|
||||||
|
end
|
||||||
|
local ci = 0
|
||||||
for _, pcmd in draw_list.CmdBuffer:iter() do
|
for _, pcmd in draw_list.CmdBuffer:iter() do
|
||||||
|
ci += 1
|
||||||
|
cm_free[ci], cm_kind[ci] = false, 0
|
||||||
if pcmd.UserCallback ~= nil then
|
if pcmd.UserCallback ~= nil then
|
||||||
pcmd.UserCallback(draw_list, pcmd)
|
pcmd.UserCallback(draw_list, pcmd)
|
||||||
elseif pcmd.ElemCount > 0 then
|
elseif pcmd.ElemCount > 0 then
|
||||||
local clip = pcmd.ClipRect
|
local clip = pcmd.ClipRect
|
||||||
local cx0, cy0 = max(floor(clip.x - ox), lim_x0), max(floor(clip.y - oy), lim_y0)
|
local rcx0, rcy0, rcx1, rcy1 = floor(clip.x - ox), floor(clip.y - oy), floor(clip.z - ox), floor(clip.w - oy)
|
||||||
local cx1, cy1 = min(floor(clip.z - ox), lim_x1), min(floor(clip.w - oy), lim_y1)
|
if rcx0 < lim_x0 or rcy0 < lim_y0 or rcx1 > lim_x1 or rcy1 > lim_y1 then lim_free = false end
|
||||||
|
if rcx0 < ex0 then ex0 = rcx0 end
|
||||||
|
if rcy0 < ey0 then ey0 = rcy0 end
|
||||||
|
if rcx1 > ex1 then ex1 = rcx1 end
|
||||||
|
if rcy1 > ey1 then ey1 = rcy1 end
|
||||||
|
local cx0, cy0 = max(rcx0, lim_x0), max(rcy0, lim_y0)
|
||||||
|
local cx1, cy1 = min(rcx1, lim_x1), min(rcy1, lim_y1)
|
||||||
local kind = 1
|
local kind = 1
|
||||||
if ccx0 and clip.x >= ccx0 and clip.y >= ccy0 and clip.z <= ccx1 and clip.w <= ccy1 then
|
if ccx0 and clip.x >= ccx0 and clip.y >= ccy0 and clip.z <= ccx1 and clip.w <= ccy1 then
|
||||||
kind = 2; seen_content = true
|
kind = 2; seen_content = true
|
||||||
@@ -926,6 +1182,8 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
|||||||
elseif seen_content then
|
elseif seen_content then
|
||||||
kind = 3
|
kind = 3
|
||||||
end
|
end
|
||||||
|
cm_kind[ci] = kind
|
||||||
|
local cfree, cex0, cey0, cex1, cey1 = cx1 > cx0 and cy1 > cy0, math.huge, math.huge, -math.huge, -math.huge
|
||||||
if cx1 > cx0 and cy1 > cy0 then
|
if cx1 > cx0 and cy1 > cy0 then
|
||||||
local tex_id = pcmd:GetTexID()
|
local tex_id = pcmd:GetTexID()
|
||||||
local t = bd.Textures[tex_id]
|
local t = bd.Textures[tex_id]
|
||||||
@@ -971,6 +1229,16 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
|||||||
bx1, by1 = ceil(max(pax, pbx, pcx) - ox) + 1, ceil(max(pay, pby, pcy) - oy) + 1
|
bx1, by1 = ceil(max(pax, pbx, pcx) - ox) + 1, ceil(max(pay, pby, pcy) - oy) + 1
|
||||||
i = i + 3
|
i = i + 3
|
||||||
end
|
end
|
||||||
|
if bx0 < cx0 or by0 < cy0 or bx1 > cx1 or by1 > cy1 then cfree = false end
|
||||||
|
if bx0 < cex0 then cex0 = bx0 end
|
||||||
|
if by0 < cey0 then cey0 = by0 end
|
||||||
|
if bx1 > cex1 then cex1 = bx1 end
|
||||||
|
if by1 > cey1 then cey1 = by1 end
|
||||||
|
if bx0 < ex0 then ex0 = bx0 end
|
||||||
|
if by0 < ey0 then ey0 = by0 end
|
||||||
|
if bx1 > ex1 then ex1 = bx1 end
|
||||||
|
if by1 > ey1 then ey1 = by1 end
|
||||||
|
if bx0 < lim_x0 or by0 < lim_y0 or bx1 > lim_x1 or by1 > lim_y1 then lim_free = false end
|
||||||
if bx0 < cx0 then bx0 = cx0 end
|
if bx0 < cx0 then bx0 = cx0 end
|
||||||
if by0 < cy0 then by0 = cy0 end
|
if by0 < cy0 then by0 = cy0 end
|
||||||
if bx1 > cx1 then bx1 = cx1 end
|
if bx1 > cx1 then bx1 = cx1 end
|
||||||
@@ -984,12 +1252,16 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if cex0 > cex1 then cex0, cey0, cex1, cey1 = 0, 0, 0, 0 end -- (no primitives)
|
||||||
|
cm_free[ci] = cfree
|
||||||
|
cm_x0[ci], cm_y0[ci], cm_x1[ci], cm_y1[ci] = cex0, cey0, cex1, cey1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
PE()
|
PE()
|
||||||
|
|
||||||
-- Split into layer kinds and place each one
|
-- Split into layer kinds and place each one
|
||||||
set = bd.Layers[draw_list]
|
set = bd.Layers[draw_list]
|
||||||
|
local list_deferred = false
|
||||||
local nk1, nk2, nk3 = 0, 0, 0
|
local nk1, nk2, nk3 = 0, 0, 0
|
||||||
local o1x, o1y, o3x, o3y = math.huge, math.huge, math.huge, math.huge
|
local o1x, o1y, o3x, o3y = math.huge, math.huge, math.huge, math.huge
|
||||||
for p = first, n do
|
for p = first, n do
|
||||||
@@ -1008,19 +1280,28 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
|||||||
end
|
end
|
||||||
if nk1 > 0 then
|
if nk1 > 0 then
|
||||||
z += 1
|
z += 1
|
||||||
DrawLayer(bd, GetLayer(bd, draw_list, 1), plist1, nk1, o1x, o1y, ox, oy, force, z)
|
if DrawLayer(bd, GetLayer(bd, draw_list, 1), plist1, nk1, o1x, o1y, ox, oy, force, z) then list_deferred = true end
|
||||||
elseif set and set[1] then HideLayer(set[1]) end
|
elseif set and set[1] then HideLayer(set[1]) end
|
||||||
if nk2 > 0 then
|
if nk2 > 0 then
|
||||||
z += 1
|
z += 1
|
||||||
DrawLayer(bd, GetLayer(bd, draw_list, 2), plist2, nk2, floor(draw_list._ScrollOriginX - ox), floor(draw_list._ScrollOriginY - oy), ox, oy, force, z,
|
if DrawLayer(bd, GetLayer(bd, draw_list, 2), plist2, nk2, floor(draw_list._ScrollOriginX - ox), floor(draw_list._ScrollOriginY - oy), ox, oy, force, z,
|
||||||
content_x0, content_y0, content_x1, content_y1)
|
content_x0, content_y0, content_x1, content_y1) then list_deferred = true end
|
||||||
elseif set and set[2] then HideLayer(set[2]) end
|
elseif set and set[2] then HideLayer(set[2]) end
|
||||||
if nk3 > 0 then
|
if nk3 > 0 then
|
||||||
z += 1
|
z += 1
|
||||||
DrawLayer(bd, GetLayer(bd, draw_list, 3), plist3, nk3, o3x, o3y, ox, oy, force, z)
|
if DrawLayer(bd, GetLayer(bd, draw_list, 3), plist3, nk3, o3x, o3y, ox, oy, force, z) then list_deferred = true end
|
||||||
elseif set and set[3] then HideLayer(set[3]) end
|
elseif set and set[3] then HideLayer(set[3]) end
|
||||||
set = bd.Layers[draw_list]
|
set = bd.Layers[draw_list]
|
||||||
if set then set.LastUsed = frame_no; set.Sig = sig end
|
-- (deferred tiles: not "unchanged" next frame even if the draw list is, so they get redrawn)
|
||||||
|
if set then
|
||||||
|
set.LastUsed = frame_no
|
||||||
|
if list_deferred then set.SnapOK = false else
|
||||||
|
Snapshot(set, draw_list, ox, oy, sw, sh)
|
||||||
|
set.SLimFree = lim_free
|
||||||
|
set.SEX0, set.SEY0, set.SEX1, set.SEY1 = ex0, ey0, ex1, ey1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if list_deferred then any_deferred = true end
|
||||||
z = PlaceOverlays(bd, draw_list, z, ox, oy)
|
z = PlaceOverlays(bd, draw_list, z, ox, oy)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1033,7 +1314,7 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
|||||||
-- Hide layers not drawn this frame; free long unused ones (closed windows, old tooltips)
|
-- Hide layers not drawn this frame; free long unused ones (closed windows, old tooltips)
|
||||||
for draw_list, set in pairs(bd.Layers) do
|
for draw_list, set in pairs(bd.Layers) do
|
||||||
if set.LastUsed ~= frame_no then
|
if set.LastUsed ~= frame_no then
|
||||||
set.Sig = nil -- hidden now: must go through the full path when shown again
|
set.SnapOK = false -- hidden now: must go through the full path when shown again
|
||||||
for kind = 1, 3 do if set[kind] then HideLayer(set[kind]) end end
|
for kind = 1, 3 do if set[kind] then HideLayer(set[kind]) end end
|
||||||
if frame_no - set.LastUsed > LAYER_KEEP_FRAMES then
|
if frame_no - set.LastUsed > LAYER_KEEP_FRAMES then
|
||||||
DestroyLayerSet(set)
|
DestroyLayerSet(set)
|
||||||
@@ -1045,6 +1326,8 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
|||||||
-- drop references so old vertex tables can be collected
|
-- drop references so old vertex tables can be collected
|
||||||
for k = n + 1, bd.LastPrimCount do pr_t[k], pr_vb[k] = nil, nil end
|
for k = n + 1, bd.LastPrimCount do pr_t[k], pr_vb[k] = nil, nil end
|
||||||
bd.LastPrimCount = n
|
bd.LastPrimCount = n
|
||||||
|
-- Tiles left for later: if the app doesn't submit a new frame, Heartbeat finishes them from this draw data
|
||||||
|
if any_deferred then bd.Stale = draw_data; bd.StaleHB = bd.HB end
|
||||||
PE()
|
PE()
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1070,7 +1353,7 @@ function ImGui_ImplRoblox_Init(parent, render_scale)
|
|||||||
local bd = {
|
local bd = {
|
||||||
Time = os.clock(), Gui = gui, Connections = {},
|
Time = os.clock(), Gui = gui, Connections = {},
|
||||||
Textures = {}, TextureCount = 0, MouseX = -1, MouseY = -1,
|
Textures = {}, TextureCount = 0, MouseX = -1, MouseY = -1,
|
||||||
Layers = {}, Overlays = {}, OverlayHolders = {}, OverlayShown = {}, FrameNo = 0, Scale = render_scale or 1, TilesActive = false, RenderRate = 60, BusyRenderRate = 0, NextRender = 0, LastPrimCount = 0, ForceRedraw = true,
|
Layers = {}, Overlays = {}, OverlayHolders = {}, OverlayShown = {}, FrameNo = 0, Scale = render_scale or 1, TilesActive = false, RenderRate = 0, BusyRenderRate = 0, RedrawBudget = 0, HB = 0, NextRender = 0, LastPrimCount = 0, ForceRedraw = true,
|
||||||
}
|
}
|
||||||
io.BackendPlatformUserData = bd
|
io.BackendPlatformUserData = bd
|
||||||
|
|
||||||
@@ -1137,7 +1420,10 @@ function ImGui_ImplRoblox_Init(parent, render_scale)
|
|||||||
if d and os.clock() + 0.002 >= bd.NextRender then
|
if d and os.clock() + 0.002 >= bd.NextRender then
|
||||||
bd.Pending = nil
|
bd.Pending = nil
|
||||||
ImGui_ImplRoblox_RenderDrawData(d)
|
ImGui_ImplRoblox_RenderDrawData(d)
|
||||||
|
elseif bd.Stale and bd.HB > bd.StaleHB then -- deferred tiles of a frame not followed by a new one
|
||||||
|
ImGui_ImplRoblox_RenderDrawData(bd.Stale)
|
||||||
end
|
end
|
||||||
|
bd.HB += 1
|
||||||
end)
|
end)
|
||||||
|
|
||||||
connect(UserInputService.WindowFocused, function() ImGui.GetIO():AddFocusEvent(true) end)
|
connect(UserInputService.WindowFocused, function() ImGui.GetIO():AddFocusEvent(true) end)
|
||||||
@@ -1200,6 +1486,7 @@ function ImGui_ImplRoblox_NewFrame()
|
|||||||
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)
|
if bd.Pending then bd.Pending = nil; table.clear(bd.Overlays) end -- skipped frame superseded (its draw lists get reset)
|
||||||
|
bd.Stale = nil
|
||||||
|
|
||||||
local screen = bd.Gui.AbsoluteSize
|
local 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)
|
||||||
@@ -1306,7 +1593,15 @@ function ImGui_ImplRoblox_NewFrame()
|
|||||||
PE()
|
PE()
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Max tile redraws per second (default 60); 0 = every frame. imgui logic still runs every frame.
|
--- Opt-in: max milliseconds per frame spent redrawing changed tiles (default 0 = no limit, every change is drawn the
|
||||||
|
--- same frame). With a budget (e.g. 1.5), tiles over it keep
|
||||||
|
--- their previous pixels and are redrawn on the following frames (each one at most a few frames late), so a big change
|
||||||
|
--- (resizing a large window, opening one, scrolling a long list) is spread over frames instead of one long frame.
|
||||||
|
function ImGui_ImplRoblox_SetRedrawBudget(ms)
|
||||||
|
ImGui_ImplRoblox_GetBackendData().RedrawBudget = (ms or 0) / 1000
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Max tile redraws per second (default 0 = every frame, limited by the redraw budget instead). imgui logic still runs every frame.
|
||||||
function ImGui_ImplRoblox_SetRenderRate(hz)
|
function ImGui_ImplRoblox_SetRenderRate(hz)
|
||||||
ImGui_ImplRoblox_GetBackendData().RenderRate = hz or 0
|
ImGui_ImplRoblox_GetBackendData().RenderRate = hz or 0
|
||||||
end
|
end
|
||||||
@@ -1336,6 +1631,7 @@ ImGui_ImplRoblox = {
|
|||||||
AddFile = ImGui_ImplRoblox_AddFile,
|
AddFile = ImGui_ImplRoblox_AddFile,
|
||||||
SetRenderScale = ImGui_ImplRoblox_SetRenderScale,
|
SetRenderScale = ImGui_ImplRoblox_SetRenderScale,
|
||||||
SetRenderRate = ImGui_ImplRoblox_SetRenderRate,
|
SetRenderRate = ImGui_ImplRoblox_SetRenderRate,
|
||||||
|
SetRedrawBudget = ImGui_ImplRoblox_SetRedrawBudget,
|
||||||
SetOverlay = ImGui_ImplRoblox_SetOverlay,
|
SetOverlay = ImGui_ImplRoblox_SetOverlay,
|
||||||
SetTextProxy = ImGui_ImplRoblox_SetTextProxy,
|
SetTextProxy = ImGui_ImplRoblox_SetTextProxy,
|
||||||
CreateTexture = ImGui_ImplRoblox_CreateTexture,
|
CreateTexture = ImGui_ImplRoblox_CreateTexture,
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
[[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"], [64949, "imstb_rectpack.lua"], [65613, "imstb_textedit.lua"], [67155, "imstb_truetype.lua"], [69943, "imstd_minstdio.lua"]]
|
[[46, "imgui.lua"], [21389, "imgui_demo.lua"], [29807, "imgui_draw.lua"], [35745, "imgui_h.lua"], [38412, "imgui_impl_roblox.lua"], [40060, "imgui_impl_roblox_fonts.lua"], [46463, "imgui_internal.lua"], [49932, "imgui_tables.lua"], [54991, "imgui_widgets.lua"], [65631, "imstb_rectpack.lua"], [66295, "imstb_textedit.lua"], [67837, "imstb_truetype.lua"], [70625, "imstd_minstdio.lua"]]
|
||||||
+5389
-4704
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
|||||||
[[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"], [57871, "imstb_rectpack.lua"], [58535, "imstb_textedit.lua"], [60077, "imstb_truetype.lua"], [62865, "imstd_minstdio.lua"]]
|
[[183, "imgui.lua"], [21526, "imgui_demo.lua"], [29944, "imgui_draw.lua"], [35882, "imgui_h.lua"], [38549, "imgui_impl_love.lua"], [39089, "imgui_internal.lua"], [42558, "imgui_tables.lua"], [47617, "imgui_widgets.lua"], [58257, "imstb_rectpack.lua"], [58921, "imstb_textedit.lua"], [60463, "imstb_truetype.lua"], [63251, "imstd_minstdio.lua"]]
|
||||||
+5019
-4631
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@ setfenv(1, setmetatable({}, {
|
|||||||
|
|
||||||
local MainScale = 1.0
|
local MainScale = 1.0
|
||||||
local RenderScale = 1.0
|
local RenderScale = 1.0
|
||||||
local RenderRate = 60
|
local RenderRate = 0 -- 0 = every frame (tile redraws are limited by the redraw budget instead)
|
||||||
local ThumbnailSize = 420
|
local ThumbnailSize = 420
|
||||||
local FontSize = 13.0
|
local FontSize = 13.0
|
||||||
local FontNames = { "Cousine-Regular", "DroidSans", "Karla-Regular", "ProggyTiny", "Roboto-Medium" }
|
local FontNames = { "Cousine-Regular", "DroidSans", "Karla-Regular", "ProggyTiny", "Roboto-Medium" }
|
||||||
|
|||||||
@@ -6373,18 +6373,26 @@ function ImGui.ItemAdd(bb, id, nav_bb_arg, extra_flags)
|
|||||||
|
|
||||||
local g = GImGui
|
local g = GImGui
|
||||||
local window = g.CurrentWindow
|
local window = g.CurrentWindow
|
||||||
|
local last_item_data, next_item_data = g.LastItemData, g.NextItemData -- [Luau] cached field reads
|
||||||
|
|
||||||
-- Set item data
|
-- Set item data
|
||||||
-- (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags.HasDisplayRect is set)
|
-- (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags.HasDisplayRect is set)
|
||||||
g.LastItemData.ID = id
|
last_item_data.ID = id
|
||||||
ImRect_Copy(g.LastItemData.Rect, bb)
|
-- [Luau] inline ImRect_Copy(g.LastItemData.Rect, bb); ImRect_Copy(g.LastItemData.NavRect, nav_bb_arg or bb)
|
||||||
ImRect_Copy(g.LastItemData.NavRect, nav_bb_arg and nav_bb_arg or bb)
|
local bb_min, bb_max = bb.Min, bb.Max
|
||||||
g.LastItemData.ItemFlags = bit32.bor(g.CurrentItemFlags, g.NextItemData.ItemFlagsSet, extra_flags)
|
local r = last_item_data.Rect
|
||||||
g.LastItemData.StatusFlags = ImGuiItemStatusFlags.None
|
r.Min.x = bb_min.x; r.Min.y = bb_min.y; r.Max.x = bb_max.x; r.Max.y = bb_max.y
|
||||||
|
local nav_bb = nav_bb_arg and nav_bb_arg or bb
|
||||||
|
r = last_item_data.NavRect
|
||||||
|
r.Min.x = nav_bb.Min.x; r.Min.y = nav_bb.Min.y; r.Max.x = nav_bb.Max.x; r.Max.y = nav_bb.Max.y
|
||||||
|
last_item_data.ItemFlags = bit32.bor(g.CurrentItemFlags, next_item_data.ItemFlagsSet, extra_flags)
|
||||||
|
last_item_data.StatusFlags = ImGuiItemStatusFlags.None
|
||||||
-- Note: we don't copy 'g.NextItemData.SelectionUserData' to an hypothetical g.LastItemData.SelectionUserData: since the former is not cleared.
|
-- Note: we don't copy 'g.NextItemData.SelectionUserData' to an hypothetical g.LastItemData.SelectionUserData: since the former is not cleared.
|
||||||
|
|
||||||
if id ~= 0 then
|
if id ~= 0 then
|
||||||
ImGui.KeepAliveID(id)
|
-- [Luau] inline ImGui.KeepAliveID(id)
|
||||||
|
if g.ActiveId == id then g.ActiveIdIsAlive = id end
|
||||||
|
if g.DeactivatedItemData.ID == id then g.DeactivatedItemData.IsAlive = true end
|
||||||
|
|
||||||
-- Directional navigation processing
|
-- Directional navigation processing
|
||||||
-- Runs prior to clipping early-out
|
-- Runs prior to clipping early-out
|
||||||
@@ -6396,7 +6404,7 @@ function ImGui.ItemAdd(bb, id, nav_bb_arg, extra_flags)
|
|||||||
-- to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
|
-- to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
|
||||||
-- We intentionally don't check if g.NavWindow ~= nil because g.NavAnyRequest should only be set when it is non nil.
|
-- We intentionally don't check if g.NavWindow ~= nil because g.NavAnyRequest should only be set when it is non nil.
|
||||||
-- If we crash on a nil g.NavWindow we need to fix the bug elsewhere.
|
-- If we crash on a nil g.NavWindow we need to fix the bug elsewhere.
|
||||||
if bit32.band(g.LastItemData.ItemFlags, ImGuiItemFlags.NoNav) == 0 then
|
if bit32.band(last_item_data.ItemFlags, ImGuiItemFlags.NoNav) == 0 then
|
||||||
-- FIXME-NAV: investigate changing the window tests into a simple 'if (g.NavFocusScopeId == g.CurrentFocusScopeId)' test.
|
-- FIXME-NAV: investigate changing the window tests into a simple 'if (g.NavFocusScopeId == g.CurrentFocusScopeId)' test.
|
||||||
window.DC.NavLayersActiveMaskNext = bit32.bor(window.DC.NavLayersActiveMaskNext, bit32.lshift(1, window.DC.NavLayerCurrent))
|
window.DC.NavLayersActiveMaskNext = bit32.bor(window.DC.NavLayersActiveMaskNext, bit32.lshift(1, window.DC.NavLayerCurrent))
|
||||||
|
|
||||||
@@ -6409,14 +6417,14 @@ function ImGui.ItemAdd(bb, id, nav_bb_arg, extra_flags)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if bit32.band(g.NextItemData.HasFlags, ImGuiNextItemDataFlags.HasShortcut) ~= 0 then
|
if bit32.band(next_item_data.HasFlags, ImGuiNextItemDataFlags.HasShortcut) ~= 0 then
|
||||||
ImGui.ItemHandleShortcut(id)
|
ImGui.ItemHandleShortcut(id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Lightweight clear of SetNextItemXXX data.
|
-- Lightweight clear of SetNextItemXXX data.
|
||||||
g.NextItemData.HasFlags = ImGuiNextItemDataFlags.None
|
next_item_data.HasFlags = ImGuiNextItemDataFlags.None
|
||||||
g.NextItemData.ItemFlagsSet = ImGuiItemFlags.None
|
next_item_data.ItemFlagsSet = ImGuiItemFlags.None
|
||||||
|
|
||||||
-- Clipping test
|
-- Clipping test
|
||||||
-- (this is an inline copy of IsClippedEx() so we can reuse the is_rect_visible value, otherwise we'd do 'if (IsClippedEx(bb, id)) return false')
|
-- (this is an inline copy of IsClippedEx() so we can reuse the is_rect_visible value, otherwise we'd do 'if (IsClippedEx(bb, id)) return false')
|
||||||
@@ -6435,11 +6443,11 @@ function ImGui.ItemAdd(bb, id, nav_bb_arg, extra_flags)
|
|||||||
|
|
||||||
-- We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
|
-- We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
|
||||||
if is_rect_visible then
|
if is_rect_visible then
|
||||||
g.LastItemData.StatusFlags = bit32.bor(g.LastItemData.StatusFlags, ImGuiItemStatusFlags.Visible)
|
last_item_data.StatusFlags = bit32.bor(last_item_data.StatusFlags, ImGuiItemStatusFlags.Visible)
|
||||||
end
|
end
|
||||||
|
|
||||||
if ImGui.IsMouseHoveringRect(bb.Min, bb.Max) then
|
if ImGui.IsMouseHoveringRect(bb.Min, bb.Max) then
|
||||||
g.LastItemData.StatusFlags = bit32.bor(g.LastItemData.StatusFlags, ImGuiItemStatusFlags.HoveredRect)
|
last_item_data.StatusFlags = bit32.bor(last_item_data.StatusFlags, ImGuiItemStatusFlags.HoveredRect)
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@@ -6485,7 +6493,7 @@ function ImGui.ItemSize(size_or_bb, text_baseline_y)
|
|||||||
if text_baseline_y == nil then text_baseline_y = -1.0 end
|
if text_baseline_y == nil then text_baseline_y = -1.0 end
|
||||||
|
|
||||||
local size_x, size_y
|
local size_x, size_y
|
||||||
local bb_min = size_or_bb.Min
|
local bb_min = rawget(size_or_bb, "Min") -- [Luau] rawget: `.Min` on an ImVec2 would go through IM_VEC2.__index
|
||||||
if bb_min then
|
if bb_min then
|
||||||
--- @cast size_or_bb ImRect
|
--- @cast size_or_bb ImRect
|
||||||
size_x = size_or_bb.Max.x - bb_min.x; size_y = size_or_bb.Max.y - bb_min.y
|
size_x = size_or_bb.Max.x - bb_min.x; size_y = size_or_bb.Max.y - bb_min.y
|
||||||
@@ -6566,18 +6574,19 @@ function ImGui.SameLine(offset_from_start_x, spacing_w)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local dc = window.DC -- [Luau] cached field read
|
||||||
if offset_from_start_x ~= 0.0 then
|
if offset_from_start_x ~= 0.0 then
|
||||||
if spacing_w < 0.0 then spacing_w = 0.0 end
|
if spacing_w < 0.0 then spacing_w = 0.0 end
|
||||||
window.DC.CursorPos.x = window.Pos.x - window.Scroll.x + offset_from_start_x + spacing_w + window.DC.GroupOffset.x + window.DC.ColumnsOffset.x
|
dc.CursorPos.x = window.Pos.x - window.Scroll.x + offset_from_start_x + spacing_w + dc.GroupOffset.x + dc.ColumnsOffset.x
|
||||||
window.DC.CursorPos.y = window.DC.CursorPosPrevLine.y
|
dc.CursorPos.y = dc.CursorPosPrevLine.y
|
||||||
else
|
else
|
||||||
if spacing_w < 0.0 then spacing_w = g.Style.ItemSpacing.x end
|
if spacing_w < 0.0 then spacing_w = g.Style.ItemSpacing.x end
|
||||||
window.DC.CursorPos.x = window.DC.CursorPosPrevLine.x + spacing_w
|
dc.CursorPos.x = dc.CursorPosPrevLine.x + spacing_w
|
||||||
window.DC.CursorPos.y = window.DC.CursorPosPrevLine.y
|
dc.CursorPos.y = dc.CursorPosPrevLine.y
|
||||||
end
|
end
|
||||||
ImVec2_Copy(window.DC.CurrLineSize, window.DC.PrevLineSize)
|
dc.CurrLineSize.x = dc.PrevLineSize.x; dc.CurrLineSize.y = dc.PrevLineSize.y -- [Luau] inline ImVec2_Copy()
|
||||||
window.DC.CurrLineTextBaseOffset = window.DC.PrevLineTextBaseOffset
|
dc.CurrLineTextBaseOffset = dc.PrevLineTextBaseOffset
|
||||||
window.DC.IsSameLine = true
|
dc.IsSameLine = true
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param indent_w? float
|
--- @param indent_w? float
|
||||||
@@ -6625,7 +6634,10 @@ end
|
|||||||
function ImGui.PushItemWidth(item_width)
|
function ImGui.PushItemWidth(item_width)
|
||||||
local g = GImGui
|
local g = GImGui
|
||||||
local window = g.CurrentWindow
|
local window = g.CurrentWindow
|
||||||
window.DC.ItemWidthStack:push_back(window.DC.ItemWidth) -- Backup current width
|
-- [Luau] inline window.DC.ItemWidthStack:push_back(window.DC.ItemWidth) -- Backup current width
|
||||||
|
local stack = window.DC.ItemWidthStack
|
||||||
|
local n = stack.Size + 1
|
||||||
|
if n > stack.Capacity then stack:push_back(window.DC.ItemWidth) else stack.Data[n] = window.DC.ItemWidth; stack.Size = n end
|
||||||
window.DC.ItemWidth = (item_width == 0.0) and window.DC.ItemWidthDefault or item_width
|
window.DC.ItemWidth = (item_width == 0.0) and window.DC.ItemWidthDefault or item_width
|
||||||
g.NextItemData.HasFlags = bit32.band(g.NextItemData.HasFlags, bit32.bnot(ImGuiNextItemDataFlags.HasWidth))
|
g.NextItemData.HasFlags = bit32.band(g.NextItemData.HasFlags, bit32.bnot(ImGuiNextItemDataFlags.HasWidth))
|
||||||
end
|
end
|
||||||
@@ -6637,8 +6649,9 @@ function ImGui.PopItemWidth()
|
|||||||
IM_ASSERT_USER_ERROR(false, "Calling PopItemWidth() too many times!")
|
IM_ASSERT_USER_ERROR(false, "Calling PopItemWidth() too many times!")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
window.DC.ItemWidth = window.DC.ItemWidthStack:back()
|
local stack = window.DC.ItemWidthStack
|
||||||
window.DC.ItemWidthStack:pop_back()
|
window.DC.ItemWidth = stack.Data[stack.Size] -- [Luau] inline back()/pop_back()
|
||||||
|
stack.Size = stack.Size - 1
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param components int
|
--- @param components int
|
||||||
@@ -6909,8 +6922,10 @@ function ImGui.CalcItemWidth()
|
|||||||
end
|
end
|
||||||
|
|
||||||
if w < 0.0 then
|
if w < 0.0 then
|
||||||
local region_avail_x = ImGui.GetContentRegionAvail().x
|
-- [Luau] scalar GetContentRegionAvail().x, no temporary ImVec2
|
||||||
w = ImMax(1.0, region_avail_x + w)
|
local mx = (window.DC.CurrentColumns or g.CurrentTable) and window.WorkRect.Max or window.ContentRegionRect.Max
|
||||||
|
local region_avail_x = mx.x - window.DC.CursorPos.x
|
||||||
|
w = math.max(1.0, region_avail_x + w)
|
||||||
end
|
end
|
||||||
|
|
||||||
w = IM_TRUNC(w)
|
w = IM_TRUNC(w)
|
||||||
@@ -6926,21 +6941,25 @@ end
|
|||||||
--- @param default_h float
|
--- @param default_h float
|
||||||
--- @return ImVec2
|
--- @return ImVec2
|
||||||
function ImGui.CalcItemSize(size, default_w, default_h)
|
function ImGui.CalcItemSize(size, default_w, default_h)
|
||||||
local avail
|
local avail_x, avail_y
|
||||||
if size.x < 0.0 or size.y < 0.0 then
|
if size.x < 0.0 or size.y < 0.0 then
|
||||||
avail = ImGui.GetContentRegionAvail()
|
-- [Luau] scalar GetContentRegionAvail(), no temporary ImVec2
|
||||||
|
local g = GImGui
|
||||||
|
local window = g.CurrentWindow
|
||||||
|
local mx = (window.DC.CurrentColumns or g.CurrentTable) and window.WorkRect.Max or window.ContentRegionRect.Max
|
||||||
|
avail_x = mx.x - window.DC.CursorPos.x; avail_y = mx.y - window.DC.CursorPos.y
|
||||||
end
|
end
|
||||||
|
|
||||||
if size.x == 0.0 then
|
if size.x == 0.0 then
|
||||||
size.x = default_w
|
size.x = default_w
|
||||||
elseif size.x < 0.0 then
|
elseif size.x < 0.0 then
|
||||||
size.x = ImMax(4.0, avail.x + size.x) -- <-- size.x is negative here so we are subtracting
|
size.x = math.max(4.0, avail_x + size.x) -- <-- size.x is negative here so we are subtracting
|
||||||
end
|
end
|
||||||
|
|
||||||
if size.y == 0.0 then
|
if size.y == 0.0 then
|
||||||
size.y = default_h
|
size.y = default_h
|
||||||
elseif size.y < 0.0 then
|
elseif size.y < 0.0 then
|
||||||
size.y = ImMax(4.0, avail.y + size.y) -- <-- size.y is negative here so we are subtracting
|
size.y = math.max(4.0, avail_y + size.y) -- <-- size.y is negative here so we are subtracting
|
||||||
end
|
end
|
||||||
|
|
||||||
return size
|
return size
|
||||||
@@ -7481,7 +7500,10 @@ function ImGui.PushID(str_id)
|
|||||||
local g = GImGui
|
local g = GImGui
|
||||||
local window = g.CurrentWindow
|
local window = g.CurrentWindow
|
||||||
local id = window:GetID(str_id)
|
local id = window:GetID(str_id)
|
||||||
window.IDStack:push_back(id)
|
-- [Luau] inline window.IDStack:push_back(id)
|
||||||
|
local stack = window.IDStack
|
||||||
|
local n = stack.Size + 1
|
||||||
|
if n > stack.Capacity then stack:push_back(id) else stack.Data[n] = id; stack.Size = n end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Push a given id value ignoring the ID stack as a seed.
|
-- Push a given id value ignoring the ID stack as a seed.
|
||||||
@@ -7492,14 +7514,18 @@ function ImGui.PushOverrideID(id)
|
|||||||
if g.DebugHookIdInfoId == id and id ~= 0 then
|
if g.DebugHookIdInfoId == id and id ~= 0 then
|
||||||
ImGui.DebugHookIdInfo(id, ImGuiDataType.ID, nil)
|
ImGui.DebugHookIdInfo(id, ImGuiDataType.ID, nil)
|
||||||
end
|
end
|
||||||
window.IDStack:push_back(id)
|
-- [Luau] inline window.IDStack:push_back(id)
|
||||||
|
local stack = window.IDStack
|
||||||
|
local n = stack.Size + 1
|
||||||
|
if n > stack.Capacity then stack:push_back(id) else stack.Data[n] = id; stack.Size = n end
|
||||||
end
|
end
|
||||||
|
|
||||||
@native
|
@native
|
||||||
function ImGui.PopID()
|
function ImGui.PopID()
|
||||||
local window = GImGui.CurrentWindow
|
local window = GImGui.CurrentWindow
|
||||||
IM_ASSERT_USER_ERROR_RET(window.IDStack.Size > 1, "Calling PopID() too many times!")
|
local stack = window.IDStack
|
||||||
window.IDStack:pop_back()
|
IM_ASSERT_USER_ERROR_RET(stack.Size > 1, "Calling PopID() too many times!")
|
||||||
|
stack.Size = stack.Size - 1 -- [Luau] inline stack:pop_back()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- This is one of the very rare legacy case where we use ImGuiWindow methods,
|
-- This is one of the very rare legacy case where we use ImGuiWindow methods,
|
||||||
@@ -7508,7 +7534,8 @@ end
|
|||||||
--- @return ImGuiID
|
--- @return ImGuiID
|
||||||
@native
|
@native
|
||||||
function MT.ImGuiWindow:GetID(id)
|
function MT.ImGuiWindow:GetID(id)
|
||||||
local seed = self.IDStack:back()
|
local stack = self.IDStack
|
||||||
|
local seed = stack.Data[stack.Size] -- [Luau] inline self.IDStack:back()
|
||||||
|
|
||||||
local h
|
local h
|
||||||
if type(id) == "string" then
|
if type(id) == "string" then
|
||||||
@@ -7904,7 +7931,9 @@ end
|
|||||||
function ImGui.IsClippedEx(bb, id)
|
function ImGui.IsClippedEx(bb, id)
|
||||||
local g = GImGui
|
local g = GImGui
|
||||||
local window = g.CurrentWindow
|
local window = g.CurrentWindow
|
||||||
if not bb:Overlaps(window.ClipRect) then
|
-- [Luau] inline bb:Overlaps(window.ClipRect)
|
||||||
|
local bmin, bmax, cr = bb.Min, bb.Max, window.ClipRect
|
||||||
|
if not (bmin.x <= cr.Max.x and bmax.x >= cr.Min.x and bmin.y <= cr.Max.y and bmax.y >= cr.Min.y) then
|
||||||
if id == 0 or (id ~= g.ActiveId and id ~= g.ActiveIdPreviousFrame and id ~= g.NavId and id ~= g.NavActivateId) then
|
if id == 0 or (id ~= g.ActiveId and id ~= g.ActiveIdPreviousFrame and id ~= g.NavId and id ~= g.NavActivateId) then
|
||||||
if not g.ItemUnclipByLog then
|
if not g.ItemUnclipByLog then
|
||||||
return true
|
return true
|
||||||
@@ -10011,7 +10040,7 @@ function ImGui.CalcWrapWidthForPos(pos, wrap_pos_x)
|
|||||||
wrap_pos_x = wrap_pos_x + window.Pos.x - window.Scroll.x -- wrap_pos_x is provided is window local space
|
wrap_pos_x = wrap_pos_x + window.Pos.x - window.Scroll.x -- wrap_pos_x is provided is window local space
|
||||||
end
|
end
|
||||||
|
|
||||||
return ImMax(wrap_pos_x - pos.x, 1.0)
|
return math.max(wrap_pos_x - pos.x, 1.0)
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
@@ -10033,7 +10062,7 @@ function ImGui.FindRenderedTextEnd(text, text_end)
|
|||||||
end
|
end
|
||||||
|
|
||||||
text_display_end = ImMemchr(text, "##", 1) or text_end
|
text_display_end = ImMemchr(text, "##", 1) or text_end
|
||||||
text_display_end = ImMin(text_display_end, text_end)
|
text_display_end = math.min(text_display_end, text_end) -- [Luau] scalar ImMin()
|
||||||
|
|
||||||
return text_display_end
|
return text_display_end
|
||||||
end
|
end
|
||||||
@@ -10104,13 +10133,17 @@ end
|
|||||||
--- @param text_size_if_known? ImVec2
|
--- @param text_size_if_known? ImVec2
|
||||||
--- @param align? ImVec2
|
--- @param align? ImVec2
|
||||||
--- @param clip_rect? ImRect
|
--- @param clip_rect? ImRect
|
||||||
|
local RenderTextClipped_align0 = ImVec2(0, 0) -- [Luau] shared read-only default
|
||||||
|
local RenderTextClippedEx_pos = ImVec2() -- [Luau] scratch: AddText() only reads pos
|
||||||
|
local RenderTextClippedEx_fine_clip = ImVec4() -- [Luau] scratch: AddText() only reads it
|
||||||
|
|
||||||
@native
|
@native
|
||||||
function ImGui.RenderTextClippedEx(draw_list, pos_min, pos_max, text, text_begin, text_display_end, text_size_if_known, align, clip_rect)
|
function ImGui.RenderTextClippedEx(draw_list, pos_min, pos_max, text, text_begin, text_display_end, text_size_if_known, align, clip_rect)
|
||||||
if not align then align = ImVec2(0, 0) end
|
if not align then align = RenderTextClipped_align0 end
|
||||||
|
|
||||||
-- Perform CPU side clipping for single clipped element to avoid using scissor state
|
-- Perform CPU side clipping for single clipped element to avoid using scissor state
|
||||||
local pos = ImVec2()
|
local pos = RenderTextClippedEx_pos
|
||||||
ImVec2_Copy(pos, pos_min)
|
pos.x = pos_min.x; pos.y = pos_min.y
|
||||||
|
|
||||||
local text_size = text_size_if_known or ImGui.CalcTextSize(text, text_display_end, false, 0.0)
|
local text_size = text_size_if_known or ImGui.CalcTextSize(text, text_display_end, false, 0.0)
|
||||||
|
|
||||||
@@ -10122,13 +10155,13 @@ function ImGui.RenderTextClippedEx(draw_list, pos_min, pos_max, text, text_begin
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
|
-- Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
|
||||||
if (align.x > 0.0) then pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x) end
|
if (align.x > 0.0) then pos.x = math.max(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x) end
|
||||||
if (align.y > 0.0) then pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y) end
|
if (align.y > 0.0) then pos.y = math.max(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y) end
|
||||||
|
|
||||||
-- Render
|
-- Render
|
||||||
local g = GImGui
|
|
||||||
if (need_clipping) then
|
if (need_clipping) then
|
||||||
local fine_clip_rect = ImVec4(clip_min.x, clip_min.y, clip_max.x, clip_max.y)
|
local fine_clip_rect = RenderTextClippedEx_fine_clip
|
||||||
|
fine_clip_rect.x = clip_min.x; fine_clip_rect.y = clip_min.y; fine_clip_rect.z = clip_max.x; fine_clip_rect.w = clip_max.y
|
||||||
draw_list:AddText(nil, 0.0, pos, ImGui.GetColorU32(ImGuiCol.Text), text, text_begin, text_display_end, 0.0, fine_clip_rect)
|
draw_list:AddText(nil, 0.0, pos, ImGui.GetColorU32(ImGuiCol.Text), text, text_begin, text_display_end, 0.0, fine_clip_rect)
|
||||||
else
|
else
|
||||||
draw_list:AddText(nil, 0.0, pos, ImGui.GetColorU32(ImGuiCol.Text), text, text_begin, text_display_end, 0.0, nil)
|
draw_list:AddText(nil, 0.0, pos, ImGui.GetColorU32(ImGuiCol.Text), text, text_begin, text_display_end, 0.0, nil)
|
||||||
@@ -10143,7 +10176,7 @@ end
|
|||||||
--- @param align? ImVec2
|
--- @param align? ImVec2
|
||||||
--- @param clip_rect? ImRect
|
--- @param clip_rect? ImRect
|
||||||
function ImGui.RenderTextClipped(pos_min, pos_max, text, text_end, text_size_if_known, align, clip_rect)
|
function ImGui.RenderTextClipped(pos_min, pos_max, text, text_end, text_size_if_known, align, clip_rect)
|
||||||
if not align then align = ImVec2(0, 0) end
|
if not align then align = RenderTextClipped_align0 end
|
||||||
|
|
||||||
-- Hide anything after a '##' string
|
-- Hide anything after a '##' string
|
||||||
local text_display_end = ImGui.FindRenderedTextEnd(text, text_end)
|
local text_display_end = ImGui.FindRenderedTextEnd(text, text_end)
|
||||||
@@ -13548,15 +13581,21 @@ end
|
|||||||
--- @param intersect_with_current_clip_rect? bool
|
--- @param intersect_with_current_clip_rect? bool
|
||||||
@native
|
@native
|
||||||
function ImGui.PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect)
|
function ImGui.PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect)
|
||||||
local window = ImGui.GetCurrentWindow()
|
local window = GImGui.CurrentWindow -- [Luau] inline GetCurrentWindow()
|
||||||
window.DrawList:PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect)
|
window.WriteAccessed = true
|
||||||
ImRect_CopyFromV4(window.ClipRect, window.DrawList._ClipRectStack:back())
|
local draw_list = window.DrawList
|
||||||
|
draw_list:PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect)
|
||||||
|
local stack = draw_list._ClipRectStack
|
||||||
|
ImRect_CopyFromV4(window.ClipRect, stack.Data[stack.Size]) -- [Luau] inline back()
|
||||||
end
|
end
|
||||||
|
|
||||||
function ImGui.PopClipRect()
|
function ImGui.PopClipRect()
|
||||||
local window = ImGui.GetCurrentWindow()
|
local window = GImGui.CurrentWindow -- [Luau] inline GetCurrentWindow()
|
||||||
window.DrawList:PopClipRect()
|
window.WriteAccessed = true
|
||||||
ImRect_CopyFromV4(window.ClipRect, window.DrawList._ClipRectStack:back())
|
local draw_list = window.DrawList
|
||||||
|
draw_list:PopClipRect()
|
||||||
|
local stack = draw_list._ClipRectStack
|
||||||
|
ImRect_CopyFromV4(window.ClipRect, stack.Data[stack.Size]) -- [Luau] inline back()
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param viewport ImGuiViewportP
|
--- @param viewport ImGuiViewportP
|
||||||
@@ -14201,12 +14240,12 @@ function ImGui.CalcTextSizeEx(text, text_begin, text_end, hide_text_after_double
|
|||||||
if text == "" or (text_end and text_end <= text_begin) then
|
if text == "" or (text_end and text_end <= text_begin) then
|
||||||
return ImVec2(0.0, font_size)
|
return ImVec2(0.0, font_size)
|
||||||
end
|
end
|
||||||
local text_size = font:CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_begin, text_display_end, nil)
|
local text_size = ImFontCalcTextSizeEx(font, font_size, FLT_MAX, wrap_width, text, text_begin, text_display_end, text_display_end, nil, ImDrawTextFlags.None) -- [Luau] inline font:CalcTextSizeA()
|
||||||
|
|
||||||
-- Round
|
-- Round
|
||||||
-- (see 7b0bf230, 4622fa4b6, #791 for details about this.)
|
-- (see 7b0bf230, 4622fa4b6, #791 for details about this.)
|
||||||
-- FIXME: Add a way to disable this.
|
-- FIXME: Add a way to disable this.
|
||||||
text_size.x = ImCeilFast(text_size.x)
|
text_size.x = math.ceil(text_size.x) -- [Luau] == ImCeilFast() for all finite values, builtin
|
||||||
|
|
||||||
return text_size
|
return text_size
|
||||||
end
|
end
|
||||||
@@ -14442,7 +14481,13 @@ function ImGui.GetColorU32(col, alpha_mul, col_is_u32)
|
|||||||
else --- @cast col ImVec4
|
else --- @cast col ImVec4
|
||||||
w = col.w * style.Alpha
|
w = col.w * style.Alpha
|
||||||
end
|
end
|
||||||
return bit32.bor(F32ToU8Sat(col.x), bit32.lshift(F32ToU8Sat(col.y), 8), bit32.lshift(F32ToU8Sat(col.z), 16), bit32.lshift(F32ToU8Sat(w), 24))
|
-- [Luau] inline F32ToU8Sat() x4
|
||||||
|
local x, y, z = col.x, col.y, col.z
|
||||||
|
if x < 0.0 then x = 0.0 elseif x > 1.0 then x = 1.0 end
|
||||||
|
if y < 0.0 then y = 0.0 elseif y > 1.0 then y = 1.0 end
|
||||||
|
if z < 0.0 then z = 0.0 elseif z > 1.0 then z = 1.0 end
|
||||||
|
if w < 0.0 then w = 0.0 elseif w > 1.0 then w = 1.0 end
|
||||||
|
return bit32.bor(math.floor(x * 255.0 + 0.5), bit32.lshift(math.floor(y * 255.0 + 0.5), 8), bit32.lshift(math.floor(z * 255.0 + 0.5), 16), bit32.lshift(math.floor(w * 255.0 + 0.5), 24))
|
||||||
else
|
else
|
||||||
--- @cast col ImU32
|
--- @cast col ImU32
|
||||||
alpha_mul = alpha_mul * style.Alpha
|
alpha_mul = alpha_mul * style.Alpha
|
||||||
@@ -14463,7 +14508,10 @@ end
|
|||||||
function ImGui.PushStyleColor(idx, col)
|
function ImGui.PushStyleColor(idx, col)
|
||||||
local g = GImGui
|
local g = GImGui
|
||||||
local backup = ImGuiColorMod(idx, g.Style.Colors[idx])
|
local backup = ImGuiColorMod(idx, g.Style.Colors[idx])
|
||||||
g.ColorStack:push_back(backup)
|
-- [Luau] inline g.ColorStack:push_back(backup)
|
||||||
|
local stack = g.ColorStack
|
||||||
|
local n = stack.Size + 1
|
||||||
|
if n > stack.Capacity then stack:push_back(backup) else stack.Data[n] = backup; stack.Size = n end
|
||||||
if g.DebugFlashStyleColorIdx ~= idx then
|
if g.DebugFlashStyleColorIdx ~= idx then
|
||||||
if type(col) == "number" then
|
if type(col) == "number" then
|
||||||
ImGui.ColorConvertU32ToFloat4(col, g.Style.Colors[idx])
|
ImGui.ColorConvertU32ToFloat4(col, g.Style.Colors[idx])
|
||||||
@@ -14483,10 +14531,11 @@ function ImGui.PopStyleColor(count)
|
|||||||
count = g.ColorStack.Size
|
count = g.ColorStack.Size
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local stack = g.ColorStack
|
||||||
while count > 0 do
|
while count > 0 do
|
||||||
local backup = g.ColorStack:back()
|
local backup = stack.Data[stack.Size] -- [Luau] inline back()/pop_back()
|
||||||
ImVec4_Copy(g.Style.Colors[backup.Col], backup.BackupValue)
|
ImVec4_Copy(g.Style.Colors[backup.Col], backup.BackupValue)
|
||||||
g.ColorStack:pop_back()
|
stack.Size = stack.Size - 1
|
||||||
count = count - 1
|
count = count - 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -14596,8 +14645,9 @@ function ImGui.PopStyleVar(count)
|
|||||||
IM_ASSERT_USER_ERROR(0, "Calling PopStyleVar() too many times!")
|
IM_ASSERT_USER_ERROR(0, "Calling PopStyleVar() too many times!")
|
||||||
count = g.StyleVarStack.Size
|
count = g.StyleVarStack.Size
|
||||||
end
|
end
|
||||||
|
local stack = g.StyleVarStack
|
||||||
while count > 0 do
|
while count > 0 do
|
||||||
local backup = g.StyleVarStack:back()
|
local backup = stack.Data[stack.Size] -- [Luau] inline back()/pop_back()
|
||||||
local var_info = ImGui.GetStyleVarInfo(backup.VarIdx)
|
local var_info = ImGui.GetStyleVarInfo(backup.VarIdx)
|
||||||
local data = g.Style[var_info.Key]
|
local data = g.Style[var_info.Key]
|
||||||
if (var_info.DataType == ImGuiDataType.Float and var_info.Count == 1) then
|
if (var_info.DataType == ImGuiDataType.Float and var_info.Count == 1) then
|
||||||
@@ -14605,7 +14655,7 @@ function ImGui.PopStyleVar(count)
|
|||||||
elseif (var_info.DataType == ImGuiDataType.Float and var_info.Count == 2) then
|
elseif (var_info.DataType == ImGuiDataType.Float and var_info.Count == 2) then
|
||||||
data.x = backup.BackupVal[1]; data.y = backup.BackupVal[2]
|
data.x = backup.BackupVal[1]; data.y = backup.BackupVal[2]
|
||||||
end
|
end
|
||||||
g.StyleVarStack:pop_back()
|
stack.Size = stack.Size - 1
|
||||||
count = count - 1
|
count = count - 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -14615,16 +14665,21 @@ end
|
|||||||
function ImGui.PushTextWrapPos(wrap_local_pos_x)
|
function ImGui.PushTextWrapPos(wrap_local_pos_x)
|
||||||
local g = GImGui
|
local g = GImGui
|
||||||
local window = g.CurrentWindow
|
local window = g.CurrentWindow
|
||||||
window.DC.TextWrapPosStack:push_back(window.DC.TextWrapPos)
|
-- [Luau] inline window.DC.TextWrapPosStack:push_back(window.DC.TextWrapPos)
|
||||||
window.DC.TextWrapPos = wrap_local_pos_x
|
local dc = window.DC
|
||||||
|
local stack = dc.TextWrapPosStack
|
||||||
|
local n = stack.Size + 1
|
||||||
|
if n > stack.Capacity then stack:push_back(dc.TextWrapPos) else stack.Data[n] = dc.TextWrapPos; stack.Size = n end
|
||||||
|
dc.TextWrapPos = wrap_local_pos_x
|
||||||
end
|
end
|
||||||
|
|
||||||
function ImGui.PopTextWrapPos()
|
function ImGui.PopTextWrapPos()
|
||||||
local g = GImGui
|
local g = GImGui
|
||||||
local window = g.CurrentWindow
|
local window = g.CurrentWindow
|
||||||
IM_ASSERT_USER_ERROR_RET(window.DC.TextWrapPosStack.Size > 0, "Calling PopTextWrapPos() too many times!")
|
local stack = window.DC.TextWrapPosStack
|
||||||
window.DC.TextWrapPos = window.DC.TextWrapPosStack:back()
|
IM_ASSERT_USER_ERROR_RET(stack.Size > 0, "Calling PopTextWrapPos() too many times!")
|
||||||
window.DC.TextWrapPosStack:pop_back()
|
window.DC.TextWrapPos = stack.Data[stack.Size] -- [Luau] inline back()/pop_back()
|
||||||
|
stack.Size = stack.Size - 1
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param window ImGuiWindow
|
--- @param window ImGuiWindow
|
||||||
@@ -14865,7 +14920,8 @@ function CalcNextScrollFromScrollTargetAndClamp(window)
|
|||||||
ImVec2_Copy(scroll, window.Scroll)
|
ImVec2_Copy(scroll, window.Scroll)
|
||||||
local decoration_size = ImVec2(window.DecoOuterSizeX1 + window.DecoInnerSizeX1 + window.DecoOuterSizeX2, window.DecoOuterSizeY1 + window.DecoInnerSizeY1 + window.DecoOuterSizeY2)
|
local decoration_size = ImVec2(window.DecoOuterSizeX1 + window.DecoInnerSizeX1 + window.DecoOuterSizeX2, window.DecoOuterSizeY1 + window.DecoInnerSizeY1 + window.DecoOuterSizeY2)
|
||||||
|
|
||||||
for axis = ImGuiAxis.X, ImGuiAxis.Y do
|
for axis_n = ImGuiAxis.X, ImGuiAxis.Y do
|
||||||
|
local axis = (axis_n == ImGuiAxis.X) and "x" or "y" -- [Luau] named key: ImVec2[1]/[2] go through IM_VEC2.__index
|
||||||
if window.ScrollTarget[axis] < FLT_MAX then
|
if window.ScrollTarget[axis] < FLT_MAX then
|
||||||
local center_ratio = window.ScrollTargetCenterRatio[axis]
|
local center_ratio = window.ScrollTargetCenterRatio[axis]
|
||||||
local scroll_target = window.ScrollTarget[axis]
|
local scroll_target = window.ScrollTarget[axis]
|
||||||
@@ -17568,7 +17624,7 @@ end
|
|||||||
function ImGui.GetMainViewport()
|
function ImGui.GetMainViewport()
|
||||||
local g = GImGui
|
local g = GImGui
|
||||||
|
|
||||||
return g.Viewports[1]
|
return g.Viewports.Data[1] -- [Luau] .Data[]: no IM_VECTOR.__index
|
||||||
end
|
end
|
||||||
|
|
||||||
-- FIXME: This leaks access to viewports not listed in PlatformIO.Viewports[]. Problematic? (#4236)
|
-- FIXME: This leaks access to viewports not listed in PlatformIO.Viewports[]. Problematic? (#4236)
|
||||||
@@ -20026,23 +20082,34 @@ function ImGui.DebugNodeFontGlyphsForSrcMask(font, baked, src_mask)
|
|||||||
local base = 0
|
local base = 0
|
||||||
while base <= max_c do
|
while base <= max_c do
|
||||||
local count = 0
|
local count = 0
|
||||||
|
-- [Luau] inline IsGlyphLoaded() + FindGlyphNoFallback() (a loaded glyph never needs loading): 0xFFFF = IM_FONTGLYPH_INDEX_UNUSED, 0xFFFE = IM_FONTGLYPH_INDEX_NOT_FOUND
|
||||||
|
local lookup, lookup_n, glyphs = baked.IndexLookup.Data, baked.IndexLookup.Size, baked.Glyphs.Data
|
||||||
for n = 0, 255 do
|
for n = 0, 255 do
|
||||||
local c = base + n
|
local c = base + n
|
||||||
if baked:IsGlyphLoaded(c) then
|
if c < lookup_n then
|
||||||
local glyph = baked:FindGlyphNoFallback(c)
|
local i = lookup[c + 1]
|
||||||
if glyph and bit32.band(src_mask, bit32.lshift(1, glyph.SourceIdx)) ~= 0 then count = count + 1 end
|
if i ~= 0xFFFF and i ~= 0xFFFE then
|
||||||
|
local glyph = glyphs[i]
|
||||||
|
if glyph and bit32.band(src_mask, bit32.lshift(1, glyph.SourceIdx)) ~= 0 then count = count + 1 end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if count > 0 and ImGui.TreeNode(base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, (count > 1) and "glyphs" or "glyph") then
|
if count > 0 and ImGui.TreeNode(base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, (count > 1) and "glyphs" or "glyph") then
|
||||||
-- Draw a 16x16 grid of glyphs
|
-- Draw a 16x16 grid of glyphs
|
||||||
local base_pos = ImGui.GetCursorScreenPos()
|
local base_pos = ImGui.GetCursorScreenPos()
|
||||||
|
local cell_p1, cell_p2 = ImVec2(), ImVec2()
|
||||||
for n = 0, 255 do
|
for n = 0, 255 do
|
||||||
local c = base + n
|
local c = base + n
|
||||||
-- We use ImFont:RenderChar as a shortcut because we don't have UTF-8 conversion functions
|
-- We use ImFont:RenderChar as a shortcut because we don't have UTF-8 conversion functions
|
||||||
-- available here and thus cannot easily generate a zero-terminated UTF-8 encoded string.
|
-- available here and thus cannot easily generate a zero-terminated UTF-8 encoded string.
|
||||||
local cell_p1 = ImVec2(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + math.floor(n / 16) * (cell_size + cell_spacing))
|
-- [Luau] reused per cell: AddRect()/RenderChar()/IsMouseHoveringRect() only read them
|
||||||
local cell_p2 = ImVec2(cell_p1.x + cell_size, cell_p1.y + cell_size)
|
cell_p1.x = base_pos.x + (n % 16) * (cell_size + cell_spacing); cell_p1.y = base_pos.y + math.floor(n / 16) * (cell_size + cell_spacing)
|
||||||
local glyph = baked:IsGlyphLoaded(c) and baked:FindGlyphNoFallback(c) or nil
|
cell_p2.x = cell_p1.x + cell_size; cell_p2.y = cell_p1.y + cell_size
|
||||||
|
local glyph = nil -- [Luau] inline IsGlyphLoaded() + FindGlyphNoFallback(), see above
|
||||||
|
if c < baked.IndexLookup.Size then
|
||||||
|
local i = baked.IndexLookup.Data[c + 1]
|
||||||
|
if i ~= 0xFFFF and i ~= 0xFFFE then glyph = baked.Glyphs.Data[i] end
|
||||||
|
end
|
||||||
draw_list:AddRect(cell_p1, cell_p2, glyph and IM_COL32(255, 255, 255, 100) or IM_COL32(255, 255, 255, 50))
|
draw_list:AddRect(cell_p1, cell_p2, glyph and IM_COL32(255, 255, 255, 100) or IM_COL32(255, 255, 255, 50))
|
||||||
if glyph and bit32.band(src_mask, bit32.lshift(1, glyph.SourceIdx)) ~= 0 then
|
if glyph and bit32.band(src_mask, bit32.lshift(1, glyph.SourceIdx)) ~= 0 then
|
||||||
font:RenderChar(draw_list, cell_size, cell_p1, glyph_col, c)
|
font:RenderChar(draw_list, cell_size, cell_p1, glyph_col, c)
|
||||||
|
|||||||
+320
-152
@@ -2608,7 +2608,7 @@ function MT.ImFont:GetFontBaked(size, density)
|
|||||||
|
|
||||||
-- Round font size
|
-- Round font size
|
||||||
-- - ImGui.PushFont() will already round, but other paths calling GetFontBaked() directly also needs it (e.g. ImFontAtlasBuildPreloadAllGlyphRanges)
|
-- - ImGui.PushFont() will already round, but other paths calling GetFontBaked() directly also needs it (e.g. ImFontAtlasBuildPreloadAllGlyphRanges)
|
||||||
size = ImGui.GetRoundedFontSize(size)
|
size = math.floor(size + 0.5) -- [Luau] inline ImGui.GetRoundedFontSize() == IM_ROUND()
|
||||||
|
|
||||||
if density < 0.0 then
|
if density < 0.0 then
|
||||||
density = self.CurrentRasterizerDensity
|
density = self.CurrentRasterizerDensity
|
||||||
@@ -2869,7 +2869,37 @@ end
|
|||||||
--- @param wrap_width float
|
--- @param wrap_width float
|
||||||
--- @param flags ImDrawTextFlags
|
--- @param flags ImDrawTextFlags
|
||||||
--- @return int
|
--- @return int
|
||||||
|
local ImFontCalcWordWrapPositionEx_Uncached
|
||||||
|
local WRAP_MEMO_MAX = 4096
|
||||||
|
|
||||||
function ImFontCalcWordWrapPositionEx(font, size, text, pos, text_end, wrap_width, flags)
|
function ImFontCalcWordWrapPositionEx(font, size, text, pos, text_end, wrap_width, flags)
|
||||||
|
local baked = font:GetFontBaked(size)
|
||||||
|
if type(text) ~= "string" then
|
||||||
|
return ImFontCalcWordWrapPositionEx_Uncached(baked, size, text, pos, text_end, wrap_width, flags)
|
||||||
|
end
|
||||||
|
-- [Luau] memo per baked font and string: CalcTextSize() and RenderText() of wrapped text ask for the same wrap points
|
||||||
|
-- (same text/pos/width) every frame. The result only depends on these inputs and the (immutable once loaded) glyph advances.
|
||||||
|
-- ponytail: whole-memo reset at WRAP_MEMO_MAX strings, LRU if dynamic text churns it
|
||||||
|
local memo = baked._WrapMemo
|
||||||
|
if memo == nil or baked._WrapMemoN >= WRAP_MEMO_MAX then
|
||||||
|
memo = {}; baked._WrapMemo = memo; baked._WrapMemoN = 0
|
||||||
|
end
|
||||||
|
local e = memo[text]
|
||||||
|
if e == nil or e.ww ~= wrap_width or e.size ~= size or e.te ~= text_end or e.fl ~= flags then
|
||||||
|
if e == nil then baked._WrapMemoN = baked._WrapMemoN + 1 end
|
||||||
|
e = { ww = wrap_width, size = size, te = text_end, fl = flags }
|
||||||
|
memo[text] = e
|
||||||
|
else
|
||||||
|
local r = e[pos]
|
||||||
|
if r then return r end
|
||||||
|
end
|
||||||
|
local r = ImFontCalcWordWrapPositionEx_Uncached(baked, size, text, pos, text_end, wrap_width, flags)
|
||||||
|
e[pos] = r
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
@native
|
||||||
|
function ImFontCalcWordWrapPositionEx_Uncached(baked, size, text, pos, text_end, wrap_width, flags)
|
||||||
-- For references, possible wrap point marked with ^
|
-- For references, possible wrap point marked with ^
|
||||||
-- "aaa bbb, ccc,ddd. eee fff. ggg!"
|
-- "aaa bbb, ccc,ddd. eee fff. ggg!"
|
||||||
-- ^ ^ ^ ^ ^__ ^ ^
|
-- ^ ^ ^ ^ ^__ ^ ^
|
||||||
@@ -2880,7 +2910,7 @@ function ImFontCalcWordWrapPositionEx(font, size, text, pos, text_end, wrap_widt
|
|||||||
-- Cut words that cannot possibly fit within one line.
|
-- Cut words that cannot possibly fit within one line.
|
||||||
-- e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish"
|
-- e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish"
|
||||||
|
|
||||||
local baked = font:GetFontBaked(size)
|
-- [Luau] baked = font:GetFontBaked(size) is done by the caller
|
||||||
local scale = size / baked.Size
|
local scale = size / baked.Size
|
||||||
|
|
||||||
local line_width = 0.0
|
local line_width = 0.0
|
||||||
@@ -2899,10 +2929,15 @@ function ImFontCalcWordWrapPositionEx(font, size, text, pos, text_end, wrap_widt
|
|||||||
local span_width = 0.0
|
local span_width = 0.0
|
||||||
|
|
||||||
local stop = false
|
local stop = false
|
||||||
|
local text_is_str = type(text) == "string" -- [Luau] ImStrByte() inlined (once per character)
|
||||||
|
local adv_data, adv_n = baked.IndexAdvanceX.Data, baked.IndexAdvanceX.Size -- [Luau] hoisted, refreshed after a glyph load
|
||||||
|
local CLASS_BLANK, CLASS_PUNCT, CLASS_OTHER = ImWcharClass.Blank, ImWcharClass.Punct, ImWcharClass.Other
|
||||||
|
local classifier_0000_007f = g_CharClassifierIsSeparator_0000_007f
|
||||||
while s < text_end do
|
while s < text_end do
|
||||||
repeat
|
repeat
|
||||||
--- @type unsigned_int
|
--- @type unsigned_int
|
||||||
local c = ImStrByte(text, s)
|
local c
|
||||||
|
if text_is_str then c = string.byte(text, s) else c = text[s] end
|
||||||
local next_s
|
local next_s
|
||||||
if c < 0x80 then
|
if c < 0x80 then
|
||||||
next_s = s + 1
|
next_s = s + 1
|
||||||
@@ -2924,24 +2959,25 @@ function ImFontCalcWordWrapPositionEx(font, size, text, pos, text_end, wrap_widt
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Optimized inline version of 'float char_width = GetCharAdvance((ImWchar)c);'
|
-- Optimized inline version of 'float char_width = GetCharAdvance((ImWchar)c);'
|
||||||
local char_width = (c < baked.IndexAdvanceX.Size) and baked.IndexAdvanceX.Data[c + 1] or -1.0
|
local char_width = (c < adv_n) and adv_data[c + 1] or -1.0
|
||||||
if char_width < 0.0 then
|
if char_width < 0.0 then
|
||||||
char_width = BuildLoadGlyphGetAdvanceOrFallback(baked, c)
|
char_width = BuildLoadGlyphGetAdvanceOrFallback(baked, c)
|
||||||
|
adv_data, adv_n = baked.IndexAdvanceX.Data, baked.IndexAdvanceX.Size
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Classify current character
|
-- Classify current character
|
||||||
local curr_type
|
local curr_type
|
||||||
if c < 128 then
|
if c < 128 then -- [Luau] ImTextClassifierGet() inlined
|
||||||
curr_type = ImTextClassifierGet(g_CharClassifierIsSeparator_0000_007f, c)
|
curr_type = bit32.band(bit32.rshift(classifier_0000_007f[bit32.rshift(c, 4) + 1], bit32.lshift(bit32.band(c, 15), 1)), 0x03)
|
||||||
elseif (c >= 0x3000 and c < 0x3010) then
|
elseif (c >= 0x3000 and c < 0x3010) then
|
||||||
curr_type = ImTextClassifierGet(g_CharClassifierIsSeparator_3000_300f, bit32.band(c, 15))
|
curr_type = ImTextClassifierGet(g_CharClassifierIsSeparator_3000_300f, bit32.band(c, 15))
|
||||||
else
|
else
|
||||||
curr_type = ImWcharClass.Other
|
curr_type = CLASS_OTHER
|
||||||
end
|
end
|
||||||
|
|
||||||
if curr_type == ImWcharClass.Blank then
|
if curr_type == CLASS_BLANK then
|
||||||
-- End span: 'A ' or '. '
|
-- End span: 'A ' or '. '
|
||||||
if (prev_type ~= ImWcharClass.Blank and not keep_blanks) then
|
if (prev_type ~= CLASS_BLANK and not keep_blanks) then
|
||||||
span_end = s
|
span_end = s
|
||||||
line_width = line_width + span_width
|
line_width = line_width + span_width
|
||||||
span_width = 0.0
|
span_width = 0.0
|
||||||
@@ -2950,13 +2986,13 @@ function ImFontCalcWordWrapPositionEx(font, size, text, pos, text_end, wrap_widt
|
|||||||
blank_width = blank_width + char_width
|
blank_width = blank_width + char_width
|
||||||
else
|
else
|
||||||
-- End span: '.X' unless X is a digit
|
-- End span: '.X' unless X is a digit
|
||||||
if (prev_type == ImWcharClass.Punct and curr_type ~= ImWcharClass.Punct and not (c >= 48 and c <= 57)) then -- FIXME: Digit checks might be removed if allow custom separators (#8503)
|
if (prev_type == CLASS_PUNCT and curr_type ~= CLASS_PUNCT and not (c >= 48 and c <= 57)) then -- FIXME: Digit checks might be removed if allow custom separators (#8503)
|
||||||
span_end = s
|
span_end = s
|
||||||
line_width = line_width + (span_width + blank_width)
|
line_width = line_width + (span_width + blank_width)
|
||||||
blank_width = 0.0
|
blank_width = 0.0
|
||||||
span_width = 0.0
|
span_width = 0.0
|
||||||
-- End span: 'A ' or '. '
|
-- End span: 'A ' or '. '
|
||||||
elseif (prev_type == ImWcharClass.Blank and keep_blanks) then
|
elseif (prev_type == CLASS_BLANK and keep_blanks) then
|
||||||
span_end = s
|
span_end = s
|
||||||
line_width = line_width + (span_width + blank_width)
|
line_width = line_width + (span_width + blank_width)
|
||||||
blank_width = 0.0
|
blank_width = 0.0
|
||||||
@@ -3011,11 +3047,15 @@ local TEXT_SIZE_CACHE_MAX = 4096
|
|||||||
|
|
||||||
@native
|
@native
|
||||||
function ImFontCalcTextSizeEx(font, size, max_width, wrap_width, text, text_begin, text_end_display, text_end, out_offset, flags)
|
function ImFontCalcTextSizeEx(font, size, max_width, wrap_width, text, text_begin, text_end_display, text_end, out_offset, flags)
|
||||||
|
local text_is_str = type(text) == "string"
|
||||||
-- [Luau] cache for the common case (whole string, single pass, no wrap/limit): per baked font, text -> unscaled width, line count.
|
-- [Luau] cache for the common case (whole string, single pass, no wrap/limit): per baked font, text -> unscaled width, line count.
|
||||||
-- Advances of a baked font never change once loaded, so entries stay valid. ponytail: whole-cache reset at TEXT_SIZE_CACHE_MAX entries, LRU if dynamic text churns it
|
-- Advances of a baked font never change once loaded, so entries stay valid. ponytail: whole-cache reset at TEXT_SIZE_CACHE_MAX entries, LRU if dynamic text churns it
|
||||||
local cache_key = nil
|
local cache_key = nil
|
||||||
if text_begin == 1 and out_offset == nil and wrap_width <= 0.0 and max_width >= FLT_MAX and type(text) == "string"
|
local wrap_cache = nil
|
||||||
and (text_end == nil or text_end == #text + 1) and (text_end_display == nil or text_end_display == #text + 1) and (flags == nil or bit32.band(flags, ImDrawTextFlags.StopOnNewLine) == 0) then
|
local text_len1 = text_is_str and #text + 1 or -1
|
||||||
|
local cacheable = text_is_str and text_begin == 1 and out_offset == nil and max_width >= FLT_MAX
|
||||||
|
and (text_end == nil or text_end == text_len1) and (text_end_display == nil or text_end_display == text_len1) and (flags == nil or bit32.band(flags, ImDrawTextFlags.StopOnNewLine) == 0)
|
||||||
|
if cacheable and wrap_width <= 0.0 then
|
||||||
local baked = font:GetFontBaked(size)
|
local baked = font:GetFontBaked(size)
|
||||||
local cache = baked._TextSizeCache
|
local cache = baked._TextSizeCache
|
||||||
if cache == nil or baked._TextSizeCacheN >= TEXT_SIZE_CACHE_MAX then
|
if cache == nil or baked._TextSizeCacheN >= TEXT_SIZE_CACHE_MAX then
|
||||||
@@ -3023,9 +3063,25 @@ function ImFontCalcTextSizeEx(font, size, max_width, wrap_width, text, text_begi
|
|||||||
end
|
end
|
||||||
local e = cache[text]
|
local e = cache[text]
|
||||||
if e then
|
if e then
|
||||||
return ImVec2(e[1] * (size / baked.Size), e[2] * size), #text + 1
|
return ImVec2(e[1] * (size / baked.Size), e[2] * size), text_len1
|
||||||
end
|
end
|
||||||
cache_key = text
|
cache_key = text
|
||||||
|
elseif cacheable and wrap_width > 0.0 then
|
||||||
|
-- [Luau] same for wrapped text (TextWrapped() measures every line every frame): exact results per (size, wrap width, text)
|
||||||
|
local baked = font:GetFontBaked(size)
|
||||||
|
local cache = baked._TextSizeCache
|
||||||
|
if cache == nil or baked._TextSizeCacheN >= TEXT_SIZE_CACHE_MAX then
|
||||||
|
cache = {}; baked._TextSizeCache = cache; baked._TextSizeCacheN = 0
|
||||||
|
end
|
||||||
|
local by_size = cache[size]
|
||||||
|
if by_size == nil then by_size = {}; cache[size] = by_size end
|
||||||
|
local by_wrap = by_size[wrap_width]
|
||||||
|
if by_wrap == nil then by_wrap = {}; by_size[wrap_width] = by_wrap end
|
||||||
|
local e = by_wrap[text]
|
||||||
|
if e then
|
||||||
|
return ImVec2(e[1], e[2]), e[3]
|
||||||
|
end
|
||||||
|
wrap_cache = by_wrap
|
||||||
end
|
end
|
||||||
if not text_end then
|
if not text_end then
|
||||||
text_end = #text + 1 -- FIXME-OPT: Need to avoid this.
|
text_end = #text + 1 -- FIXME-OPT: Need to avoid this.
|
||||||
@@ -3046,6 +3102,7 @@ function ImFontCalcTextSizeEx(font, size, max_width, wrap_width, text, text_begi
|
|||||||
|
|
||||||
local s = text_begin
|
local s = text_begin
|
||||||
local stop = false
|
local stop = false
|
||||||
|
local adv_data, adv_n = baked.IndexAdvanceX.Data, baked.IndexAdvanceX.Size -- [Luau] hoisted, refreshed after a glyph load
|
||||||
while s < text_end_display do
|
while s < text_end_display do
|
||||||
repeat
|
repeat
|
||||||
-- Word-wrapping
|
-- Word-wrapping
|
||||||
@@ -3053,6 +3110,7 @@ function ImFontCalcTextSizeEx(font, size, max_width, wrap_width, text, text_begi
|
|||||||
-- Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
|
-- Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
|
||||||
if not word_wrap_eol then
|
if not word_wrap_eol then
|
||||||
word_wrap_eol = ImFontCalcWordWrapPositionEx(font, size, text, s, text_end, wrap_width - line_width, flags)
|
word_wrap_eol = ImFontCalcWordWrapPositionEx(font, size, text, s, text_end, wrap_width - line_width, flags)
|
||||||
|
adv_data, adv_n = baked.IndexAdvanceX.Data, baked.IndexAdvanceX.Size -- [Luau] may have loaded glyphs
|
||||||
end
|
end
|
||||||
|
|
||||||
if s >= word_wrap_eol then
|
if s >= word_wrap_eol then
|
||||||
@@ -3073,7 +3131,8 @@ function ImFontCalcTextSizeEx(font, size, max_width, wrap_width, text, text_begi
|
|||||||
|
|
||||||
-- Decode and advance source
|
-- Decode and advance source
|
||||||
local prev_s = s
|
local prev_s = s
|
||||||
local c = ImStrByte(text, s)
|
local c
|
||||||
|
if text_is_str then c = string.byte(text, s) else c = text[s] end -- [Luau] ImStrByte() inlined
|
||||||
if c < 0x80 then
|
if c < 0x80 then
|
||||||
s = s + 1
|
s = s + 1
|
||||||
else
|
else
|
||||||
@@ -3098,9 +3157,10 @@ function ImFontCalcTextSizeEx(font, size, max_width, wrap_width, text, text_begi
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Optimized inline version of 'float char_width = GetCharAdvance((ImWchar)c);'
|
-- Optimized inline version of 'float char_width = GetCharAdvance((ImWchar)c);'
|
||||||
local char_width = (c < baked.IndexAdvanceX.Size) and baked.IndexAdvanceX.Data[c + 1] or -1.0
|
local char_width = (c < adv_n) and adv_data[c + 1] or -1.0
|
||||||
if (char_width < 0.0) then
|
if (char_width < 0.0) then
|
||||||
char_width = BuildLoadGlyphGetAdvanceOrFallback(baked, c)
|
char_width = BuildLoadGlyphGetAdvanceOrFallback(baked, c)
|
||||||
|
adv_data, adv_n = baked.IndexAdvanceX.Data, baked.IndexAdvanceX.Size
|
||||||
end
|
end
|
||||||
char_width = char_width * scale
|
char_width = char_width * scale
|
||||||
|
|
||||||
@@ -3133,6 +3193,9 @@ function ImFontCalcTextSizeEx(font, size, max_width, wrap_width, text, text_begi
|
|||||||
if cache_key then
|
if cache_key then
|
||||||
baked._TextSizeCache[cache_key] = { text_size.x / scale, text_size.y / line_height }
|
baked._TextSizeCache[cache_key] = { text_size.x / scale, text_size.y / line_height }
|
||||||
baked._TextSizeCacheN = baked._TextSizeCacheN + 1
|
baked._TextSizeCacheN = baked._TextSizeCacheN + 1
|
||||||
|
elseif wrap_cache then
|
||||||
|
wrap_cache[text] = { text_size.x, text_size.y, out_remaining }
|
||||||
|
baked._TextSizeCacheN = baked._TextSizeCacheN + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
return text_size, out_remaining
|
return text_size, out_remaining
|
||||||
@@ -3155,6 +3218,8 @@ end
|
|||||||
--- @param col ImU32
|
--- @param col ImU32
|
||||||
--- @param c ImWchar
|
--- @param c ImWchar
|
||||||
--- @param cpu_fine_clip ImVec4
|
--- @param cpu_fine_clip ImVec4
|
||||||
|
local RenderChar_a, RenderChar_c, RenderChar_uv_a, RenderChar_uv_c = ImVec2(), ImVec2(), ImVec2(), ImVec2()
|
||||||
|
|
||||||
function MT.ImFont:RenderChar(draw_list, size, pos, col, c, cpu_fine_clip)
|
function MT.ImFont:RenderChar(draw_list, size, pos, col, c, cpu_fine_clip)
|
||||||
local baked = self:GetFontBaked(size)
|
local baked = self:GetFontBaked(size)
|
||||||
local glyph = baked:FindGlyph(c)
|
local glyph = baked:FindGlyph(c)
|
||||||
@@ -3202,7 +3267,10 @@ function MT.ImFont:RenderChar(draw_list, size, pos, col, c, cpu_fine_clip)
|
|||||||
if y1 >= y2 then return end
|
if y1 >= y2 then return end
|
||||||
end
|
end
|
||||||
draw_list:PrimReserve(6, 4)
|
draw_list:PrimReserve(6, 4)
|
||||||
draw_list:PrimRectUV(ImVec2(x1, y1), ImVec2(x2, y2), ImVec2(u1, v1), ImVec2(u2, v2), col)
|
-- [Luau] scratch vectors, PrimRectUV() only reads them
|
||||||
|
local a, c, uv_a, uv_c = RenderChar_a, RenderChar_c, RenderChar_uv_a, RenderChar_uv_c
|
||||||
|
a.x = x1; a.y = y1; c.x = x2; c.y = y2; uv_a.x = u1; uv_a.y = v1; uv_c.x = u2; uv_c.y = v2
|
||||||
|
draw_list:PrimRectUV(a, c, uv_a, uv_c, col)
|
||||||
end
|
end
|
||||||
|
|
||||||
function MT.ImFontAtlas:AddFont(font_cfg_in)
|
function MT.ImFontAtlas:AddFont(font_cfg_in)
|
||||||
@@ -3645,7 +3713,10 @@ function MT.ImDrawList:AddDrawCmd()
|
|||||||
draw_cmd.IdxOffset = self.IdxBuffer.Size
|
draw_cmd.IdxOffset = self.IdxBuffer.Size
|
||||||
|
|
||||||
IM_ASSERT(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z and draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w)
|
IM_ASSERT(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z and draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w)
|
||||||
self.CmdBuffer:push_back(draw_cmd)
|
-- [Luau] inline self.CmdBuffer:push_back(draw_cmd)
|
||||||
|
local cmd_buffer = self.CmdBuffer
|
||||||
|
local n = cmd_buffer.Size + 1
|
||||||
|
if n > cmd_buffer.Capacity then cmd_buffer:push_back(draw_cmd) else cmd_buffer.Data[n] = draw_cmd; cmd_buffer.Size = n end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Pop trailing draw command (used before merging or presenting to user)
|
-- Pop trailing draw command (used before merging or presenting to user)
|
||||||
@@ -3696,15 +3767,17 @@ end
|
|||||||
--- @param CMD_RHS ImDrawCmd|ImDrawCmdHeader
|
--- @param CMD_RHS ImDrawCmd|ImDrawCmdHeader
|
||||||
--- @return bool
|
--- @return bool
|
||||||
local function ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS)
|
local function ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS)
|
||||||
if CMD_LHS.ClipRect.x ~= CMD_RHS.ClipRect.x or
|
local lcr, rcr = CMD_LHS.ClipRect, CMD_RHS.ClipRect -- [Luau] cache
|
||||||
CMD_LHS.ClipRect.y ~= CMD_RHS.ClipRect.y or
|
if lcr.x ~= rcr.x or
|
||||||
CMD_LHS.ClipRect.z ~= CMD_RHS.ClipRect.z or
|
lcr.y ~= rcr.y or
|
||||||
CMD_LHS.ClipRect.w ~= CMD_RHS.ClipRect.w then
|
lcr.z ~= rcr.z or
|
||||||
|
lcr.w ~= rcr.w then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
if CMD_LHS.TexRef._TexData ~= CMD_RHS.TexRef._TexData or
|
local ltex, rtex = CMD_LHS.TexRef, CMD_RHS.TexRef
|
||||||
CMD_LHS.TexRef._TexID ~= CMD_RHS.TexRef._TexID then
|
if ltex ~= rtex and (ltex._TexData ~= rtex._TexData or
|
||||||
|
ltex._TexID ~= rtex._TexID) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -3753,10 +3826,14 @@ function MT.ImDrawList:_OnChangedClipRect()
|
|||||||
-- If current command is used with different settings we need to add a new command
|
-- If current command is used with different settings we need to add a new command
|
||||||
IM_ASSERT_PARANOID(self.CmdBuffer.Size > 0)
|
IM_ASSERT_PARANOID(self.CmdBuffer.Size > 0)
|
||||||
local curr_cmd = self.CmdBuffer.Data[self.CmdBuffer.Size]
|
local curr_cmd = self.CmdBuffer.Data[self.CmdBuffer.Size]
|
||||||
if (curr_cmd.ElemCount ~= 0 and self._CmdHeader.ClipRect ~= curr_cmd.ClipRect) then
|
if curr_cmd.ElemCount ~= 0 then
|
||||||
self:AddDrawCmd()
|
-- [Luau] inline IM_VEC4.__eq
|
||||||
|
local hcr, ccr = self._CmdHeader.ClipRect, curr_cmd.ClipRect
|
||||||
|
if hcr.x ~= ccr.x or hcr.y ~= ccr.y or hcr.z ~= ccr.z or hcr.w ~= ccr.w then
|
||||||
|
self:AddDrawCmd()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
IM_ASSERT(curr_cmd.UserCallback == nil)
|
IM_ASSERT(curr_cmd.UserCallback == nil)
|
||||||
@@ -3870,8 +3947,8 @@ function MT.ImDrawListSplitter:Split(draw_list, channels_count)
|
|||||||
if i > old_channels_count or channels[i] == nil then
|
if i > old_channels_count or channels[i] == nil then
|
||||||
channels[i] = ImDrawChannel()
|
channels[i] = ImDrawChannel()
|
||||||
else
|
else
|
||||||
channels[i]._CmdBuffer:resize(0)
|
channels[i]._CmdBuffer.Size = 0 -- [Luau] inline resize(0)
|
||||||
channels[i]._IdxBuffer:resize(0)
|
channels[i]._IdxBuffer.Size = 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -3930,16 +4007,21 @@ function MT.ImDrawListSplitter:Merge(draw_list)
|
|||||||
local dst_idx = draw_list.IdxBuffer.Data
|
local dst_idx = draw_list.IdxBuffer.Data
|
||||||
for i = 2, self._Count do
|
for i = 2, self._Count do
|
||||||
local ch = channels[i]
|
local ch = channels[i]
|
||||||
local src_cmds = ch._CmdBuffer.Data
|
-- [Luau] skip empty buffers: their .Data may be nil (reading it goes through IM_VECTOR.__index)
|
||||||
for n = 1, ch._CmdBuffer.Size do
|
if ch._CmdBuffer.Size > 0 then
|
||||||
dst_cmds[cmd_write] = src_cmds[n]
|
local src_cmds = ch._CmdBuffer.Data
|
||||||
src_cmds[n] = nil -- ownership moved to the draw list
|
for n = 1, ch._CmdBuffer.Size do
|
||||||
cmd_write = cmd_write + 1
|
dst_cmds[cmd_write] = src_cmds[n]
|
||||||
|
src_cmds[n] = nil -- ownership moved to the draw list
|
||||||
|
cmd_write = cmd_write + 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
local src_idx = ch._IdxBuffer.Data
|
if ch._IdxBuffer.Size > 0 then
|
||||||
for n = 1, ch._IdxBuffer.Size do
|
local src_idx = ch._IdxBuffer.Data
|
||||||
dst_idx[idx_write] = src_idx[n]
|
for n = 1, ch._IdxBuffer.Size do
|
||||||
idx_write = idx_write + 1
|
dst_idx[idx_write] = src_idx[n]
|
||||||
|
idx_write = idx_write + 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
draw_list._IdxWritePtr = idx_write
|
draw_list._IdxWritePtr = idx_write
|
||||||
@@ -3962,6 +4044,7 @@ end
|
|||||||
|
|
||||||
--- @param draw_list ImDrawList
|
--- @param draw_list ImDrawList
|
||||||
--- @param idx int # 0-based channel index
|
--- @param idx int # 0-based channel index
|
||||||
|
@native
|
||||||
function MT.ImDrawListSplitter:SetCurrentChannel(draw_list, idx)
|
function MT.ImDrawListSplitter:SetCurrentChannel(draw_list, idx)
|
||||||
IM_ASSERT(idx >= 0 and idx < self._Count)
|
IM_ASSERT(idx >= 0 and idx < self._Count)
|
||||||
if self._Current == idx then
|
if self._Current == idx then
|
||||||
@@ -3975,18 +4058,29 @@ function MT.ImDrawListSplitter:SetCurrentChannel(draw_list, idx)
|
|||||||
cur._IdxBuffer = draw_list.IdxBuffer
|
cur._IdxBuffer = draw_list.IdxBuffer
|
||||||
self._Current = idx
|
self._Current = idx
|
||||||
local ch = channels[idx + 1]
|
local ch = channels[idx + 1]
|
||||||
draw_list.CmdBuffer = ch._CmdBuffer
|
local cmd_buffer = ch._CmdBuffer
|
||||||
draw_list.IdxBuffer = ch._IdxBuffer
|
local idx_buffer = ch._IdxBuffer
|
||||||
draw_list._IdxWritePtr = draw_list.IdxBuffer.Size + 1
|
draw_list.CmdBuffer = cmd_buffer
|
||||||
|
draw_list.IdxBuffer = idx_buffer
|
||||||
|
draw_list._IdxWritePtr = idx_buffer.Size + 1
|
||||||
|
|
||||||
-- If current command is used with different settings we need to add a new command
|
-- If current command is used with different settings we need to add a new command
|
||||||
local curr_cmd = nil
|
-- [Luau] ImDrawCmd_HeaderCopyRef() / ImDrawCmd_HeaderCompare() inlined
|
||||||
if draw_list.CmdBuffer.Size > 0 then curr_cmd = draw_list.CmdBuffer.Data[draw_list.CmdBuffer.Size] end
|
local cmd_count = cmd_buffer.Size
|
||||||
if curr_cmd == nil then
|
if cmd_count == 0 then
|
||||||
draw_list:AddDrawCmd()
|
draw_list:AddDrawCmd()
|
||||||
elseif curr_cmd.ElemCount == 0 then
|
return
|
||||||
ImDrawCmd_HeaderCopyRef(curr_cmd, draw_list._CmdHeader) -- Copy ClipRect, TexRef, VtxOffset
|
end
|
||||||
elseif not ImDrawCmd_HeaderCompare(curr_cmd, draw_list._CmdHeader) then
|
local curr_cmd = cmd_buffer.Data[cmd_count]
|
||||||
|
local header = draw_list._CmdHeader
|
||||||
|
local hcr, ccr = header.ClipRect, curr_cmd.ClipRect
|
||||||
|
if curr_cmd.ElemCount == 0 then
|
||||||
|
ccr.x = hcr.x; ccr.y = hcr.y; ccr.z = hcr.z; ccr.w = hcr.w -- Copy ClipRect, TexRef, VtxOffset
|
||||||
|
curr_cmd.TexRef = header.TexRef
|
||||||
|
curr_cmd.VtxOffset = header.VtxOffset
|
||||||
|
elseif ccr.x ~= hcr.x or ccr.y ~= hcr.y or ccr.z ~= hcr.z or ccr.w ~= hcr.w
|
||||||
|
or curr_cmd.TexRef._TexData ~= header.TexRef._TexData or curr_cmd.TexRef._TexID ~= header.TexRef._TexID
|
||||||
|
or curr_cmd.VtxOffset ~= header.VtxOffset then
|
||||||
draw_list:AddDrawCmd()
|
draw_list:AddDrawCmd()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -4020,14 +4114,15 @@ function MT.ImDrawList:AddConvexPolyFilled(points, points_count, col)
|
|||||||
local vtx_inner_idx = self._VtxCurrentIdx
|
local vtx_inner_idx = self._VtxCurrentIdx
|
||||||
local vtx_outer_idx = self._VtxCurrentIdx + 1
|
local vtx_outer_idx = self._VtxCurrentIdx + 1
|
||||||
local idx_data = self.IdxBuffer.Data
|
local idx_data = self.IdxBuffer.Data
|
||||||
|
local idx_write_ptr = self._IdxWritePtr -- [Luau] write pointers kept in locals, stored back after the loops
|
||||||
for i = 2, points_count - 1 do
|
for i = 2, points_count - 1 do
|
||||||
local idx_write_ptr = self._IdxWritePtr
|
|
||||||
idx_data[idx_write_ptr + 0] = vtx_inner_idx; idx_data[idx_write_ptr + 1] = vtx_inner_idx + ((i - 1) * 2); idx_data[idx_write_ptr + 2] = vtx_inner_idx + (i * 2)
|
idx_data[idx_write_ptr + 0] = vtx_inner_idx; idx_data[idx_write_ptr + 1] = vtx_inner_idx + ((i - 1) * 2); idx_data[idx_write_ptr + 2] = vtx_inner_idx + (i * 2)
|
||||||
self._IdxWritePtr = idx_write_ptr + 3
|
idx_write_ptr = idx_write_ptr + 3
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Compute normals
|
-- Compute normals
|
||||||
self._Data.TempBuffer:reserve_discard(points_count)
|
local temp_buffer = self._Data.TempBuffer
|
||||||
|
if points_count > temp_buffer.Capacity then temp_buffer:reserve_discard(points_count) end -- [Luau] skip the method lookup when no growth is needed
|
||||||
local temp_normals = self._Data.TempBuffer.Data
|
local temp_normals = self._Data.TempBuffer.Data
|
||||||
|
|
||||||
local i0 = points_count
|
local i0 = points_count
|
||||||
@@ -4044,6 +4139,8 @@ function MT.ImDrawList:AddConvexPolyFilled(points, points_count, col)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local vtx_data = self.VtxBuffer.Buf
|
local vtx_data = self.VtxBuffer.Buf
|
||||||
|
local vtx_write_ptr = self._VtxWritePtr
|
||||||
|
local uv_x, uv_y = uv.x, uv.y
|
||||||
i0 = points_count
|
i0 = points_count
|
||||||
for i1 = 1, points_count do
|
for i1 = 1, points_count do
|
||||||
local n0 = temp_normals[i0]
|
local n0 = temp_normals[i0]
|
||||||
@@ -4054,18 +4151,19 @@ function MT.ImDrawList:AddConvexPolyFilled(points, points_count, col)
|
|||||||
dm_x = dm_x * AA_SIZE * 0.5
|
dm_x = dm_x * AA_SIZE * 0.5
|
||||||
dm_y = dm_y * AA_SIZE * 0.5
|
dm_y = dm_y * AA_SIZE * 0.5
|
||||||
|
|
||||||
local vtx_write_ptr = self._VtxWritePtr
|
local p1 = points[i1]
|
||||||
WV(vtx_data, vtx_write_ptr + 0, points[i1].x - dm_x, points[i1].y - dm_y, uv.x, uv.y, col)
|
WV(vtx_data, vtx_write_ptr + 0, p1.x - dm_x, p1.y - dm_y, uv_x, uv_y, col)
|
||||||
WV(vtx_data, vtx_write_ptr + 1, points[i1].x + dm_x, points[i1].y + dm_y, uv.x, uv.y, col_trans)
|
WV(vtx_data, vtx_write_ptr + 1, p1.x + dm_x, p1.y + dm_y, uv_x, uv_y, col_trans)
|
||||||
self._VtxWritePtr = vtx_write_ptr + 2
|
vtx_write_ptr = vtx_write_ptr + 2
|
||||||
|
|
||||||
local idx_write_ptr = self._IdxWritePtr
|
|
||||||
idx_data[idx_write_ptr + 0] = vtx_inner_idx + ((i1 - 1) * 2); idx_data[idx_write_ptr + 1] = vtx_inner_idx + ((i0 - 1) * 2); idx_data[idx_write_ptr + 2] = vtx_outer_idx + ((i0 - 1) * 2)
|
idx_data[idx_write_ptr + 0] = vtx_inner_idx + ((i1 - 1) * 2); idx_data[idx_write_ptr + 1] = vtx_inner_idx + ((i0 - 1) * 2); idx_data[idx_write_ptr + 2] = vtx_outer_idx + ((i0 - 1) * 2)
|
||||||
idx_data[idx_write_ptr + 3] = vtx_outer_idx + ((i0 - 1) * 2); idx_data[idx_write_ptr + 4] = vtx_outer_idx + ((i1 - 1) * 2); idx_data[idx_write_ptr + 5] = vtx_inner_idx + ((i1 - 1) * 2)
|
idx_data[idx_write_ptr + 3] = vtx_outer_idx + ((i0 - 1) * 2); idx_data[idx_write_ptr + 4] = vtx_outer_idx + ((i1 - 1) * 2); idx_data[idx_write_ptr + 5] = vtx_inner_idx + ((i1 - 1) * 2)
|
||||||
self._IdxWritePtr = idx_write_ptr + 6
|
idx_write_ptr = idx_write_ptr + 6
|
||||||
|
|
||||||
i0 = i1
|
i0 = i1
|
||||||
end
|
end
|
||||||
|
self._VtxWritePtr = vtx_write_ptr
|
||||||
|
self._IdxWritePtr = idx_write_ptr
|
||||||
self._VtxCurrentIdx = self._VtxCurrentIdx + vtx_count
|
self._VtxCurrentIdx = self._VtxCurrentIdx + vtx_count
|
||||||
else
|
else
|
||||||
local idx_count = (points_count - 2) * 3
|
local idx_count = (points_count - 2) * 3
|
||||||
@@ -4116,8 +4214,9 @@ end
|
|||||||
-- Reserve space for a number of vertices and indices.
|
-- Reserve space for a number of vertices and indices.
|
||||||
-- You must finish filling your reserved data before calling PrimReserve() again, as it may reallocate or
|
-- You must finish filling your reserved data before calling PrimReserve() again, as it may reallocate or
|
||||||
-- submit the intermediate results. PrimUnreserve() can be used to release unused allocations.
|
-- submit the intermediate results. PrimUnreserve() can be used to release unused allocations.
|
||||||
|
-- [Luau] local function (also MT.ImDrawList.PrimReserve): hot callers in this file skip the method lookup
|
||||||
@native
|
@native
|
||||||
function MT.ImDrawList:PrimReserve(idx_count, vtx_count)
|
local function PrimReserve(self, idx_count, vtx_count)
|
||||||
-- Large mesh support (when enabled)
|
-- Large mesh support (when enabled)
|
||||||
IM_ASSERT_PARANOID(idx_count >= 0 and vtx_count >= 0)
|
IM_ASSERT_PARANOID(idx_count >= 0 and vtx_count >= 0)
|
||||||
|
|
||||||
@@ -4132,14 +4231,20 @@ function MT.ImDrawList:PrimReserve(idx_count, vtx_count)
|
|||||||
local draw_cmd = self.CmdBuffer.Data[self.CmdBuffer.Size]
|
local draw_cmd = self.CmdBuffer.Data[self.CmdBuffer.Size]
|
||||||
draw_cmd.ElemCount = draw_cmd.ElemCount + idx_count
|
draw_cmd.ElemCount = draw_cmd.ElemCount + idx_count
|
||||||
|
|
||||||
local vtx_buffer_old_size = self.VtxBuffer.Size
|
-- [Luau] inline resize() when no growth is needed (IdxBuffer:resize goes through IM_VECTOR.__index)
|
||||||
self.VtxBuffer:resize(vtx_buffer_old_size + vtx_count)
|
local vtx_buffer = self.VtxBuffer
|
||||||
|
local vtx_buffer_old_size = vtx_buffer.Size
|
||||||
|
local vtx_new_size = vtx_buffer_old_size + vtx_count
|
||||||
|
if vtx_new_size > vtx_buffer.Capacity then vtx_buffer:resize(vtx_new_size) else vtx_buffer.Size = vtx_new_size end
|
||||||
self._VtxWritePtr = vtx_buffer_old_size + 1
|
self._VtxWritePtr = vtx_buffer_old_size + 1
|
||||||
|
|
||||||
local idx_buffer_old_size = self.IdxBuffer.Size
|
local idx_buffer = self.IdxBuffer
|
||||||
self.IdxBuffer:resize(idx_buffer_old_size + idx_count)
|
local idx_buffer_old_size = idx_buffer.Size
|
||||||
|
local idx_new_size = idx_buffer_old_size + idx_count
|
||||||
|
if idx_new_size > idx_buffer.Capacity then idx_buffer:resize(idx_new_size) else idx_buffer.Size = idx_new_size end
|
||||||
self._IdxWritePtr = idx_buffer_old_size + 1
|
self._IdxWritePtr = idx_buffer_old_size + 1
|
||||||
end
|
end
|
||||||
|
MT.ImDrawList.PrimReserve = PrimReserve
|
||||||
|
|
||||||
-- Release the number of reserved vertices/indices from the end of the last reservation made with PrimReserve().
|
-- Release the number of reserved vertices/indices from the end of the last reservation made with PrimReserve().
|
||||||
function MT.ImDrawList:PrimUnreserve(idx_count, vtx_count)
|
function MT.ImDrawList:PrimUnreserve(idx_count, vtx_count)
|
||||||
@@ -4159,12 +4264,13 @@ end
|
|||||||
--- @param a ImVec2
|
--- @param a ImVec2
|
||||||
--- @param c ImVec2
|
--- @param c ImVec2
|
||||||
--- @param col ImU32
|
--- @param col ImU32
|
||||||
|
-- [Luau] scalar PrimRect (a = ax,ay; c = cx,cy): no ImVec2 needed by callers, vertex writes inlined
|
||||||
@native
|
@native
|
||||||
function MT.ImDrawList:PrimRect(a, c, col)
|
local function PrimRectXY(self, ax, ay, cx, cy, col)
|
||||||
local uv = self._Data.TexUvWhitePixel
|
local uv = self._Data.TexUvWhitePixel
|
||||||
|
|
||||||
local idx = self._VtxCurrentIdx
|
local idx = self._VtxCurrentIdx
|
||||||
local idx_data = self.IdxBuffer.Data; local vtx_data = self.VtxBuffer.Buf
|
local idx_data = self.IdxBuffer.Data; local b = self.VtxBuffer.Buf
|
||||||
|
|
||||||
local idx_write_ptr = self._IdxWritePtr
|
local idx_write_ptr = self._IdxWritePtr
|
||||||
idx_data[idx_write_ptr + 0] = idx
|
idx_data[idx_write_ptr + 0] = idx
|
||||||
@@ -4176,17 +4282,22 @@ function MT.ImDrawList:PrimRect(a, c, col)
|
|||||||
idx_data[idx_write_ptr + 5] = idx + 3
|
idx_data[idx_write_ptr + 5] = idx + 3
|
||||||
|
|
||||||
local vtx_write_ptr = self._VtxWritePtr
|
local vtx_write_ptr = self._VtxWritePtr
|
||||||
local ax, ay, cx, cy, u, v = a.x, a.y, c.x, c.y, uv.x, uv.y
|
local u, v = uv.x, uv.y
|
||||||
WV(vtx_data, vtx_write_ptr + 0, ax, ay, u, v, col)
|
local o = (vtx_write_ptr - 1) * 36
|
||||||
WV(vtx_data, vtx_write_ptr + 1, cx, ay, u, v, col)
|
writef64(b, o, ax); writef64(b, o + 8, ay); writef64(b, o + 16, u); writef64(b, o + 24, v); writeu32(b, o + 32, col)
|
||||||
WV(vtx_data, vtx_write_ptr + 2, cx, cy, u, v, col)
|
writef64(b, o + 36, cx); writef64(b, o + 44, ay); writef64(b, o + 52, u); writef64(b, o + 60, v); writeu32(b, o + 68, col)
|
||||||
WV(vtx_data, vtx_write_ptr + 3, ax, cy, u, v, col)
|
writef64(b, o + 72, cx); writef64(b, o + 80, cy); writef64(b, o + 88, u); writef64(b, o + 96, v); writeu32(b, o + 104, col)
|
||||||
|
writef64(b, o + 108, ax); writef64(b, o + 116, cy); writef64(b, o + 124, u); writef64(b, o + 132, v); writeu32(b, o + 140, col)
|
||||||
|
|
||||||
self._VtxWritePtr = vtx_write_ptr + 4
|
self._VtxWritePtr = vtx_write_ptr + 4
|
||||||
self._VtxCurrentIdx = idx + 4
|
self._VtxCurrentIdx = idx + 4
|
||||||
self._IdxWritePtr = idx_write_ptr + 6
|
self._IdxWritePtr = idx_write_ptr + 6
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function MT.ImDrawList:PrimRect(a, c, col)
|
||||||
|
PrimRectXY(self, a.x, a.y, c.x, c.y, col)
|
||||||
|
end
|
||||||
|
|
||||||
--- @param a ImVec2
|
--- @param a ImVec2
|
||||||
--- @param c ImVec2
|
--- @param c ImVec2
|
||||||
--- @param uv_a ImVec2
|
--- @param uv_a ImVec2
|
||||||
@@ -4256,11 +4367,12 @@ function MT.ImDrawList:AddPolyline(points, points_count, col, thickness, flags)
|
|||||||
|
|
||||||
local idx_count = use_texture and (count * 6) or (thick_line and count * 18 or count * 12)
|
local idx_count = use_texture and (count * 6) or (thick_line and count * 18 or count * 12)
|
||||||
local vtx_count = use_texture and (points_count * 2) or (thick_line and points_count * 4 or points_count * 3)
|
local vtx_count = use_texture and (points_count * 2) or (thick_line and points_count * 4 or points_count * 3)
|
||||||
self:PrimReserve(idx_count, vtx_count)
|
PrimReserve(self, idx_count, vtx_count)
|
||||||
|
|
||||||
-- Temporary buffer
|
-- Temporary buffer
|
||||||
local temp_buffer_size = points_count * ((use_texture or not thick_line) and 3 or 5)
|
local temp_buffer_size = points_count * ((use_texture or not thick_line) and 3 or 5)
|
||||||
self._Data.TempBuffer:reserve_discard(temp_buffer_size)
|
local temp_buffer = self._Data.TempBuffer
|
||||||
|
if temp_buffer_size > temp_buffer.Capacity then temp_buffer:reserve_discard(temp_buffer_size) end -- [Luau] skip the method lookup when no growth is needed
|
||||||
local temp_normals = self._Data.TempBuffer.Data
|
local temp_normals = self._Data.TempBuffer.Data
|
||||||
local temp_points = self._Data.TempBuffer.Data; local temp_points_start = points_count + 1
|
local temp_points = self._Data.TempBuffer.Data; local temp_points_start = points_count + 1
|
||||||
|
|
||||||
@@ -4296,6 +4408,7 @@ function MT.ImDrawList:AddPolyline(points, points_count, col, thickness, flags)
|
|||||||
-- Generate indices and vertices
|
-- Generate indices and vertices
|
||||||
local idx1 = self._VtxCurrentIdx
|
local idx1 = self._VtxCurrentIdx
|
||||||
local idx_data = self.IdxBuffer.Data; local vtx_data = self.VtxBuffer.Buf
|
local idx_data = self.IdxBuffer.Data; local vtx_data = self.VtxBuffer.Buf
|
||||||
|
local idx_write_ptr = self._IdxWritePtr -- [Luau] write pointers kept in locals, stored back after the loops
|
||||||
for i1 = 1, count do
|
for i1 = 1, count do
|
||||||
local i2 = (i1 == points_count) and 1 or i1 + 1
|
local i2 = (i1 == points_count) and 1 or i1 + 1
|
||||||
local idx2 = (i1 == points_count) and self._VtxCurrentIdx or (idx1 + (use_texture and 2 or 3))
|
local idx2 = (i1 == points_count) and self._VtxCurrentIdx or (idx1 + (use_texture and 2 or 3))
|
||||||
@@ -4316,45 +4429,44 @@ function MT.ImDrawList:AddPolyline(points, points_count, col, thickness, flags)
|
|||||||
|
|
||||||
if use_texture then
|
if use_texture then
|
||||||
-- Add indices for two triangles
|
-- Add indices for two triangles
|
||||||
local idx_write_ptr = self._IdxWritePtr
|
|
||||||
idx_data[idx_write_ptr + 0] = idx2 + 0; idx_data[idx_write_ptr + 1] = idx1 + 0; idx_data[idx_write_ptr + 2] = idx1 + 1
|
idx_data[idx_write_ptr + 0] = idx2 + 0; idx_data[idx_write_ptr + 1] = idx1 + 0; idx_data[idx_write_ptr + 2] = idx1 + 1
|
||||||
idx_data[idx_write_ptr + 3] = idx2 + 1; idx_data[idx_write_ptr + 4] = idx1 + 1; idx_data[idx_write_ptr + 5] = idx2 + 0
|
idx_data[idx_write_ptr + 3] = idx2 + 1; idx_data[idx_write_ptr + 4] = idx1 + 1; idx_data[idx_write_ptr + 5] = idx2 + 0
|
||||||
self._IdxWritePtr = idx_write_ptr + 6
|
idx_write_ptr = idx_write_ptr + 6
|
||||||
else
|
else
|
||||||
-- Add indices for four triangles
|
-- Add indices for four triangles
|
||||||
local idx_write_ptr = self._IdxWritePtr
|
|
||||||
idx_data[idx_write_ptr + 0] = idx2 + 0; idx_data[idx_write_ptr + 1] = idx1 + 0; idx_data[idx_write_ptr + 2] = idx1 + 2
|
idx_data[idx_write_ptr + 0] = idx2 + 0; idx_data[idx_write_ptr + 1] = idx1 + 0; idx_data[idx_write_ptr + 2] = idx1 + 2
|
||||||
idx_data[idx_write_ptr + 3] = idx1 + 2; idx_data[idx_write_ptr + 4] = idx2 + 2; idx_data[idx_write_ptr + 5] = idx2 + 0
|
idx_data[idx_write_ptr + 3] = idx1 + 2; idx_data[idx_write_ptr + 4] = idx2 + 2; idx_data[idx_write_ptr + 5] = idx2 + 0
|
||||||
idx_data[idx_write_ptr + 6] = idx2 + 1; idx_data[idx_write_ptr + 7] = idx1 + 1; idx_data[idx_write_ptr + 8] = idx1 + 0
|
idx_data[idx_write_ptr + 6] = idx2 + 1; idx_data[idx_write_ptr + 7] = idx1 + 1; idx_data[idx_write_ptr + 8] = idx1 + 0
|
||||||
idx_data[idx_write_ptr + 9] = idx1 + 0; idx_data[idx_write_ptr + 10] = idx2 + 0; idx_data[idx_write_ptr + 11] = idx2 + 1
|
idx_data[idx_write_ptr + 9] = idx1 + 0; idx_data[idx_write_ptr + 10] = idx2 + 0; idx_data[idx_write_ptr + 11] = idx2 + 1
|
||||||
self._IdxWritePtr = idx_write_ptr + 12
|
idx_write_ptr = idx_write_ptr + 12
|
||||||
end
|
end
|
||||||
|
|
||||||
idx1 = idx2
|
idx1 = idx2
|
||||||
end
|
end
|
||||||
|
self._IdxWritePtr = idx_write_ptr
|
||||||
|
|
||||||
-- Add vertices
|
-- Add vertices
|
||||||
|
local vtx_write_ptr = self._VtxWritePtr
|
||||||
if use_texture then
|
if use_texture then
|
||||||
-- Texture-based: need to implement TexUvLines lookup
|
-- Texture-based: need to implement TexUvLines lookup
|
||||||
local tex_uvs = self._Data.TexUvLines[integer_thickness]
|
local tex_uvs = self._Data.TexUvLines[integer_thickness]
|
||||||
local tex_uv0 = ImVec2(tex_uvs.x, tex_uvs.y)
|
local tex_uv0 = ImVec2(tex_uvs.x, tex_uvs.y)
|
||||||
local tex_uv1 = ImVec2(tex_uvs.z, tex_uvs.w)
|
local tex_uv1 = ImVec2(tex_uvs.z, tex_uvs.w)
|
||||||
for i = 0, points_count - 1 do
|
for i = 0, points_count - 1 do
|
||||||
local vtx_write_ptr = self._VtxWritePtr
|
|
||||||
WVP(vtx_data, vtx_write_ptr + 0, temp_points[temp_points_start + i * 2 + 0], tex_uv0, col)
|
WVP(vtx_data, vtx_write_ptr + 0, temp_points[temp_points_start + i * 2 + 0], tex_uv0, col)
|
||||||
WVP(vtx_data, vtx_write_ptr + 1, temp_points[temp_points_start + i * 2 + 1], tex_uv1, col)
|
WVP(vtx_data, vtx_write_ptr + 1, temp_points[temp_points_start + i * 2 + 1], tex_uv1, col)
|
||||||
self._VtxWritePtr = vtx_write_ptr + 2
|
vtx_write_ptr = vtx_write_ptr + 2
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- If we're not using a texture, we need the center vertex as well
|
-- If we're not using a texture, we need the center vertex as well
|
||||||
for i = 0, points_count - 1 do
|
for i = 0, points_count - 1 do
|
||||||
local vtx_write_ptr = self._VtxWritePtr
|
|
||||||
WVP(vtx_data, vtx_write_ptr + 0, points[i + 1], opaque_uv, col)
|
WVP(vtx_data, vtx_write_ptr + 0, points[i + 1], opaque_uv, col)
|
||||||
WVP(vtx_data, vtx_write_ptr + 1, temp_points[temp_points_start + i * 2 + 0], opaque_uv, col_trans)
|
WVP(vtx_data, vtx_write_ptr + 1, temp_points[temp_points_start + i * 2 + 0], opaque_uv, col_trans)
|
||||||
WVP(vtx_data, vtx_write_ptr + 2, temp_points[temp_points_start + i * 2 + 1], opaque_uv, col_trans)
|
WVP(vtx_data, vtx_write_ptr + 2, temp_points[temp_points_start + i * 2 + 1], opaque_uv, col_trans)
|
||||||
self._VtxWritePtr = vtx_write_ptr + 3
|
vtx_write_ptr = vtx_write_ptr + 3
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
self._VtxWritePtr = vtx_write_ptr
|
||||||
else
|
else
|
||||||
-- [PATH 3] Non texture-based lines (thick)
|
-- [PATH 3] Non texture-based lines (thick)
|
||||||
local half_inner_thickness = (thickness - AA_SIZE) * 0.5
|
local half_inner_thickness = (thickness - AA_SIZE) * 0.5
|
||||||
@@ -4375,6 +4487,7 @@ function MT.ImDrawList:AddPolyline(points, points_count, col, thickness, flags)
|
|||||||
-- Generate indices and vertices
|
-- Generate indices and vertices
|
||||||
local idx1 = self._VtxCurrentIdx
|
local idx1 = self._VtxCurrentIdx
|
||||||
local idx_data = self.IdxBuffer.Data; local vtx_data = self.VtxBuffer.Buf
|
local idx_data = self.IdxBuffer.Data; local vtx_data = self.VtxBuffer.Buf
|
||||||
|
local idx_write_ptr = self._IdxWritePtr -- [Luau] write pointers kept in locals, stored back after the loops
|
||||||
for i1 = 1, count do
|
for i1 = 1, count do
|
||||||
local i2 = (i1 == points_count) and 1 or i1 + 1
|
local i2 = (i1 == points_count) and 1 or i1 + 1
|
||||||
local idx2 = (i1 == points_count) and self._VtxCurrentIdx or (idx1 + 4)
|
local idx2 = (i1 == points_count) and self._VtxCurrentIdx or (idx1 + 4)
|
||||||
@@ -4400,34 +4513,35 @@ function MT.ImDrawList:AddPolyline(points, points_count, col, thickness, flags)
|
|||||||
temp_points[temp_points_start + out_off + 3].y = points[i2].y - dm_out_y
|
temp_points[temp_points_start + out_off + 3].y = points[i2].y - dm_out_y
|
||||||
|
|
||||||
-- Add indices
|
-- Add indices
|
||||||
local idx_write_ptr = self._IdxWritePtr
|
|
||||||
idx_data[idx_write_ptr + 0] = idx2 + 1; idx_data[idx_write_ptr + 1] = idx1 + 1; idx_data[idx_write_ptr + 2] = idx1 + 2
|
idx_data[idx_write_ptr + 0] = idx2 + 1; idx_data[idx_write_ptr + 1] = idx1 + 1; idx_data[idx_write_ptr + 2] = idx1 + 2
|
||||||
idx_data[idx_write_ptr + 3] = idx1 + 2; idx_data[idx_write_ptr + 4] = idx2 + 2; idx_data[idx_write_ptr + 5] = idx2 + 1
|
idx_data[idx_write_ptr + 3] = idx1 + 2; idx_data[idx_write_ptr + 4] = idx2 + 2; idx_data[idx_write_ptr + 5] = idx2 + 1
|
||||||
idx_data[idx_write_ptr + 6] = idx2 + 1; idx_data[idx_write_ptr + 7] = idx1 + 1; idx_data[idx_write_ptr + 8] = idx1 + 0
|
idx_data[idx_write_ptr + 6] = idx2 + 1; idx_data[idx_write_ptr + 7] = idx1 + 1; idx_data[idx_write_ptr + 8] = idx1 + 0
|
||||||
idx_data[idx_write_ptr + 9] = idx1 + 0; idx_data[idx_write_ptr + 10] = idx2 + 0; idx_data[idx_write_ptr + 11] = idx2 + 1
|
idx_data[idx_write_ptr + 9] = idx1 + 0; idx_data[idx_write_ptr + 10] = idx2 + 0; idx_data[idx_write_ptr + 11] = idx2 + 1
|
||||||
idx_data[idx_write_ptr + 12] = idx2 + 2; idx_data[idx_write_ptr + 13] = idx1 + 2; idx_data[idx_write_ptr + 14] = idx1 + 3
|
idx_data[idx_write_ptr + 12] = idx2 + 2; idx_data[idx_write_ptr + 13] = idx1 + 2; idx_data[idx_write_ptr + 14] = idx1 + 3
|
||||||
idx_data[idx_write_ptr + 15] = idx1 + 3; idx_data[idx_write_ptr + 16] = idx2 + 3; idx_data[idx_write_ptr + 17] = idx2 + 2
|
idx_data[idx_write_ptr + 15] = idx1 + 3; idx_data[idx_write_ptr + 16] = idx2 + 3; idx_data[idx_write_ptr + 17] = idx2 + 2
|
||||||
self._IdxWritePtr = idx_write_ptr + 18
|
idx_write_ptr = idx_write_ptr + 18
|
||||||
|
|
||||||
idx1 = idx2
|
idx1 = idx2
|
||||||
end
|
end
|
||||||
|
self._IdxWritePtr = idx_write_ptr
|
||||||
|
|
||||||
-- Add vertices
|
-- Add vertices
|
||||||
|
local vtx_write_ptr = self._VtxWritePtr
|
||||||
for i = 0, points_count - 1 do
|
for i = 0, points_count - 1 do
|
||||||
local vtx_write_ptr = self._VtxWritePtr
|
|
||||||
WVP(vtx_data, vtx_write_ptr + 0, temp_points[temp_points_start + i * 4 + 0], opaque_uv, col_trans)
|
WVP(vtx_data, vtx_write_ptr + 0, temp_points[temp_points_start + i * 4 + 0], opaque_uv, col_trans)
|
||||||
WVP(vtx_data, vtx_write_ptr + 1, temp_points[temp_points_start + i * 4 + 1], opaque_uv, col)
|
WVP(vtx_data, vtx_write_ptr + 1, temp_points[temp_points_start + i * 4 + 1], opaque_uv, col)
|
||||||
WVP(vtx_data, vtx_write_ptr + 2, temp_points[temp_points_start + i * 4 + 2], opaque_uv, col)
|
WVP(vtx_data, vtx_write_ptr + 2, temp_points[temp_points_start + i * 4 + 2], opaque_uv, col)
|
||||||
WVP(vtx_data, vtx_write_ptr + 3, temp_points[temp_points_start + i * 4 + 3], opaque_uv, col_trans)
|
WVP(vtx_data, vtx_write_ptr + 3, temp_points[temp_points_start + i * 4 + 3], opaque_uv, col_trans)
|
||||||
self._VtxWritePtr = vtx_write_ptr + 4
|
vtx_write_ptr = vtx_write_ptr + 4
|
||||||
end
|
end
|
||||||
|
self._VtxWritePtr = vtx_write_ptr
|
||||||
end
|
end
|
||||||
self._VtxCurrentIdx = self._VtxCurrentIdx + vtx_count
|
self._VtxCurrentIdx = self._VtxCurrentIdx + vtx_count
|
||||||
else
|
else
|
||||||
-- [PATH 4] Non texture-based, Non anti-aliased lines
|
-- [PATH 4] Non texture-based, Non anti-aliased lines
|
||||||
local idx_count = count * 6
|
local idx_count = count * 6
|
||||||
local vtx_count = count * 4
|
local vtx_count = count * 4
|
||||||
self:PrimReserve(idx_count, vtx_count)
|
PrimReserve(self, idx_count, vtx_count)
|
||||||
|
|
||||||
local idx_data = self.IdxBuffer.Data; local vtx_data = self.VtxBuffer.Buf
|
local idx_data = self.IdxBuffer.Data; local vtx_data = self.VtxBuffer.Buf
|
||||||
for i1 = 1, count do
|
for i1 = 1, count do
|
||||||
@@ -4457,7 +4571,20 @@ function MT.ImDrawList:AddPolyline(points, points_count, col, thickness, flags)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MT.ImDrawList:PathRect(a, b, rounding, flags)
|
-- [Luau] scalar PathLineTo(): no temporary ImVec2 (the path slot is written in place)
|
||||||
|
local function PathLineToXY(self, x, y)
|
||||||
|
local path = self._Path
|
||||||
|
local n = path.Size + 1
|
||||||
|
if n > path.Capacity then path:push_back(ImVec2(x, y)) return end
|
||||||
|
local dest = path.Data[n]
|
||||||
|
dest.x = x; dest.y = y
|
||||||
|
path.Size = n
|
||||||
|
end
|
||||||
|
|
||||||
|
local _PathArcToFastExXY -- [Luau] forward declaration, defined below with _PathArcToFastEx()
|
||||||
|
|
||||||
|
-- [Luau] scalar PathRect (a = ax,ay; b = bx,by); PathRect() below wraps it
|
||||||
|
local function PathRectXY(self, ax, ay, bx, by, rounding, flags)
|
||||||
if not rounding then rounding = 0.0 end
|
if not rounding then rounding = 0.0 end
|
||||||
if not flags then flags = 0 end
|
if not flags then flags = 0 end
|
||||||
|
|
||||||
@@ -4466,26 +4593,31 @@ function MT.ImDrawList:PathRect(a, b, rounding, flags)
|
|||||||
if bit32.band(flags, ImDrawFlags.RoundCornersMask_) == 0 then
|
if bit32.band(flags, ImDrawFlags.RoundCornersMask_) == 0 then
|
||||||
flags = bit32.bor(flags, ImDrawFlags.RoundCornersAll)
|
flags = bit32.bor(flags, ImDrawFlags.RoundCornersAll)
|
||||||
end
|
end
|
||||||
rounding = ImMin(rounding, ImAbs(b.x - a.x) * (((bit32.band(flags, ImDrawFlags.RoundCornersTop) == ImDrawFlags.RoundCornersTop) or (bit32.band(flags, ImDrawFlags.RoundCornersBottom) == ImDrawFlags.RoundCornersBottom)) and 0.5 or 1.0) - 1.0)
|
rounding = ImMin(rounding, ImAbs(bx - ax) * (((bit32.band(flags, ImDrawFlags.RoundCornersTop) == ImDrawFlags.RoundCornersTop) or (bit32.band(flags, ImDrawFlags.RoundCornersBottom) == ImDrawFlags.RoundCornersBottom)) and 0.5 or 1.0) - 1.0)
|
||||||
rounding = ImMin(rounding, ImAbs(b.y - a.y) * (((bit32.band(flags, ImDrawFlags.RoundCornersLeft) == ImDrawFlags.RoundCornersLeft) or (bit32.band(flags, ImDrawFlags.RoundCornersRight) == ImDrawFlags.RoundCornersRight)) and 0.5 or 1.0) - 1.0)
|
rounding = ImMin(rounding, ImAbs(by - ay) * (((bit32.band(flags, ImDrawFlags.RoundCornersLeft) == ImDrawFlags.RoundCornersLeft) or (bit32.band(flags, ImDrawFlags.RoundCornersRight) == ImDrawFlags.RoundCornersRight)) and 0.5 or 1.0) - 1.0)
|
||||||
end
|
end
|
||||||
if rounding < 0.5 or (bit32.band(flags, ImDrawFlags.RoundCornersMask_) == ImDrawFlags.RoundCornersNone) then
|
if rounding < 0.5 or (bit32.band(flags, ImDrawFlags.RoundCornersMask_) == ImDrawFlags.RoundCornersNone) then
|
||||||
self:PathLineTo(a)
|
PathLineToXY(self, ax, ay)
|
||||||
self:PathLineTo(ImVec2(b.x, a.y))
|
PathLineToXY(self, bx, ay)
|
||||||
self:PathLineTo(b)
|
PathLineToXY(self, bx, by)
|
||||||
self:PathLineTo(ImVec2(a.x, b.y))
|
PathLineToXY(self, ax, by)
|
||||||
else
|
else
|
||||||
local rounding_tl = (bit32.band(flags, ImDrawFlags.RoundCornersTopLeft) ~= 0) and rounding or 0.0
|
local rounding_tl = (bit32.band(flags, ImDrawFlags.RoundCornersTopLeft) ~= 0) and rounding or 0.0
|
||||||
local rounding_tr = (bit32.band(flags, ImDrawFlags.RoundCornersTopRight) ~= 0) and rounding or 0.0
|
local rounding_tr = (bit32.band(flags, ImDrawFlags.RoundCornersTopRight) ~= 0) and rounding or 0.0
|
||||||
local rounding_br = (bit32.band(flags, ImDrawFlags.RoundCornersBottomRight) ~= 0) and rounding or 0.0
|
local rounding_br = (bit32.band(flags, ImDrawFlags.RoundCornersBottomRight) ~= 0) and rounding or 0.0
|
||||||
local rounding_bl = (bit32.band(flags, ImDrawFlags.RoundCornersBottomLeft) ~= 0) and rounding or 0.0
|
local rounding_bl = (bit32.band(flags, ImDrawFlags.RoundCornersBottomLeft) ~= 0) and rounding or 0.0
|
||||||
self:PathArcToFast(ImVec2(a.x + rounding_tl, a.y + rounding_tl), rounding_tl, 6, 9)
|
-- [Luau] inline PathArcToFast() (radius < 0.5 handled by _PathArcToFastExXY())
|
||||||
self:PathArcToFast(ImVec2(b.x - rounding_tr, a.y + rounding_tr), rounding_tr, 9, 12)
|
_PathArcToFastExXY(self, ax + rounding_tl, ay + rounding_tl, rounding_tl, 6 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 9 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 0)
|
||||||
self:PathArcToFast(ImVec2(b.x - rounding_br, b.y - rounding_br), rounding_br, 0, 3)
|
_PathArcToFastExXY(self, bx - rounding_tr, ay + rounding_tr, rounding_tr, 9 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 0)
|
||||||
self:PathArcToFast(ImVec2(a.x + rounding_bl, b.y - rounding_bl), rounding_bl, 3, 6)
|
_PathArcToFastExXY(self, bx - rounding_br, by - rounding_br, rounding_br, 0 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 3 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 0)
|
||||||
|
_PathArcToFastExXY(self, ax + rounding_bl, by - rounding_bl, rounding_bl, 3 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 6 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function MT.ImDrawList:PathRect(a, b, rounding, flags)
|
||||||
|
PathRectXY(self, a.x, a.y, b.x, b.y, rounding, flags)
|
||||||
|
end
|
||||||
|
|
||||||
@native
|
@native
|
||||||
function MT.ImDrawList:AddRectFilled(p_min, p_max, col, rounding, flags)
|
function MT.ImDrawList:AddRectFilled(p_min, p_max, col, rounding, flags)
|
||||||
if not rounding then rounding = 0.0 end
|
if not rounding then rounding = 0.0 end
|
||||||
@@ -4494,10 +4626,10 @@ function MT.ImDrawList:AddRectFilled(p_min, p_max, col, rounding, flags)
|
|||||||
if bit32.band(col, IM_COL32_A_MASK) == 0 then return end
|
if bit32.band(col, IM_COL32_A_MASK) == 0 then return end
|
||||||
|
|
||||||
if rounding < 0.5 or (bit32.band(flags, ImDrawFlags.RoundCornersMask_) == ImDrawFlags.RoundCornersNone) then
|
if rounding < 0.5 or (bit32.band(flags, ImDrawFlags.RoundCornersMask_) == ImDrawFlags.RoundCornersNone) then
|
||||||
self:PrimReserve(6, 4)
|
PrimReserve(self, 6, 4) -- [Luau] local calls
|
||||||
self:PrimRect(p_min, p_max, col)
|
PrimRectXY(self, p_min.x, p_min.y, p_max.x, p_max.y, col)
|
||||||
else
|
else
|
||||||
self:PathRect(p_min, p_max, rounding, flags)
|
PathRectXY(self, p_min.x, p_min.y, p_max.x, p_max.y, rounding, flags)
|
||||||
self:PathFillConvex(col)
|
self:PathFillConvex(col)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -4516,13 +4648,19 @@ function MT.ImDrawList:AddRectFilledMultiColor(p_min, p_max, col_upr_left, col_u
|
|||||||
end
|
end
|
||||||
|
|
||||||
local uv = self._Data.TexUvWhitePixel
|
local uv = self._Data.TexUvWhitePixel
|
||||||
self:PrimReserve(6, 4)
|
PrimReserve(self, 6, 4)
|
||||||
self:PrimWriteIdx(self._VtxCurrentIdx); self:PrimWriteIdx(self._VtxCurrentIdx + 1); self:PrimWriteIdx(self._VtxCurrentIdx + 2)
|
-- [Luau] inline PrimWriteIdx()/PrimWriteVtx(): direct writes, no temporary ImVec2
|
||||||
self:PrimWriteIdx(self._VtxCurrentIdx); self:PrimWriteIdx(self._VtxCurrentIdx + 2); self:PrimWriteIdx(self._VtxCurrentIdx + 3)
|
local idx, idx_data, idx_write_ptr = self._VtxCurrentIdx, self.IdxBuffer.Data, self._IdxWritePtr
|
||||||
self:PrimWriteVtx(p_min, uv, col_upr_left)
|
idx_data[idx_write_ptr] = idx; idx_data[idx_write_ptr + 1] = idx + 1; idx_data[idx_write_ptr + 2] = idx + 2
|
||||||
self:PrimWriteVtx(ImVec2(p_max.x, p_min.y), uv, col_upr_right)
|
idx_data[idx_write_ptr + 3] = idx; idx_data[idx_write_ptr + 4] = idx + 2; idx_data[idx_write_ptr + 5] = idx + 3
|
||||||
self:PrimWriteVtx(p_max, uv, col_bot_right)
|
self._IdxWritePtr = idx_write_ptr + 6
|
||||||
self:PrimWriteVtx(ImVec2(p_min.x, p_max.y), uv, col_bot_left)
|
local vtx_data, vtx_write_ptr = self.VtxBuffer.Buf, self._VtxWritePtr
|
||||||
|
WV(vtx_data, vtx_write_ptr + 0, p_min.x, p_min.y, uv.x, uv.y, col_upr_left)
|
||||||
|
WV(vtx_data, vtx_write_ptr + 1, p_max.x, p_min.y, uv.x, uv.y, col_upr_right)
|
||||||
|
WV(vtx_data, vtx_write_ptr + 2, p_max.x, p_max.y, uv.x, uv.y, col_bot_right)
|
||||||
|
WV(vtx_data, vtx_write_ptr + 3, p_min.x, p_max.y, uv.x, uv.y, col_bot_left)
|
||||||
|
self._VtxWritePtr = vtx_write_ptr + 4
|
||||||
|
self._VtxCurrentIdx = idx + 4
|
||||||
end
|
end
|
||||||
|
|
||||||
-- p_min = upper-left, p_max = lower-right
|
-- p_min = upper-left, p_max = lower-right
|
||||||
@@ -4555,17 +4693,17 @@ function MT.ImDrawList:AddRect(p_min, p_max, col, rounding, thickness, flags)
|
|||||||
if (rounding == nil or rounding < 0.5 or bit32.band(flags, ImDrawFlags.RoundCornersMask_) == ImDrawFlags.RoundCornersNone)
|
if (rounding == nil or rounding < 0.5 or bit32.band(flags, ImDrawFlags.RoundCornersMask_) == ImDrawFlags.RoundCornersNone)
|
||||||
and thickness >= 1.0 and thickness % 1 == 0 and p_max.x - p_min.x > 2 * thickness and p_max.y - p_min.y > 2 * thickness then
|
and thickness >= 1.0 and thickness % 1 == 0 and p_max.x - p_min.x > 2 * thickness and p_max.y - p_min.y > 2 * thickness then
|
||||||
local x0, y0, x1, y1, t = p_min.x, p_min.y, p_max.x, p_max.y, thickness
|
local x0, y0, x1, y1, t = p_min.x, p_min.y, p_max.x, p_max.y, thickness
|
||||||
self:PrimReserve(24, 16)
|
PrimReserve(self, 24, 16)
|
||||||
self:PrimRect(ImVec2(x0, y0), ImVec2(x1, y0 + t), col)
|
PrimRectXY(self, x0, y0, x1, y0 + t, col)
|
||||||
self:PrimRect(ImVec2(x0, y1 - t), ImVec2(x1, y1), col)
|
PrimRectXY(self, x0, y1 - t, x1, y1, col)
|
||||||
self:PrimRect(ImVec2(x0, y0 + t), ImVec2(x0 + t, y1 - t), col)
|
PrimRectXY(self, x0, y0 + t, x0 + t, y1 - t, col)
|
||||||
self:PrimRect(ImVec2(x1 - t, y0 + t), ImVec2(x1, y1 - t), col)
|
PrimRectXY(self, x1 - t, y0 + t, x1, y1 - t, col)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if bit32.band(self.Flags, ImDrawListFlags.AntiAliasedLines) ~= 0 then
|
if bit32.band(self.Flags, ImDrawListFlags.AntiAliasedLines) ~= 0 then
|
||||||
self:PathRect(p_min + ImVec2(0.50, 0.50), p_max - ImVec2(0.50, 0.50), rounding, flags)
|
PathRectXY(self, p_min.x + 0.50, p_min.y + 0.50, p_max.x - 0.50, p_max.y - 0.50, rounding, flags) -- [Luau] scalar math, no temporary ImVec2
|
||||||
else
|
else
|
||||||
self:PathRect(p_min + ImVec2(0.50, 0.50), p_max - ImVec2(0.49, 0.49), rounding, flags) -- Better looking lower-right corner and rounded non-AA shapes.
|
PathRectXY(self, p_min.x + 0.50, p_min.y + 0.50, p_max.x - 0.49, p_max.y - 0.49, rounding, flags) -- Better looking lower-right corner and rounded non-AA shapes.
|
||||||
end
|
end
|
||||||
|
|
||||||
self:PathStroke(col, thickness, ImDrawFlags.Closed)
|
self:PathStroke(col, thickness, ImDrawFlags.Closed)
|
||||||
@@ -4574,8 +4712,8 @@ end
|
|||||||
function MT.ImDrawList:AddLine(p1, p2, col, thickness)
|
function MT.ImDrawList:AddLine(p1, p2, col, thickness)
|
||||||
if bit32.band(col, IM_COL32_A_MASK) == 0 then return end
|
if bit32.band(col, IM_COL32_A_MASK) == 0 then return end
|
||||||
|
|
||||||
self:PathLineTo(p1 + ImVec2(0.5, 0.5))
|
PathLineToXY(self, p1.x + 0.5, p1.y + 0.5) -- [Luau] scalar math, no temporary ImVec2
|
||||||
self:PathLineTo(p2 + ImVec2(0.5, 0.5))
|
PathLineToXY(self, p2.x + 0.5, p2.y + 0.5)
|
||||||
self:PathStroke(col, thickness)
|
self:PathStroke(col, thickness)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -4590,8 +4728,8 @@ function MT.ImDrawList:AddLineH(min_x, max_x, y, col, thickness)
|
|||||||
if bit32.band(col, IM_COL32_A_MASK) == 0 then
|
if bit32.band(col, IM_COL32_A_MASK) == 0 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
self:PathLineTo(ImVec2(min_x + 0.5, y + 0.5))
|
PathLineToXY(self, min_x + 0.5, y + 0.5)
|
||||||
self:PathLineTo(ImVec2(max_x + 0.5, y + 0.5))
|
PathLineToXY(self, max_x + 0.5, y + 0.5)
|
||||||
self:PathStroke(col, thickness)
|
self:PathStroke(col, thickness)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -4606,8 +4744,8 @@ function MT.ImDrawList:AddLineV(x, min_y, max_y, col, thickness)
|
|||||||
if bit32.band(col, IM_COL32_A_MASK) == 0 then
|
if bit32.band(col, IM_COL32_A_MASK) == 0 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
self:PathLineTo(ImVec2(x + 0.5, min_y + 0.5))
|
PathLineToXY(self, x + 0.5, min_y + 0.5)
|
||||||
self:PathLineTo(ImVec2(x + 0.5, max_y + 0.5))
|
PathLineToXY(self, x + 0.5, max_y + 0.5)
|
||||||
self:PathStroke(col, thickness)
|
self:PathStroke(col, thickness)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -4667,7 +4805,7 @@ function MT.ImDrawList:AddCircle(center, radius, col, num_segments, thickness)
|
|||||||
|
|
||||||
if num_segments <= 0 then
|
if num_segments <= 0 then
|
||||||
-- Use arc with automatic segment count
|
-- Use arc with automatic segment count
|
||||||
self:_PathArcToFastEx(center, radius - 0.5, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0)
|
_PathArcToFastExXY(self, center.x, center.y, radius - 0.5, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0)
|
||||||
self._Path.Size = self._Path.Size - 1
|
self._Path.Size = self._Path.Size - 1
|
||||||
else
|
else
|
||||||
-- Explicit segment count (still clamp to avoid drawing insanely tessellated shapes)
|
-- Explicit segment count (still clamp to avoid drawing insanely tessellated shapes)
|
||||||
@@ -4694,7 +4832,7 @@ function MT.ImDrawList:AddCircleFilled(center, radius, col, num_segments)
|
|||||||
|
|
||||||
if num_segments <= 0 then
|
if num_segments <= 0 then
|
||||||
-- Use arc with automatic segment count
|
-- Use arc with automatic segment count
|
||||||
self:_PathArcToFastEx(center, radius, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0)
|
_PathArcToFastExXY(self, center.x, center.y, radius, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0)
|
||||||
self._Path.Size = self._Path.Size - 1
|
self._Path.Size = self._Path.Size - 1
|
||||||
else
|
else
|
||||||
-- Explicit segment count (still clamp to avoid drawing insanely tessellated shapes)
|
-- Explicit segment count (still clamp to avoid drawing insanely tessellated shapes)
|
||||||
@@ -4717,6 +4855,8 @@ end
|
|||||||
--- @param text_end int
|
--- @param text_end int
|
||||||
--- @param wrap_width float
|
--- @param wrap_width float
|
||||||
--- @param cpu_fine_clip_rect? ImVec4
|
--- @param cpu_fine_clip_rect? ImVec4
|
||||||
|
local AddText_clip_rect = ImVec4()
|
||||||
|
|
||||||
@native
|
@native
|
||||||
function MT.ImDrawList:AddText(font, font_size, pos, col, text, text_begin, text_end, wrap_width, cpu_fine_clip_rect)
|
function MT.ImDrawList:AddText(font, font_size, pos, col, text, text_begin, text_end, wrap_width, cpu_fine_clip_rect)
|
||||||
-- Overload: AddText(pos, col, text, text_end?)
|
-- Overload: AddText(pos, col, text, text_end?)
|
||||||
@@ -4742,13 +4882,14 @@ function MT.ImDrawList:AddText(font, font_size, pos, col, text, text_begin, text
|
|||||||
font_size = self._Data.FontSize
|
font_size = self._Data.FontSize
|
||||||
end
|
end
|
||||||
|
|
||||||
local clip_rect = ImVec4() -- Don't modify the clip rect!
|
local clip_rect = AddText_clip_rect -- Don't modify the clip rect! -- [Luau] scratch: ImFont:RenderText() only reads it
|
||||||
ImVec4_Copy(clip_rect, self._CmdHeader.ClipRect)
|
local src = self._CmdHeader.ClipRect
|
||||||
|
clip_rect.x = src.x; clip_rect.y = src.y; clip_rect.z = src.z; clip_rect.w = src.w
|
||||||
if (cpu_fine_clip_rect) then
|
if (cpu_fine_clip_rect) then
|
||||||
clip_rect.x = ImMax(clip_rect.x, cpu_fine_clip_rect.x)
|
clip_rect.x = math.max(clip_rect.x, cpu_fine_clip_rect.x)
|
||||||
clip_rect.y = ImMax(clip_rect.y, cpu_fine_clip_rect.y)
|
clip_rect.y = math.max(clip_rect.y, cpu_fine_clip_rect.y)
|
||||||
clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect.z)
|
clip_rect.z = math.min(clip_rect.z, cpu_fine_clip_rect.z)
|
||||||
clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect.w)
|
clip_rect.w = math.min(clip_rect.w, cpu_fine_clip_rect.w)
|
||||||
end
|
end
|
||||||
|
|
||||||
font:RenderText(self, font_size, pos, col, clip_rect, text, text_begin, text_end, wrap_width, (cpu_fine_clip_rect ~= nil) and ImDrawTextFlags.CpuFineClip or ImDrawTextFlags.None)
|
font:RenderText(self, font_size, pos, col, clip_rect, text, text_begin, text_end, wrap_width, (cpu_fine_clip_rect ~= nil) and ImDrawTextFlags.CpuFineClip or ImDrawTextFlags.None)
|
||||||
@@ -4846,20 +4987,25 @@ function MT.ImDrawList:PushClipRect(cr_min, cr_max, intersect_with_current_clip_
|
|||||||
if cr.w > current.w then cr.w = current.w end
|
if cr.w > current.w then cr.w = current.w end
|
||||||
end
|
end
|
||||||
|
|
||||||
cr.z = ImMax(cr.x, cr.z)
|
cr.z = math.max(cr.x, cr.z)
|
||||||
cr.w = ImMax(cr.y, cr.w)
|
cr.w = math.max(cr.y, cr.w)
|
||||||
|
|
||||||
self._ClipRectStack:push_back(cr)
|
-- [Luau] inline self._ClipRectStack:push_back(cr)
|
||||||
|
local stack = self._ClipRectStack
|
||||||
|
local n = stack.Size + 1
|
||||||
|
if n > stack.Capacity then stack:push_back(cr) else stack.Data[n] = cr; stack.Size = n end
|
||||||
ImVec4_Copy(self._CmdHeader.ClipRect, cr)
|
ImVec4_Copy(self._CmdHeader.ClipRect, cr)
|
||||||
self:_OnChangedClipRect()
|
self:_OnChangedClipRect()
|
||||||
end
|
end
|
||||||
|
|
||||||
function MT.ImDrawList:PopClipRect()
|
function MT.ImDrawList:PopClipRect()
|
||||||
self._ClipRectStack:pop_back()
|
local stack = self._ClipRectStack
|
||||||
if (self._ClipRectStack.Size == 0) then -- LUA: No "Ternary Operator"
|
IM_ASSERT(stack.Size > 0)
|
||||||
|
stack.Size = stack.Size - 1 -- [Luau] inline pop_back()
|
||||||
|
if (stack.Size == 0) then -- LUA: No "Ternary Operator"
|
||||||
ImVec4_Copy(self._CmdHeader.ClipRect, self._Data.ClipRectFullscreen)
|
ImVec4_Copy(self._CmdHeader.ClipRect, self._Data.ClipRectFullscreen)
|
||||||
else
|
else
|
||||||
ImVec4_Copy(self._CmdHeader.ClipRect, self._ClipRectStack.Data[self._ClipRectStack.Size])
|
ImVec4_Copy(self._CmdHeader.ClipRect, stack.Data[stack.Size])
|
||||||
end
|
end
|
||||||
self:_OnChangedClipRect()
|
self:_OnChangedClipRect()
|
||||||
end
|
end
|
||||||
@@ -4869,10 +5015,11 @@ end
|
|||||||
--- @param a_min_sample int
|
--- @param a_min_sample int
|
||||||
--- @param a_max_sample int
|
--- @param a_max_sample int
|
||||||
--- @param a_step int
|
--- @param a_step int
|
||||||
|
-- [Luau] scalar center (cx, cy); assigned to the forward-declared local, _PathArcToFastEx() wraps it
|
||||||
@native
|
@native
|
||||||
function MT.ImDrawList:_PathArcToFastEx(center, radius, a_min_sample, a_max_sample, a_step)
|
function _PathArcToFastExXY(self, cx, cy, radius, a_min_sample, a_max_sample, a_step)
|
||||||
if radius < 0.5 then
|
if radius < 0.5 then
|
||||||
self._Path:push_back(center)
|
PathLineToXY(self, cx, cy)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -4908,6 +5055,7 @@ function MT.ImDrawList:_PathArcToFastEx(center, radius, a_min_sample, a_max_samp
|
|||||||
self._Path:resize(self._Path.Size + samples)
|
self._Path:resize(self._Path.Size + samples)
|
||||||
local out_ptr = self._Path.Size - samples + 1
|
local out_ptr = self._Path.Size - samples + 1
|
||||||
|
|
||||||
|
local path_data, arc_fast_vtx = self._Path.Data, self._Data.ArcFastVtx -- [Luau] cache
|
||||||
local sample_index = a_min_sample
|
local sample_index = a_min_sample
|
||||||
if sample_index < 0 or sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX then
|
if sample_index < 0 or sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX then
|
||||||
sample_index = sample_index % IM_DRAWLIST_ARCFAST_SAMPLE_MAX
|
sample_index = sample_index % IM_DRAWLIST_ARCFAST_SAMPLE_MAX
|
||||||
@@ -4924,8 +5072,9 @@ function MT.ImDrawList:_PathArcToFastEx(center, radius, a_min_sample, a_max_samp
|
|||||||
sample_index = sample_index - IM_DRAWLIST_ARCFAST_SAMPLE_MAX
|
sample_index = sample_index - IM_DRAWLIST_ARCFAST_SAMPLE_MAX
|
||||||
end
|
end
|
||||||
|
|
||||||
local s = self._Data.ArcFastVtx[sample_index + 1]
|
local s = arc_fast_vtx[sample_index + 1]
|
||||||
ImVec2_CopyV(self._Path.Data[out_ptr], center.x + s.x * radius, center.y + s.y * radius)
|
local d = path_data[out_ptr]
|
||||||
|
d.x = cx + s.x * radius; d.y = cy + s.y * radius
|
||||||
out_ptr = out_ptr + 1
|
out_ptr = out_ptr + 1
|
||||||
|
|
||||||
a = a + a_step
|
a = a + a_step
|
||||||
@@ -4940,8 +5089,9 @@ function MT.ImDrawList:_PathArcToFastEx(center, radius, a_min_sample, a_max_samp
|
|||||||
sample_index = sample_index + IM_DRAWLIST_ARCFAST_SAMPLE_MAX
|
sample_index = sample_index + IM_DRAWLIST_ARCFAST_SAMPLE_MAX
|
||||||
end
|
end
|
||||||
|
|
||||||
local s = self._Data.ArcFastVtx[sample_index + 1]
|
local s = arc_fast_vtx[sample_index + 1]
|
||||||
ImVec2_CopyV(self._Path.Data[out_ptr], center.x + s.x * radius, center.y + s.y * radius)
|
local d = path_data[out_ptr]
|
||||||
|
d.x = cx + s.x * radius; d.y = cy + s.y * radius
|
||||||
out_ptr = out_ptr + 1
|
out_ptr = out_ptr + 1
|
||||||
|
|
||||||
a = a - a_step
|
a = a - a_step
|
||||||
@@ -4956,14 +5106,19 @@ function MT.ImDrawList:_PathArcToFastEx(center, radius, a_min_sample, a_max_samp
|
|||||||
normalized_max_sample = normalized_max_sample + IM_DRAWLIST_ARCFAST_SAMPLE_MAX
|
normalized_max_sample = normalized_max_sample + IM_DRAWLIST_ARCFAST_SAMPLE_MAX
|
||||||
end
|
end
|
||||||
|
|
||||||
local s = self._Data.ArcFastVtx[normalized_max_sample + 1]
|
local s = arc_fast_vtx[normalized_max_sample + 1]
|
||||||
ImVec2_CopyV(self._Path.Data[out_ptr], center.x + s.x * radius, center.y + s.y * radius)
|
local d = path_data[out_ptr]
|
||||||
|
d.x = cx + s.x * radius; d.y = cy + s.y * radius
|
||||||
out_ptr = out_ptr + 1
|
out_ptr = out_ptr + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
IM_ASSERT_PARANOID(self._Path.Size == out_ptr - 1)
|
IM_ASSERT_PARANOID(self._Path.Size == out_ptr - 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function MT.ImDrawList:_PathArcToFastEx(center, radius, a_min_sample, a_max_sample, a_step)
|
||||||
|
_PathArcToFastExXY(self, center.x, center.y, radius, a_min_sample, a_max_sample, a_step)
|
||||||
|
end
|
||||||
|
|
||||||
-- 0: East, 3: South, 6: West, 9: North, 12: East
|
-- 0: East, 3: South, 6: West, 9: North, 12: East
|
||||||
@native
|
@native
|
||||||
function MT.ImDrawList:PathArcToFast(center, radius, a_min_of_12, a_max_of_12)
|
function MT.ImDrawList:PathArcToFast(center, radius, a_min_of_12, a_max_of_12)
|
||||||
@@ -4972,7 +5127,7 @@ function MT.ImDrawList:PathArcToFast(center, radius, a_min_of_12, a_max_of_12)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
self:_PathArcToFastEx(center, radius, a_min_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, a_max_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 0)
|
_PathArcToFastExXY(self, center.x, center.y, radius, a_min_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, a_max_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param center ImVec2
|
--- @param center ImVec2
|
||||||
@@ -4988,11 +5143,17 @@ function MT.ImDrawList:_PathArcToN(center, radius, a_min, a_max, num_segments)
|
|||||||
|
|
||||||
-- Note that we are adding a point at both a_min and a_max.
|
-- Note that we are adding a point at both a_min and a_max.
|
||||||
-- If you are trying to draw a full closed circle you don't want the overlapping points!
|
-- If you are trying to draw a full closed circle you don't want the overlapping points!
|
||||||
self._Path:reserve(self._Path.Size + (num_segments + 1))
|
local path = self._Path
|
||||||
|
path:reserve(path.Size + (num_segments + 1))
|
||||||
|
-- [Luau] write into the reserved ImVec2 slots: no temporary ImVec2, no push_back() method lookups
|
||||||
|
local data, size = path.Data, path.Size
|
||||||
for i = 0, num_segments do
|
for i = 0, num_segments do
|
||||||
local a = a_min + (i / num_segments) * (a_max - a_min)
|
local a = a_min + (i / num_segments) * (a_max - a_min)
|
||||||
self._Path:push_back(ImVec2(center.x + ImCos(a) * radius, center.y + ImSin(a) * radius))
|
size = size + 1
|
||||||
|
local p = data[size]
|
||||||
|
p.x = center.x + ImCos(a) * radius; p.y = center.y + ImSin(a) * radius
|
||||||
end
|
end
|
||||||
|
path.Size = size
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param center ImVec2
|
--- @param center ImVec2
|
||||||
@@ -5033,9 +5194,9 @@ function MT.ImDrawList:PathArcTo(center, radius, a_min, a_max, num_segments)
|
|||||||
|
|
||||||
self._Path:reserve(self._Path.Size + (a_mid_samples + 1 + (a_emit_start and 1 or 0) + (a_emit_end and 1 or 0)))
|
self._Path:reserve(self._Path.Size + (a_mid_samples + 1 + (a_emit_start and 1 or 0) + (a_emit_end and 1 or 0)))
|
||||||
|
|
||||||
if a_emit_start then self._Path:push_back(ImVec2(center.x + ImCos(a_min) * radius, center.y + ImSin(a_min) * radius)) end
|
if a_emit_start then PathLineToXY(self, center.x + ImCos(a_min) * radius, center.y + ImSin(a_min) * radius) end -- [Luau] no temporary ImVec2
|
||||||
if a_mid_samples > 0 then self:_PathArcToFastEx(center, radius, a_min_sample, a_max_sample, 0) end
|
if a_mid_samples > 0 then _PathArcToFastExXY(self, center.x, center.y, radius, a_min_sample, a_max_sample, 0) end
|
||||||
if a_emit_end then self._Path:push_back(ImVec2(center.x + ImCos(a_max) * radius, center.y + ImSin(a_max) * radius)) end
|
if a_emit_end then PathLineToXY(self, center.x + ImCos(a_max) * radius, center.y + ImSin(a_max) * radius) end
|
||||||
else
|
else
|
||||||
local arc_length = ImAbs(a_max - a_min)
|
local arc_length = ImAbs(a_max - a_min)
|
||||||
local circle_segment_count = self:_CalcCircleAutoSegmentCount(radius)
|
local circle_segment_count = self:_CalcCircleAutoSegmentCount(radius)
|
||||||
@@ -5146,6 +5307,7 @@ function MT.ImFont:RenderText(draw_list, size, pos, col, clip_rect, text, text_b
|
|||||||
-- Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
|
-- Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
|
||||||
if not word_wrap_eol then
|
if not word_wrap_eol then
|
||||||
word_wrap_eol = ImFontCalcWordWrapPositionEx(self, size, text, s, text_end, wrap_width - (x - origin_x), flags)
|
word_wrap_eol = ImFontCalcWordWrapPositionEx(self, size, text, s, text_end, wrap_width - (x - origin_x), flags)
|
||||||
|
lookup, lookup_n, glyphs = baked.IndexLookup.Data, baked.IndexLookup.Size, baked.Glyphs.Data -- [Luau] may have loaded glyphs
|
||||||
end
|
end
|
||||||
|
|
||||||
if s >= word_wrap_eol then
|
if s >= word_wrap_eol then
|
||||||
@@ -5345,18 +5507,24 @@ end
|
|||||||
--- @param pos ImVec2
|
--- @param pos ImVec2
|
||||||
--- @param col ImU32
|
--- @param col ImU32
|
||||||
--- @param sz float
|
--- @param sz float
|
||||||
|
local checkmark_p = ImVec2() -- [Luau] scratch: PathLineTo() copies the point
|
||||||
|
|
||||||
function ImGui.RenderCheckMark(draw_list, pos, col, sz)
|
function ImGui.RenderCheckMark(draw_list, pos, col, sz)
|
||||||
local thickness = ImMax(sz / 5.0, 1.0)
|
local thickness = math.max(sz / 5.0, 1.0)
|
||||||
sz = sz - thickness * 0.5
|
sz = sz - thickness * 0.5
|
||||||
pos = pos + ImVec2(thickness * 0.25, thickness * 0.25)
|
local pos_x, pos_y = pos.x + thickness * 0.25, pos.y + thickness * 0.25 -- [Luau] scalar pos
|
||||||
|
|
||||||
local third = sz / 3.0
|
local third = sz / 3.0
|
||||||
local bx = pos.x + third
|
local bx = pos_x + third
|
||||||
local by = pos.y + sz - third * 0.5
|
local by = pos_y + sz - third * 0.5
|
||||||
|
|
||||||
draw_list:PathLineTo(ImVec2(bx - third, by - third))
|
local p = checkmark_p
|
||||||
draw_list:PathLineTo(ImVec2(bx, by))
|
p.x = bx - third; p.y = by - third
|
||||||
draw_list:PathLineTo(ImVec2(bx + third * 2.0, by - third * 2.0))
|
draw_list:PathLineTo(p)
|
||||||
|
p.x = bx; p.y = by
|
||||||
|
draw_list:PathLineTo(p)
|
||||||
|
p.x = bx + third * 2.0; p.y = by - third * 2.0
|
||||||
|
draw_list:PathLineTo(p)
|
||||||
draw_list:PathStroke(col, thickness)
|
draw_list:PathStroke(col, thickness)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
--- @meta
|
--- @meta
|
||||||
|
|
||||||
local type = ImGui._GetTypeFunc()
|
local type = type -- [Luau] builtin (FASTCALL); ImGui._GetTypeFunc() kept for compatibility
|
||||||
local rawget = rawget; local rawset = rawset
|
local rawget = rawget; local rawset = rawset
|
||||||
|
|
||||||
-- [LuaBitOp](https://bitop.luajit.org/semantics.html)
|
-- [LuaBitOp](https://bitop.luajit.org/semantics.html)
|
||||||
@@ -2130,7 +2130,8 @@ IM_COL32_A_MASK = 0xFF000000
|
|||||||
--- @param G ImU32
|
--- @param G ImU32
|
||||||
--- @param B ImU32
|
--- @param B ImU32
|
||||||
--- @param A ImU32
|
--- @param A ImU32
|
||||||
IM_COL32 = function(R, G, B, A) return (bitOr(bitLShift(A, IM_COL32_A_SHIFT), bitLShift(B, IM_COL32_B_SHIFT), bitLShift(G, IM_COL32_G_SHIFT), bitLShift(R, IM_COL32_R_SHIFT))) end
|
--@hoist
|
||||||
|
function IM_COL32(R, G, B, A) return (bitOr(bitLShift(A, IM_COL32_A_SHIFT), bitLShift(B, IM_COL32_B_SHIFT), bitLShift(G, IM_COL32_G_SHIFT), bitLShift(R, IM_COL32_R_SHIFT))) end
|
||||||
IM_COL32_WHITE = IM_COL32(255, 255, 255, 255)
|
IM_COL32_WHITE = IM_COL32(255, 255, 255, 255)
|
||||||
IM_COL32_BLACK = IM_COL32(0, 0, 0, 255)
|
IM_COL32_BLACK = IM_COL32(0, 0, 0, 255)
|
||||||
IM_COL32_BLACK_TRANS = IM_COL32(0, 0, 0, 0)
|
IM_COL32_BLACK_TRANS = IM_COL32(0, 0, 0, 0)
|
||||||
|
|||||||
+27
-4
@@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
--- @meta
|
--- @meta
|
||||||
|
|
||||||
local type = ImGui._GetTypeFunc()
|
local type = type -- [Luau] builtin (FASTCALL); ImGui._GetTypeFunc() kept for compatibility
|
||||||
local rawget = rawget
|
local rawget = rawget
|
||||||
|
|
||||||
local mathMin = math.min; local mathMax = math.max
|
local mathMin = math.min; local mathMax = math.max
|
||||||
@@ -189,6 +189,7 @@ end
|
|||||||
-- - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support variety of types: signed/unsigned int/long long float/double
|
-- - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support variety of types: signed/unsigned int/long long float/double
|
||||||
--- @overload fun(lhs: number, rhs: number): number
|
--- @overload fun(lhs: number, rhs: number): number
|
||||||
--- @overload fun(lhs: ImVec2, rhs: ImVec2): ImVec2
|
--- @overload fun(lhs: ImVec2, rhs: ImVec2): ImVec2
|
||||||
|
--@hoist
|
||||||
@native
|
@native
|
||||||
function ImMin(lhs, rhs)
|
function ImMin(lhs, rhs)
|
||||||
if type(lhs) == "number" then return mathMin(lhs, rhs) end
|
if type(lhs) == "number" then return mathMin(lhs, rhs) end
|
||||||
@@ -197,6 +198,7 @@ end
|
|||||||
|
|
||||||
--- @overload fun(lhs: number, rhs: number): number
|
--- @overload fun(lhs: number, rhs: number): number
|
||||||
--- @overload fun(lhs: ImVec2, rhs: ImVec2): ImVec2
|
--- @overload fun(lhs: ImVec2, rhs: ImVec2): ImVec2
|
||||||
|
--@hoist
|
||||||
@native
|
@native
|
||||||
function ImMax(lhs, rhs)
|
function ImMax(lhs, rhs)
|
||||||
if type(lhs) == "number" then return mathMax(lhs, rhs) end
|
if type(lhs) == "number" then return mathMax(lhs, rhs) end
|
||||||
@@ -211,6 +213,7 @@ ImStd.ImQsort = function(base, count, cmp_func) if count > 0 then t_sort(base, c
|
|||||||
|
|
||||||
--- @overload fun(v: number, mn: number, mx: number): number
|
--- @overload fun(v: number, mn: number, mx: number): number
|
||||||
--- @overload fun(v: ImVec2, mn: ImVec2, mx: ImVec2): ImVec2
|
--- @overload fun(v: ImVec2, mn: ImVec2, mx: ImVec2): ImVec2
|
||||||
|
--@hoist
|
||||||
@native
|
@native
|
||||||
function ImClamp(v, mn, mx)
|
function ImClamp(v, mn, mx)
|
||||||
if type(v) == "number" and type(mn) == "number" and type(mx) == "number" then return mathMin(mathMax(v, mn), mx)
|
if type(v) == "number" and type(mn) == "number" and type(mx) == "number" then return mathMin(mathMax(v, mn), mx)
|
||||||
@@ -221,6 +224,7 @@ end
|
|||||||
--- @overload fun(a: number, b: number, t: number): number
|
--- @overload fun(a: number, b: number, t: number): number
|
||||||
--- @overload fun(a: ImVec2, b: ImVec2, t: number): ImVec2
|
--- @overload fun(a: ImVec2, b: ImVec2, t: number): ImVec2
|
||||||
--- @overload fun(a: ImVec2, b: ImVec2, t: ImVec2): ImVec2
|
--- @overload fun(a: ImVec2, b: ImVec2, t: ImVec2): ImVec2
|
||||||
|
--@hoist
|
||||||
@native
|
@native
|
||||||
function ImLerp(a, b, t)
|
function ImLerp(a, b, t)
|
||||||
if type(a) == "number" and type(b) == "number" and type(t) == "number" then return ((a) + ((b) - (a)) * (t))
|
if type(a) == "number" and type(b) == "number" and type(t) == "number" then return ((a) + ((b) - (a)) * (t))
|
||||||
@@ -229,10 +233,12 @@ function ImLerp(a, b, t)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--@hoist
|
||||||
local function _Trunc(val) if val >= 0 then return mathFloor(val) else return mathCeil(val) end end
|
local function _Trunc(val) if val >= 0 then return mathFloor(val) else return mathCeil(val) end end
|
||||||
|
|
||||||
--- @overload fun(v: float): float
|
--- @overload fun(v: float): float
|
||||||
--- @overload fun(v: ImVec2): ImVec2
|
--- @overload fun(v: ImVec2): ImVec2
|
||||||
|
--@hoist
|
||||||
function ImTrunc(v)
|
function ImTrunc(v)
|
||||||
if type(v) == "number" then return _Trunc(v)
|
if type(v) == "number" then return _Trunc(v)
|
||||||
elseif type(v) == "table" then return ImVec2(_Trunc(v.x), _Trunc(v.y))
|
elseif type(v) == "table" then return ImVec2(_Trunc(v.x), _Trunc(v.y))
|
||||||
@@ -256,6 +262,7 @@ function ImRsqrt(x) return (1 / ImSqrt(x)) end
|
|||||||
function IM_TRUNC(VAL) return math.floor(VAL) end -- Positive values only!
|
function IM_TRUNC(VAL) return math.floor(VAL) end -- Positive values only!
|
||||||
function IM_ROUND(VAL) return math.floor(VAL + 0.5) end -- Positive values only!
|
function IM_ROUND(VAL) return math.floor(VAL + 0.5) end -- Positive values only!
|
||||||
|
|
||||||
|
--@hoist
|
||||||
function ImFloor(f) if f >= 0 or math.floor(f) == f then return math.floor(f) else return math.floor(f) - 1 end end -- Decent replacement for floorf()
|
function ImFloor(f) if f >= 0 or math.floor(f) == f then return math.floor(f) else return math.floor(f) - 1 end end -- Decent replacement for floorf()
|
||||||
|
|
||||||
-- Helpers: Bit manipulation
|
-- Helpers: Bit manipulation
|
||||||
@@ -376,6 +383,7 @@ function ImStd.strncmp(str1, str2, max_count)
|
|||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--@hoist
|
||||||
function ImSaturate(f) return ((f < 0.0 and 0.0) or (f > 1.0 and 1.0) or f) end
|
function ImSaturate(f) return ((f < 0.0 and 0.0) or (f > 1.0 and 1.0) or f) end
|
||||||
|
|
||||||
IM_F32_TO_INT8_UNBOUND = function(val) return math.floor(val * 255.0 + (val >= 0 and 0.5 or -0.5)) end -- Unsaturated, for display purpose
|
IM_F32_TO_INT8_UNBOUND = function(val) return math.floor(val * 255.0 + (val >= 0 and 0.5 or -0.5)) end -- Unsaturated, for display purpose
|
||||||
@@ -837,14 +845,21 @@ function IM_RECT:GetBR() return ImVec2(self.Max.x, self.Max.y) end -- Bottom-rig
|
|||||||
--- @param r ImRect
|
--- @param r ImRect
|
||||||
@native
|
@native
|
||||||
function IM_RECT:ClipWith(r)
|
function IM_RECT:ClipWith(r)
|
||||||
ImVec2_Copy(self.Min, ImMax(self.Min, r.Min)); ImVec2_Copy(self.Max, ImMin(self.Max, r.Max))
|
-- [Luau] scalar math, no temporary ImVec2 (= ImMax(Min, r.Min) / ImMin(Max, r.Max))
|
||||||
|
local smin, smax, rmin, rmax = self.Min, self.Max, r.Min, r.Max
|
||||||
|
smin.x = mathMax(smin.x, rmin.x); smin.y = mathMax(smin.y, rmin.y)
|
||||||
|
smax.x = mathMin(smax.x, rmax.x); smax.y = mathMin(smax.y, rmax.y)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Full version, ensure both points are fully clipped.
|
-- Full version, ensure both points are fully clipped.
|
||||||
--- @param r ImRect
|
--- @param r ImRect
|
||||||
@native
|
@native
|
||||||
function IM_RECT:ClipWithFull(r)
|
function IM_RECT:ClipWithFull(r)
|
||||||
ImVec2_Copy(self.Min, ImClamp(self.Min, r.Min, r.Max)); ImVec2_Copy(self.Max, ImClamp(self.Max, r.Min, r.Max))
|
-- [Luau] scalar math, no temporary ImVec2 (= ImClamp(Min/Max, r.Min, r.Max))
|
||||||
|
local smin, smax, rmin, rmax = self.Min, self.Max, r.Min, r.Max
|
||||||
|
local min_x, min_y, max_x, max_y = rmin.x, rmin.y, rmax.x, rmax.y
|
||||||
|
smin.x = mathMax(min_x, mathMin(smin.x, max_x)); smin.y = mathMax(min_y, mathMin(smin.y, max_y))
|
||||||
|
smax.x = mathMax(min_x, mathMin(smax.x, max_x)); smax.y = mathMax(min_y, mathMin(smax.y, max_y))
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param p ImRect|ImVec2
|
--- @param p ImRect|ImVec2
|
||||||
@@ -983,7 +998,15 @@ function MT.ImDrawList:PathClear() self._Path:clear() end
|
|||||||
|
|
||||||
--- @param pos ImVec2
|
--- @param pos ImVec2
|
||||||
@native
|
@native
|
||||||
function MT.ImDrawList:PathLineTo(pos) self._Path:push_back(pos) end
|
function MT.ImDrawList:PathLineTo(pos)
|
||||||
|
-- [Luau] inline self._Path:push_back(pos) (ImVec2_TCopy into the preallocated slot)
|
||||||
|
local path = self._Path
|
||||||
|
local n = path.Size + 1
|
||||||
|
if n > path.Capacity then path:push_back(pos) return end
|
||||||
|
local dest = path.Data[n]
|
||||||
|
dest.x = pos.x; dest.y = pos.y
|
||||||
|
path.Size = n
|
||||||
|
end
|
||||||
|
|
||||||
function MT.ImDrawList:PathLineToMergeDuplicate(pos)
|
function MT.ImDrawList:PathLineToMergeDuplicate(pos)
|
||||||
local path_size = self._Path.Size
|
local path_size = self._Path.Size
|
||||||
|
|||||||
+185
-121
@@ -200,6 +200,7 @@ end
|
|||||||
|
|
||||||
local bit32 = bit32
|
local bit32 = bit32
|
||||||
local band, bor, bnot, lshift, rshift = bit32.band, bit32.bor, bit32.bnot, bit32.lshift, bit32.rshift
|
local band, bor, bnot, lshift, rshift = bit32.band, bit32.bor, bit32.bnot, bit32.lshift, bit32.rshift
|
||||||
|
local mathMax, mathMin = math.max, math.min -- [Luau] scalar ImMax()/ImMin() in table code are written as mathMax()/mathMin() (no call through the vector-aware helper)
|
||||||
|
|
||||||
IM_COL32_DISABLE = IM_COL32(0, 0, 0, 1) -- Special sentinel code which cannot be used as a regular color.
|
IM_COL32_DISABLE = IM_COL32(0, 0, 0, 1) -- Special sentinel code which cannot be used as a regular color.
|
||||||
IMGUI_TABLE_MAX_COLUMNS = 512 -- Arbitrary "safety" maximum, may be lifted in the future if needed. Must fit in ImGuiTableColumnIdx/ImGuiTableDrawChannelIdx.
|
IMGUI_TABLE_MAX_COLUMNS = 512 -- Arbitrary "safety" maximum, may be lifted in the future if needed. Must fit in ImGuiTableColumnIdx/ImGuiTableDrawChannelIdx.
|
||||||
@@ -482,7 +483,7 @@ function ImGui.BeginTableEx(name, id, columns_count, flags, outer_size, inner_wi
|
|||||||
-- - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[].
|
-- - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[].
|
||||||
local use_child_window = band(flags, bor(ImGuiTableFlags.ScrollX, ImGuiTableFlags.ScrollY)) ~= 0
|
local use_child_window = band(flags, bor(ImGuiTableFlags.ScrollX, ImGuiTableFlags.ScrollY)) ~= 0
|
||||||
local avail_size = ImGui.GetContentRegionAvail()
|
local avail_size = ImGui.GetContentRegionAvail()
|
||||||
local actual_outer_size = ImTrunc(ImGui.CalcItemSize(outer_size, ImMax(avail_size.x, IMGUI_WINDOW_HARD_MIN_SIZE), use_child_window and ImMax(avail_size.y, IMGUI_WINDOW_HARD_MIN_SIZE) or 0.0))
|
local actual_outer_size = ImTrunc(ImGui.CalcItemSize(outer_size, mathMax(avail_size.x, IMGUI_WINDOW_HARD_MIN_SIZE), use_child_window and mathMax(avail_size.y, IMGUI_WINDOW_HARD_MIN_SIZE) or 0.0))
|
||||||
local outer_rect = ImRect(outer_window.DC.CursorPos, outer_window.DC.CursorPos + actual_outer_size)
|
local outer_rect = ImRect(outer_window.DC.CursorPos, outer_window.DC.CursorPos + actual_outer_size)
|
||||||
local outer_window_is_measuring_size = (outer_window.AutoFitFramesX > 0) or (outer_window.AutoFitFramesY > 0) -- Doesn't apply to AlwaysAutoResize windows!
|
local outer_window_is_measuring_size = (outer_window.AutoFitFramesX > 0) or (outer_window.AutoFitFramesY > 0) -- Doesn't apply to AlwaysAutoResize windows!
|
||||||
if use_child_window and ImGui.IsClippedEx(outer_rect, 0) and not outer_window_is_measuring_size then
|
if use_child_window and ImGui.IsClippedEx(outer_rect, 0) and not outer_window_is_measuring_size then
|
||||||
@@ -638,15 +639,15 @@ function ImGui.BeginTableEx(name, id, columns_count, flags, outer_size, inner_wi
|
|||||||
-- different x/y values to BeginChild().
|
-- different x/y values to BeginChild().
|
||||||
local border_size = TABLE_BORDER_SIZE
|
local border_size = TABLE_BORDER_SIZE
|
||||||
if band(flags, ImGuiTableFlags.BordersOuterV) ~= 0 then
|
if band(flags, ImGuiTableFlags.BordersOuterV) ~= 0 then
|
||||||
table_.HostClipRect.Min.x = ImMin(table_.HostClipRect.Min.x + border_size, table_.HostClipRect.Max.x)
|
table_.HostClipRect.Min.x = mathMin(table_.HostClipRect.Min.x + border_size, table_.HostClipRect.Max.x)
|
||||||
if inner_window.DecoOuterSizeX2 == 0.0 then
|
if inner_window.DecoOuterSizeX2 == 0.0 then
|
||||||
table_.HostClipRect.Max.x = ImMax(table_.HostClipRect.Max.x - border_size, table_.HostClipRect.Min.x)
|
table_.HostClipRect.Max.x = mathMax(table_.HostClipRect.Max.x - border_size, table_.HostClipRect.Min.x)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if band(flags, ImGuiTableFlags.BordersOuterH) ~= 0 then
|
if band(flags, ImGuiTableFlags.BordersOuterH) ~= 0 then
|
||||||
table_.HostClipRect.Min.y = ImMin(table_.HostClipRect.Min.y + border_size, table_.HostClipRect.Max.y)
|
table_.HostClipRect.Min.y = mathMin(table_.HostClipRect.Min.y + border_size, table_.HostClipRect.Max.y)
|
||||||
if inner_window.DecoOuterSizeY2 == 0.0 then
|
if inner_window.DecoOuterSizeY2 == 0.0 then
|
||||||
table_.HostClipRect.Max.y = ImMax(table_.HostClipRect.Max.y - border_size, table_.HostClipRect.Min.y)
|
table_.HostClipRect.Max.y = mathMax(table_.HostClipRect.Max.y - border_size, table_.HostClipRect.Min.y)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -679,7 +680,7 @@ function ImGui.BeginTableEx(name, id, columns_count, flags, outer_size, inner_wi
|
|||||||
ImRect_Copy(table_.InnerClipRect, (inner_window == outer_window) and table_.WorkRect or inner_window.ClipRect)
|
ImRect_Copy(table_.InnerClipRect, (inner_window == outer_window) and table_.WorkRect or inner_window.ClipRect)
|
||||||
table_.InnerClipRect:ClipWith(table_.WorkRect) -- We need this to honor inner_width
|
table_.InnerClipRect:ClipWith(table_.WorkRect) -- We need this to honor inner_width
|
||||||
table_.InnerClipRect:ClipWithFull(table_.HostClipRect)
|
table_.InnerClipRect:ClipWithFull(table_.HostClipRect)
|
||||||
table_.InnerClipRect.Max.y = (band(flags, ImGuiTableFlags.NoHostExtendY) ~= 0) and ImMin(table_.InnerClipRect.Max.y, inner_window.WorkRect.Max.y) or table_.HostClipRect.Max.y
|
table_.InnerClipRect.Max.y = (band(flags, ImGuiTableFlags.NoHostExtendY) ~= 0) and mathMin(table_.InnerClipRect.Max.y, inner_window.WorkRect.Max.y) or table_.HostClipRect.Max.y
|
||||||
|
|
||||||
table_.RowPosY1 = table_.WorkRect.Min.y -- This is needed somehow
|
table_.RowPosY1 = table_.WorkRect.Min.y -- This is needed somehow
|
||||||
table_.RowPosY2 = table_.WorkRect.Min.y
|
table_.RowPosY2 = table_.WorkRect.Min.y
|
||||||
@@ -1050,7 +1051,7 @@ function ImGui.TableUpdateLayout(table_)
|
|||||||
BitArrayClearAll(table_.EnabledMaskByIndex)
|
BitArrayClearAll(table_.EnabledMaskByIndex)
|
||||||
BitArrayClearAll(table_.EnabledMaskByDisplayOrder)
|
BitArrayClearAll(table_.EnabledMaskByDisplayOrder)
|
||||||
table_.LeftMostEnabledColumn = -1
|
table_.LeftMostEnabledColumn = -1
|
||||||
table_.MinColumnWidth = ImMax(1.0, g.Style.FramePadding.x * 1.0) -- g.Style.ColumnsMinSpacing -- FIXME-TABLE
|
table_.MinColumnWidth = mathMax(1.0, g.Style.FramePadding.x * 1.0) -- g.Style.ColumnsMinSpacing -- FIXME-TABLE
|
||||||
|
|
||||||
-- [Part 1] Apply/lock Enabled and Order states. Calculate auto/ideal width for columns. Count fixed/stretch columns.
|
-- [Part 1] Apply/lock Enabled and Order states. Calculate auto/ideal width for columns. Count fixed/stretch columns.
|
||||||
-- Process columns in their visible orders as we are building the Prev/Next indices.
|
-- Process columns in their visible orders as we are building the Prev/Next indices.
|
||||||
@@ -1152,7 +1153,7 @@ function ImGui.TableUpdateLayout(table_)
|
|||||||
stretch_sum_width_auto = stretch_sum_width_auto + column.WidthAuto
|
stretch_sum_width_auto = stretch_sum_width_auto + column.WidthAuto
|
||||||
count_stretch = count_stretch + 1
|
count_stretch = count_stretch + 1
|
||||||
else
|
else
|
||||||
fixed_max_width_auto = ImMax(fixed_max_width_auto, column.WidthAuto)
|
fixed_max_width_auto = mathMax(fixed_max_width_auto, column.WidthAuto)
|
||||||
count_fixed = count_fixed + 1
|
count_fixed = count_fixed + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1204,7 +1205,7 @@ function ImGui.TableUpdateLayout(table_)
|
|||||||
-- FIXME: Move this to .WidthGiven to avoid temporary lossyness?
|
-- FIXME: Move this to .WidthGiven to avoid temporary lossyness?
|
||||||
-- FIXME: This break IsPreserveWidthAuto from not flickering if the stored WidthAuto was smaller.
|
-- FIXME: This break IsPreserveWidthAuto from not flickering if the stored WidthAuto was smaller.
|
||||||
if column.AutoFitQueue > 0x01 and table_.IsInitializing and not column.IsPreserveWidthAuto then
|
if column.AutoFitQueue > 0x01 and table_.IsInitializing and not column.IsPreserveWidthAuto then
|
||||||
column.WidthRequest = ImMax(column.WidthRequest, table_.MinColumnWidth * 4.0) -- FIXME-TABLE: Another constant/scale?
|
column.WidthRequest = mathMax(column.WidthRequest, table_.MinColumnWidth * 4.0) -- FIXME-TABLE: Another constant/scale?
|
||||||
end
|
end
|
||||||
sum_width_requests = sum_width_requests + column.WidthRequest
|
sum_width_requests = sum_width_requests + column.WidthRequest
|
||||||
else
|
else
|
||||||
@@ -1238,7 +1239,7 @@ function ImGui.TableUpdateLayout(table_)
|
|||||||
local work_rect = Rect(table_.WorkRect)
|
local work_rect = Rect(table_.WorkRect)
|
||||||
local width_spacings = (table_.OuterPaddingX * 2.0) + (table_.CellSpacingX1 + table_.CellSpacingX2) * (table_.ColumnsEnabledCount - 1)
|
local width_spacings = (table_.OuterPaddingX * 2.0) + (table_.CellSpacingX1 + table_.CellSpacingX2) * (table_.ColumnsEnabledCount - 1)
|
||||||
local width_removed = (table_.HasScrollbarYPrev and not table_.InnerWindow.ScrollbarY) and g.Style.ScrollbarSize or 0.0 -- To synchronize decoration width of synced tables with mismatching scrollbar state (#5920)
|
local width_removed = (table_.HasScrollbarYPrev and not table_.InnerWindow.ScrollbarY) and g.Style.ScrollbarSize or 0.0 -- To synchronize decoration width of synced tables with mismatching scrollbar state (#5920)
|
||||||
local width_avail = ImMax(1.0, ((band(table_.Flags, ImGuiTableFlags.ScrollX) ~= 0 and table_.InnerWidth == 0.0) and table_.InnerClipRect:GetWidth() or work_rect:GetWidth()) - width_removed)
|
local width_avail = mathMax(1.0, ((band(table_.Flags, ImGuiTableFlags.ScrollX) ~= 0 and table_.InnerWidth == 0.0) and table_.InnerClipRect:GetWidth() or work_rect:GetWidth()) - width_removed)
|
||||||
local width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests
|
local width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests
|
||||||
local width_remaining_for_stretched_columns = width_avail_for_stretched_columns
|
local width_remaining_for_stretched_columns = width_avail_for_stretched_columns
|
||||||
table_.ColumnsGivenWidth = width_spacings + (table_.CellPaddingX * 2.0) * table_.ColumnsEnabledCount
|
table_.ColumnsGivenWidth = width_spacings + (table_.CellPaddingX * 2.0) * table_.ColumnsEnabledCount
|
||||||
@@ -1249,7 +1250,7 @@ function ImGui.TableUpdateLayout(table_)
|
|||||||
-- Allocate width for stretched/weighted columns (StretchWeight gets converted into WidthRequest)
|
-- Allocate width for stretched/weighted columns (StretchWeight gets converted into WidthRequest)
|
||||||
if band(column.Flags, ImGuiTableColumnFlags.WidthStretch) ~= 0 then
|
if band(column.Flags, ImGuiTableColumnFlags.WidthStretch) ~= 0 then
|
||||||
local weight_ratio = column.StretchWeight / stretch_sum_weights
|
local weight_ratio = column.StretchWeight / stretch_sum_weights
|
||||||
column.WidthRequest = IM_TRUNC(ImMax(width_avail_for_stretched_columns * weight_ratio, table_.MinColumnWidth) + 0.01)
|
column.WidthRequest = IM_TRUNC(mathMax(width_avail_for_stretched_columns * weight_ratio, table_.MinColumnWidth) + 0.01)
|
||||||
width_remaining_for_stretched_columns = width_remaining_for_stretched_columns - column.WidthRequest
|
width_remaining_for_stretched_columns = width_remaining_for_stretched_columns - column.WidthRequest
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1260,7 +1261,7 @@ function ImGui.TableUpdateLayout(table_)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Assign final width, record width in case we will need to shrink
|
-- Assign final width, record width in case we will need to shrink
|
||||||
column.WidthGiven = ImTrunc(ImMax(column.WidthRequest, table_.MinColumnWidth))
|
column.WidthGiven = ImTrunc(mathMax(column.WidthRequest, table_.MinColumnWidth))
|
||||||
table_.ColumnsGivenWidth = table_.ColumnsGivenWidth + column.WidthGiven
|
table_.ColumnsGivenWidth = table_.ColumnsGivenWidth + column.WidthGiven
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1291,7 +1292,7 @@ function ImGui.TableUpdateLayout(table_)
|
|||||||
table_instance.HoveredRowLast = table_instance.HoveredRowNext
|
table_instance.HoveredRowLast = table_instance.HoveredRowNext
|
||||||
table_instance.HoveredRowNext = -1
|
table_instance.HoveredRowNext = -1
|
||||||
table_.HoveredColumnBody = -1; table_.HoveredColumnBorder = -1
|
table_.HoveredColumnBody = -1; table_.HoveredColumnBorder = -1
|
||||||
local mouse_hit_rect = ImRect(table_.OuterRect.Min.x, table_.OuterRect.Min.y, table_.OuterRect.Max.x, ImMax(table_.OuterRect.Max.y, table_.OuterRect.Min.y + table_instance.LastOuterHeight))
|
local mouse_hit_rect = ImRect(table_.OuterRect.Min.x, table_.OuterRect.Min.y, table_.OuterRect.Max.x, mathMax(table_.OuterRect.Max.y, table_.OuterRect.Min.y + table_instance.LastOuterHeight))
|
||||||
local backup_active_id = g.ActiveId
|
local backup_active_id = g.ActiveId
|
||||||
g.ActiveId = 0
|
g.ActiveId = 0
|
||||||
local is_hovering_table = ImGui.ItemHoverable(mouse_hit_rect, 0, ImGuiItemFlags.None)
|
local is_hovering_table = ImGui.ItemHoverable(mouse_hit_rect, 0, ImGuiItemFlags.None)
|
||||||
@@ -1346,8 +1347,8 @@ function ImGui.TableUpdateLayout(table_)
|
|||||||
|
|
||||||
-- Lock width based on start position and minimum/maximum width for this position
|
-- Lock width based on start position and minimum/maximum width for this position
|
||||||
column.WidthMax = ImGui.TableCalcMaxColumnWidth(table_, column_n)
|
column.WidthMax = ImGui.TableCalcMaxColumnWidth(table_, column_n)
|
||||||
column.WidthGiven = ImMin(column.WidthGiven, column.WidthMax)
|
column.WidthGiven = mathMin(column.WidthGiven, column.WidthMax)
|
||||||
column.WidthGiven = ImMax(column.WidthGiven, ImMin(column.WidthRequest, table_.MinColumnWidth))
|
column.WidthGiven = mathMax(column.WidthGiven, mathMin(column.WidthRequest, table_.MinColumnWidth))
|
||||||
column.MaxX = offset_x + column.WidthGiven + table_.CellSpacingX1 + table_.CellSpacingX2 + table_.CellPaddingX * 2.0
|
column.MaxX = offset_x + column.WidthGiven + table_.CellSpacingX1 + table_.CellSpacingX2 + table_.CellPaddingX * 2.0
|
||||||
|
|
||||||
-- Lock other positions
|
-- Lock other positions
|
||||||
@@ -1455,7 +1456,7 @@ function ImGui.TableUpdateLayout(table_)
|
|||||||
-- [Part 7] Detect/store when we are hovering the unused space after the right-most column (so e.g. context menus can react on it)
|
-- [Part 7] Detect/store when we are hovering the unused space after the right-most column (so e.g. context menus can react on it)
|
||||||
-- Clear Resizable flag if none of our column are actually resizable (either via an explicit _NoResize flag, either
|
-- Clear Resizable flag if none of our column are actually resizable (either via an explicit _NoResize flag, either
|
||||||
-- because of using _WidthAuto/_WidthStretch). This will hide the resizing option from the context menu.
|
-- because of using _WidthAuto/_WidthStretch). This will hide the resizing option from the context menu.
|
||||||
local unused_x1 = ImMax(table_.WorkRect.Min.x, table_.Columns[table_.RightMostEnabledColumn].ClipRect.Max.x)
|
local unused_x1 = mathMax(table_.WorkRect.Min.x, table_.Columns[table_.RightMostEnabledColumn].ClipRect.Max.x)
|
||||||
if is_hovering_table and table_.HoveredColumnBody == -1 then
|
if is_hovering_table and table_.HoveredColumnBody == -1 then
|
||||||
if mouse_skewed_x >= unused_x1 then
|
if mouse_skewed_x >= unused_x1 then
|
||||||
table_.HoveredColumnBody = columns_count
|
table_.HoveredColumnBody = columns_count
|
||||||
@@ -1475,7 +1476,7 @@ function ImGui.TableUpdateLayout(table_)
|
|||||||
end
|
end
|
||||||
if band(table_.Flags, ImGuiTableFlags.NoHostExtendX) ~= 0 then
|
if band(table_.Flags, ImGuiTableFlags.NoHostExtendX) ~= 0 then
|
||||||
table_.OuterRect.Max.x = unused_x1; table_.WorkRect.Max.x = unused_x1
|
table_.OuterRect.Max.x = unused_x1; table_.WorkRect.Max.x = unused_x1
|
||||||
table_.InnerClipRect.Max.x = ImMin(table_.InnerClipRect.Max.x, unused_x1)
|
table_.InnerClipRect.Max.x = mathMin(table_.InnerClipRect.Max.x, unused_x1)
|
||||||
end
|
end
|
||||||
ImRect_Copy(table_.InnerWindow.ParentWorkRect, table_.WorkRect)
|
ImRect_Copy(table_.InnerWindow.ParentWorkRect, table_.WorkRect)
|
||||||
table_.BorderX1 = table_.InnerClipRect.Min.x
|
table_.BorderX1 = table_.InnerClipRect.Min.x
|
||||||
@@ -1486,7 +1487,7 @@ function ImGui.TableUpdateLayout(table_)
|
|||||||
if band(table_.Flags, ImGuiTableFlags.NoHostExtendY) ~= 0 then
|
if band(table_.Flags, ImGuiTableFlags.NoHostExtendY) ~= 0 then
|
||||||
window_content_max_y = table_.OuterRect.Max.y
|
window_content_max_y = table_.OuterRect.Max.y
|
||||||
else
|
else
|
||||||
window_content_max_y = ImMax(table_.InnerWindow.ContentRegionRect.Max.y, (band(table_.Flags, ImGuiTableFlags.ScrollY) ~= 0) and 0.0 or table_.OuterRect.Max.y)
|
window_content_max_y = mathMax(table_.InnerWindow.ContentRegionRect.Max.y, (band(table_.Flags, ImGuiTableFlags.ScrollY) ~= 0) and 0.0 or table_.OuterRect.Max.y)
|
||||||
end
|
end
|
||||||
table_.InnerWindow.WorkRect.Max.y = ImClamp(window_content_max_y - g.Style.CellPadding.y, table_.InnerWindow.WorkRect.Min.y, table_.InnerWindow.WorkRect.Max.y)
|
table_.InnerWindow.WorkRect.Max.y = ImClamp(window_content_max_y - g.Style.CellPadding.y, table_.InnerWindow.WorkRect.Min.y, table_.InnerWindow.WorkRect.Max.y)
|
||||||
|
|
||||||
@@ -1578,7 +1579,7 @@ function ImGui.TableUpdateBorders(table_)
|
|||||||
local table_instance = ImGui.TableGetInstanceData(table_, table_.InstanceCurrent)
|
local table_instance = ImGui.TableGetInstanceData(table_, table_.InstanceCurrent)
|
||||||
local hit_half_width = ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale)
|
local hit_half_width = ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale)
|
||||||
local hit_y1 = ((table_.FreezeRowsCount >= 1) and table_.OuterRect.Min.y or table_.WorkRect.Min.y) + table_.AngledHeadersHeight
|
local hit_y1 = ((table_.FreezeRowsCount >= 1) and table_.OuterRect.Min.y or table_.WorkRect.Min.y) + table_.AngledHeadersHeight
|
||||||
local hit_y2_body = ImMax(table_.OuterRect.Max.y, hit_y1 + table_instance.LastOuterHeight - table_.AngledHeadersHeight)
|
local hit_y2_body = mathMax(table_.OuterRect.Max.y, hit_y1 + table_instance.LastOuterHeight - table_.AngledHeadersHeight)
|
||||||
local hit_y2_head = hit_y1 + table_instance.LastTopHeadersRowHeight
|
local hit_y2_head = hit_y1 + table_instance.LastTopHeadersRowHeight
|
||||||
|
|
||||||
for order_n = 0, table_.ColumnsCount - 1 do
|
for order_n = 0, table_.ColumnsCount - 1 do
|
||||||
@@ -1672,10 +1673,10 @@ function ImGui.EndTable()
|
|||||||
if inner_window ~= outer_window then
|
if inner_window ~= outer_window then
|
||||||
inner_window.DC.CursorMaxPos.y = inner_content_max_y
|
inner_window.DC.CursorMaxPos.y = inner_content_max_y
|
||||||
elseif band(flags, ImGuiTableFlags.NoHostExtendY) == 0 then
|
elseif band(flags, ImGuiTableFlags.NoHostExtendY) == 0 then
|
||||||
local v = ImMax(table_.OuterRect.Max.y, inner_content_max_y) -- Patch OuterRect/InnerRect height
|
local v = mathMax(table_.OuterRect.Max.y, inner_content_max_y) -- Patch OuterRect/InnerRect height
|
||||||
table_.OuterRect.Max.y = v; table_.InnerRect.Max.y = v
|
table_.OuterRect.Max.y = v; table_.InnerRect.Max.y = v
|
||||||
end
|
end
|
||||||
table_.WorkRect.Max.y = ImMax(table_.WorkRect.Max.y, table_.OuterRect.Max.y)
|
table_.WorkRect.Max.y = mathMax(table_.WorkRect.Max.y, table_.OuterRect.Max.y)
|
||||||
table_instance.LastOuterHeight = table_.OuterRect:GetHeight()
|
table_instance.LastOuterHeight = table_.OuterRect:GetHeight()
|
||||||
|
|
||||||
-- Setup inner scrolling range
|
-- Setup inner scrolling range
|
||||||
@@ -1685,10 +1686,10 @@ function ImGui.EndTable()
|
|||||||
local outer_padding_for_border = (band(table_.Flags, ImGuiTableFlags.BordersOuterV) ~= 0) and TABLE_BORDER_SIZE or 0.0
|
local outer_padding_for_border = (band(table_.Flags, ImGuiTableFlags.BordersOuterV) ~= 0) and TABLE_BORDER_SIZE or 0.0
|
||||||
local max_pos_x = inner_window.DC.CursorMaxPos.x
|
local max_pos_x = inner_window.DC.CursorMaxPos.x
|
||||||
if table_.RightMostEnabledColumn ~= -1 then
|
if table_.RightMostEnabledColumn ~= -1 then
|
||||||
max_pos_x = ImMax(max_pos_x, table_.Columns[table_.RightMostEnabledColumn].WorkMaxX + table_.CellPaddingX + table_.OuterPaddingX - outer_padding_for_border)
|
max_pos_x = mathMax(max_pos_x, table_.Columns[table_.RightMostEnabledColumn].WorkMaxX + table_.CellPaddingX + table_.OuterPaddingX - outer_padding_for_border)
|
||||||
end
|
end
|
||||||
if table_.ResizedColumn ~= -1 then
|
if table_.ResizedColumn ~= -1 then
|
||||||
max_pos_x = ImMax(max_pos_x, table_.ResizeLockMinContentsX2)
|
max_pos_x = mathMax(max_pos_x, table_.ResizeLockMinContentsX2)
|
||||||
end
|
end
|
||||||
inner_window.DC.CursorMaxPos.x = max_pos_x + table_.TempData.AngledHeadersExtraWidth
|
inner_window.DC.CursorMaxPos.x = max_pos_x + table_.TempData.AngledHeadersExtraWidth
|
||||||
end
|
end
|
||||||
@@ -1731,12 +1732,12 @@ function ImGui.EndTable()
|
|||||||
auto_fit_width_for_stretched = auto_fit_width_for_stretched + column_width_request
|
auto_fit_width_for_stretched = auto_fit_width_for_stretched + column_width_request
|
||||||
end
|
end
|
||||||
if band(column.Flags, ImGuiTableColumnFlags.WidthStretch) ~= 0 and band(column.Flags, ImGuiTableColumnFlags.NoResize) ~= 0 then
|
if band(column.Flags, ImGuiTableColumnFlags.WidthStretch) ~= 0 and band(column.Flags, ImGuiTableColumnFlags.NoResize) ~= 0 then
|
||||||
auto_fit_width_for_stretched_min = ImMax(auto_fit_width_for_stretched_min, column_width_request / (column.StretchWeight / table_.ColumnsStretchSumWeights))
|
auto_fit_width_for_stretched_min = mathMax(auto_fit_width_for_stretched_min, column_width_request / (column.StretchWeight / table_.ColumnsStretchSumWeights))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local width_spacings = (table_.OuterPaddingX * 2.0) + (table_.CellSpacingX1 + table_.CellSpacingX2) * (table_.ColumnsEnabledCount - 1)
|
local width_spacings = (table_.OuterPaddingX * 2.0) + (table_.CellSpacingX1 + table_.CellSpacingX2) * (table_.ColumnsEnabledCount - 1)
|
||||||
table_.ColumnsAutoFitWidth = width_spacings + (table_.CellPaddingX * 2.0) * table_.ColumnsEnabledCount + auto_fit_width_for_fixed + ImMax(auto_fit_width_for_stretched, auto_fit_width_for_stretched_min)
|
table_.ColumnsAutoFitWidth = width_spacings + (table_.CellPaddingX * 2.0) * table_.ColumnsEnabledCount + auto_fit_width_for_fixed + mathMax(auto_fit_width_for_stretched, auto_fit_width_for_stretched_min)
|
||||||
|
|
||||||
-- Update scroll
|
-- Update scroll
|
||||||
if band(table_.Flags, ImGuiTableFlags.ScrollX) == 0 and inner_window ~= outer_window then
|
if band(table_.Flags, ImGuiTableFlags.ScrollX) == 0 and inner_window ~= outer_window then
|
||||||
@@ -1801,27 +1802,27 @@ function ImGui.EndTable()
|
|||||||
-- FIXME-TABLE: Could we remove this section?
|
-- FIXME-TABLE: Could we remove this section?
|
||||||
-- ColumnsAutoFitWidth may be one frame ahead here since for Fixed+NoResize is calculated from latest contents
|
-- ColumnsAutoFitWidth may be one frame ahead here since for Fixed+NoResize is calculated from latest contents
|
||||||
IM_ASSERT(band(table_.Flags, ImGuiTableFlags.ScrollX) == 0)
|
IM_ASSERT(band(table_.Flags, ImGuiTableFlags.ScrollX) == 0)
|
||||||
outer_window.DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table_.OuterRect.Min.x + table_.ColumnsAutoFitWidth)
|
outer_window.DC.CursorMaxPos.x = mathMax(backup_outer_max_pos.x, table_.OuterRect.Min.x + table_.ColumnsAutoFitWidth)
|
||||||
elseif temp_data.UserOuterSize.x <= 0.0 then
|
elseif temp_data.UserOuterSize.x <= 0.0 then
|
||||||
-- Some references for this: #7651 + tests "table_reported_size", "table_reported_size_outer" equivalent Y block, #9352
|
-- Some references for this: #7651 + tests "table_reported_size", "table_reported_size_outer" equivalent Y block, #9352
|
||||||
-- - FIXME-TABLE: Would make sense to pre-compute expected scrollbar visibility/sizes to generally save a frame of feedback? See broken test in 'table_reported_size_outer'
|
-- - FIXME-TABLE: Would make sense to pre-compute expected scrollbar visibility/sizes to generally save a frame of feedback? See broken test in 'table_reported_size_outer'
|
||||||
local outer_content_max_x = table_.OuterRect.Min.x + table_.ColumnsAutoFitWidth
|
local outer_content_max_x = table_.OuterRect.Min.x + table_.ColumnsAutoFitWidth
|
||||||
local decoration_size = table_.TempData.AngledHeadersExtraWidth + ((inner_window ~= outer_window) and inner_window.ScrollbarSizes.x or 0.0)
|
local decoration_size = table_.TempData.AngledHeadersExtraWidth + ((inner_window ~= outer_window) and inner_window.ScrollbarSizes.x or 0.0)
|
||||||
outer_window.DC.IdealMaxPos.x = ImMax(outer_window.DC.IdealMaxPos.x, outer_content_max_x + decoration_size - temp_data.UserOuterSize.x)
|
outer_window.DC.IdealMaxPos.x = mathMax(outer_window.DC.IdealMaxPos.x, outer_content_max_x + decoration_size - temp_data.UserOuterSize.x)
|
||||||
outer_window.DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table_.OuterRect.Max.x, outer_content_max_x + decoration_size))
|
outer_window.DC.CursorMaxPos.x = mathMax(backup_outer_max_pos.x, mathMin(table_.OuterRect.Max.x, outer_content_max_x + decoration_size))
|
||||||
else
|
else
|
||||||
outer_window.DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table_.OuterRect.Max.x)
|
outer_window.DC.CursorMaxPos.x = mathMax(backup_outer_max_pos.x, table_.OuterRect.Max.x)
|
||||||
end
|
end
|
||||||
if temp_data.UserOuterSize.y <= 0.0 then
|
if temp_data.UserOuterSize.y <= 0.0 then
|
||||||
-- (same comment as above)
|
-- (same comment as above)
|
||||||
local outer_content_size_y = (inner_window == outer_window) and (inner_content_max_y - table_.InnerRect.Min.y) or (inner_content_max_y - inner_window.DC.CursorStartPos.y)
|
local outer_content_size_y = (inner_window == outer_window) and (inner_content_max_y - table_.InnerRect.Min.y) or (inner_content_max_y - inner_window.DC.CursorStartPos.y)
|
||||||
local outer_content_max_y = table_.OuterRect.Min.y + outer_content_size_y
|
local outer_content_max_y = table_.OuterRect.Min.y + outer_content_size_y
|
||||||
local decoration_size = (inner_window ~= outer_window) and inner_window.ScrollbarSizes.y or 0.0
|
local decoration_size = (inner_window ~= outer_window) and inner_window.ScrollbarSizes.y or 0.0
|
||||||
outer_window.DC.IdealMaxPos.y = ImMax(outer_window.DC.IdealMaxPos.y, outer_content_max_y + decoration_size - temp_data.UserOuterSize.y)
|
outer_window.DC.IdealMaxPos.y = mathMax(outer_window.DC.IdealMaxPos.y, outer_content_max_y + decoration_size - temp_data.UserOuterSize.y)
|
||||||
outer_window.DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table_.OuterRect.Max.y, outer_content_max_y + decoration_size))
|
outer_window.DC.CursorMaxPos.y = mathMax(backup_outer_max_pos.y, mathMin(table_.OuterRect.Max.y, outer_content_max_y + decoration_size))
|
||||||
else
|
else
|
||||||
-- OuterRect.Max.y may already have been pushed downward from the initial value (unless ImGuiTableFlags.NoHostExtendY is set)
|
-- OuterRect.Max.y may already have been pushed downward from the initial value (unless ImGuiTableFlags.NoHostExtendY is set)
|
||||||
outer_window.DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, table_.OuterRect.Max.y)
|
outer_window.DC.CursorMaxPos.y = mathMax(backup_outer_max_pos.y, table_.OuterRect.Max.y)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Save settings
|
-- Save settings
|
||||||
@@ -2039,7 +2040,7 @@ function ImGui.TableSetupScrollFreeze(columns, rows)
|
|||||||
IM_ASSERT(columns >= 0 and columns < IMGUI_TABLE_MAX_COLUMNS)
|
IM_ASSERT(columns >= 0 and columns < IMGUI_TABLE_MAX_COLUMNS)
|
||||||
IM_ASSERT(rows >= 0 and rows < 128) -- Arbitrary limit
|
IM_ASSERT(rows >= 0 and rows < 128) -- Arbitrary limit
|
||||||
|
|
||||||
table_.FreezeColumnsRequest = (band(table_.Flags, ImGuiTableFlags.ScrollX) ~= 0) and ImMin(columns, table_.ColumnsCount) or 0
|
table_.FreezeColumnsRequest = (band(table_.Flags, ImGuiTableFlags.ScrollX) ~= 0) and mathMin(columns, table_.ColumnsCount) or 0
|
||||||
table_.FreezeColumnsCount = (table_.InnerWindow.Scroll.x ~= 0.0) and table_.FreezeColumnsRequest or 0
|
table_.FreezeColumnsCount = (table_.InnerWindow.Scroll.x ~= 0.0) and table_.FreezeColumnsRequest or 0
|
||||||
table_.FreezeRowsRequest = (band(table_.Flags, ImGuiTableFlags.ScrollY) ~= 0) and rows or 0
|
table_.FreezeRowsRequest = (band(table_.Flags, ImGuiTableFlags.ScrollY) ~= 0) and rows or 0
|
||||||
table_.FreezeRowsCount = (table_.InnerWindow.Scroll.y ~= 0.0) and table_.FreezeRowsRequest or 0
|
table_.FreezeRowsCount = (table_.InnerWindow.Scroll.y ~= 0.0) and table_.FreezeRowsRequest or 0
|
||||||
@@ -2065,11 +2066,23 @@ end
|
|||||||
-- they would meddle many times with the underlying ImDrawCmd.
|
-- they would meddle many times with the underlying ImDrawCmd.
|
||||||
-- Instead, we do a preemptive overwrite of clipping rectangle _without_ altering the command-buffer and let
|
-- Instead, we do a preemptive overwrite of clipping rectangle _without_ altering the command-buffer and let
|
||||||
-- the subsequent single call to SetCurrentChannel() does it things once.
|
-- the subsequent single call to SetCurrentChannel() does it things once.
|
||||||
|
@native
|
||||||
function ImGui.SetWindowClipRectBeforeSetChannel(window, clip_rect)
|
function ImGui.SetWindowClipRectBeforeSetChannel(window, clip_rect)
|
||||||
local clip_rect_vec4 = clip_rect:ToVec4()
|
-- [Luau] scalar copies, no temporary ImVec4: the clip rect stack top is a uniquely owned ImVec4 (PushClipRect always pushes a fresh one), so overwrite it in place
|
||||||
ImRect_Copy(window.ClipRect, clip_rect)
|
local min, max = clip_rect.Min, clip_rect.Max
|
||||||
ImVec4_Copy(window.DrawList._CmdHeader.ClipRect, clip_rect_vec4)
|
local x1, y1, x2, y2 = min.x, min.y, max.x, max.y
|
||||||
window.DrawList._ClipRectStack.Data[window.DrawList._ClipRectStack.Size] = clip_rect_vec4
|
local wcr = window.ClipRect
|
||||||
|
wcr.Min.x = x1; wcr.Min.y = y1; wcr.Max.x = x2; wcr.Max.y = y2
|
||||||
|
local draw_list = window.DrawList
|
||||||
|
local hcr = draw_list._CmdHeader.ClipRect
|
||||||
|
hcr.x = x1; hcr.y = y1; hcr.z = x2; hcr.w = y2
|
||||||
|
local stack = draw_list._ClipRectStack
|
||||||
|
local top = stack.Data[stack.Size]
|
||||||
|
if top ~= nil then
|
||||||
|
top.x = x1; top.y = y1; top.z = x2; top.w = y2
|
||||||
|
else
|
||||||
|
stack.Data[stack.Size] = ImVec4(x1, y1, x2, y2)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function ImGui.TableGetColumnCount()
|
function ImGui.TableGetColumnCount()
|
||||||
@@ -2154,8 +2167,8 @@ function ImGui.TableGetCellBgRect(table_, column_n)
|
|||||||
-- x1 = x1 - table.OuterPaddingX
|
-- x1 = x1 - table.OuterPaddingX
|
||||||
--if column.NextEnabledColumn == -1 then
|
--if column.NextEnabledColumn == -1 then
|
||||||
-- x2 = x2 + table.OuterPaddingX
|
-- x2 = x2 + table.OuterPaddingX
|
||||||
x1 = ImMax(x1, table_.WorkRect.Min.x)
|
x1 = mathMax(x1, table_.WorkRect.Min.x)
|
||||||
x2 = ImMin(x2, table_.WorkRect.Max.x)
|
x2 = mathMin(x2, table_.WorkRect.Max.x)
|
||||||
return ImRect(x1, table_.RowPosY1, x2, table_.RowPosY2)
|
return ImRect(x1, table_.RowPosY1, x2, table_.RowPosY2)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2274,8 +2287,7 @@ function ImGui.TableNextRow(row_flags, row_min_height)
|
|||||||
|
|
||||||
-- We honor min_row_height requested by user, but cannot guarantee per-row maximum height,
|
-- We honor min_row_height requested by user, but cannot guarantee per-row maximum height,
|
||||||
-- because that would essentially require a unique clipping rectangle per-cell.
|
-- because that would essentially require a unique clipping rectangle per-cell.
|
||||||
table_.RowPosY2 = table_.RowPosY2 + table_.RowCellPaddingY * 2.0
|
table_.RowPosY2 = mathMax(table_.RowPosY2 + table_.RowCellPaddingY * 2.0, table_.RowPosY1 + row_min_height) -- [Luau] inline ImMax
|
||||||
table_.RowPosY2 = ImMax(table_.RowPosY2, table_.RowPosY1 + row_min_height)
|
|
||||||
|
|
||||||
-- Disable output until user calls TableNextColumn()
|
-- Disable output until user calls TableNextColumn()
|
||||||
table_.InnerWindow.SkipItems = true
|
table_.InnerWindow.SkipItems = true
|
||||||
@@ -2302,15 +2314,16 @@ function ImGui.TableBeginRow(table_)
|
|||||||
|
|
||||||
table_.RowPosY1 = next_y1; table_.RowPosY2 = next_y1
|
table_.RowPosY1 = next_y1; table_.RowPosY2 = next_y1
|
||||||
table_.RowTextBaseline = 0.0
|
table_.RowTextBaseline = 0.0
|
||||||
table_.RowIndentOffsetX = window.DC.Indent.x - table_.HostIndentX -- Lock indent
|
local dc = window.DC -- [Luau] cache
|
||||||
|
table_.RowIndentOffsetX = dc.Indent.x - table_.HostIndentX -- Lock indent
|
||||||
|
|
||||||
window.DC.PrevLineTextBaseOffset = 0.0
|
dc.PrevLineTextBaseOffset = 0.0
|
||||||
window.DC.CursorPosPrevLine.x = window.DC.CursorPos.x
|
dc.CursorPosPrevLine.x = dc.CursorPos.x
|
||||||
window.DC.CursorPosPrevLine.y = window.DC.CursorPos.y + table_.RowCellPaddingY -- This allows users to call SameLine() to share LineSize between columns.
|
dc.CursorPosPrevLine.y = dc.CursorPos.y + table_.RowCellPaddingY -- This allows users to call SameLine() to share LineSize between columns.
|
||||||
window.DC.PrevLineSize.x = 0.0; window.DC.PrevLineSize.y = 0.0 -- This allows users to call SameLine() to share LineSize between columns, and to call it from first column too.
|
dc.PrevLineSize.x = 0.0; dc.PrevLineSize.y = 0.0 -- This allows users to call SameLine() to share LineSize between columns, and to call it from first column too.
|
||||||
window.DC.CurrLineSize.x = 0.0; window.DC.CurrLineSize.y = 0.0
|
dc.CurrLineSize.x = 0.0; dc.CurrLineSize.y = 0.0
|
||||||
window.DC.IsSameLine = false; window.DC.IsSetPos = false
|
dc.IsSameLine = false; dc.IsSetPos = false
|
||||||
window.DC.CursorMaxPos.y = next_y1
|
dc.CursorMaxPos.y = next_y1
|
||||||
|
|
||||||
-- Making the header BG color non-transparent will allow us to overlay it multiple times when handling smooth dragging.
|
-- Making the header BG color non-transparent will allow us to overlay it multiple times when handling smooth dragging.
|
||||||
if band(table_.RowFlags, ImGuiTableRowFlags.Headers) ~= 0 then
|
if band(table_.RowFlags, ImGuiTableRowFlags.Headers) ~= 0 then
|
||||||
@@ -2321,6 +2334,9 @@ function ImGui.TableBeginRow(table_)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- [Luau] scratch vectors for TableEndRow() row background (never escape)
|
||||||
|
local TableEndRow_RowMin, TableEndRow_RowMax = ImVec2(), ImVec2()
|
||||||
|
|
||||||
-- [Internal] Called by TableNextRow()
|
-- [Internal] Called by TableNextRow()
|
||||||
function ImGui.TableEndRow(table_)
|
function ImGui.TableEndRow(table_)
|
||||||
local g = GImGui
|
local g = GImGui
|
||||||
@@ -2347,7 +2363,7 @@ function ImGui.TableEndRow(table_)
|
|||||||
local bg_y2 = table_.RowPosY2
|
local bg_y2 = table_.RowPosY2
|
||||||
local unfreeze_rows_actual = (table_.CurrentRow + 1 == table_.FreezeRowsCount)
|
local unfreeze_rows_actual = (table_.CurrentRow + 1 == table_.FreezeRowsCount)
|
||||||
local unfreeze_rows_request = (table_.CurrentRow + 1 == table_.FreezeRowsRequest)
|
local unfreeze_rows_request = (table_.CurrentRow + 1 == table_.FreezeRowsRequest)
|
||||||
local table_instance = ImGui.TableGetInstanceData(table_, table_.InstanceCurrent)
|
local table_instance = (table_.InstanceCurrent == 0) and table_.InstanceDataFirst or ImGui.TableGetInstanceData(table_, table_.InstanceCurrent) -- [Luau] inline common case
|
||||||
if band(table_.RowFlags, ImGuiTableRowFlags.Headers) ~= 0 and (table_.CurrentRow == 0 or band(table_.LastRowFlags, ImGuiTableRowFlags.Headers) ~= 0) then
|
if band(table_.RowFlags, ImGuiTableRowFlags.Headers) ~= 0 and (table_.CurrentRow == 0 or band(table_.LastRowFlags, ImGuiTableRowFlags.Headers) ~= 0) then
|
||||||
table_instance.LastTopHeadersRowHeight = table_instance.LastTopHeadersRowHeight + bg_y2 - bg_y1
|
table_instance.LastTopHeadersRowHeight = table_instance.LastTopHeadersRowHeight + bg_y2 - bg_y1
|
||||||
end
|
end
|
||||||
@@ -2384,7 +2400,9 @@ function ImGui.TableEndRow(table_)
|
|||||||
-- In theory we could call SetWindowClipRectBeforeSetChannel() but since we know TableEndRow() is
|
-- In theory we could call SetWindowClipRectBeforeSetChannel() but since we know TableEndRow() is
|
||||||
-- always followed by a change of clipping rectangle we perform the smallest overwrite possible here.
|
-- always followed by a change of clipping rectangle we perform the smallest overwrite possible here.
|
||||||
if band(table_.Flags, ImGuiTableFlags.NoClip) == 0 then
|
if band(table_.Flags, ImGuiTableFlags.NoClip) == 0 then
|
||||||
ImVec4_Copy(window.DrawList._CmdHeader.ClipRect, table_.Bg0ClipRectForDrawCmd:ToVec4())
|
-- [Luau] scalar copy, no temporary ImVec4
|
||||||
|
local src, dst = table_.Bg0ClipRectForDrawCmd, window.DrawList._CmdHeader.ClipRect
|
||||||
|
dst.x = src.Min.x; dst.y = src.Min.y; dst.z = src.Max.x; dst.w = src.Max.y
|
||||||
end
|
end
|
||||||
table_.DrawSplitter:SetCurrentChannel(window.DrawList, TABLE_DRAW_CHANNEL_BG0)
|
table_.DrawSplitter:SetCurrentChannel(window.DrawList, TABLE_DRAW_CHANNEL_BG0)
|
||||||
end
|
end
|
||||||
@@ -2392,13 +2410,16 @@ function ImGui.TableEndRow(table_)
|
|||||||
-- Draw row background
|
-- Draw row background
|
||||||
-- We soft/cpu clip this so all backgrounds and borders can share the same clipping rectangle
|
-- We soft/cpu clip this so all backgrounds and borders can share the same clipping rectangle
|
||||||
if bg_col0 ~= 0 or bg_col1 ~= 0 then
|
if bg_col0 ~= 0 or bg_col1 ~= 0 then
|
||||||
local row_rect = ImRect(table_.WorkRect.Min.x, bg_y1, table_.WorkRect.Max.x, bg_y2)
|
-- [Luau] row_rect = ImRect(WorkRect.Min.x, bg_y1, WorkRect.Max.x, bg_y2); row_rect:ClipWith(BgClipRect), in scratch vectors (only read by AddRectFilled)
|
||||||
row_rect:ClipWith(table_.BgClipRect)
|
local bg_clip_rect = table_.BgClipRect
|
||||||
if bg_col0 ~= 0 and row_rect.Min.y < row_rect.Max.y then
|
local row_min, row_max = TableEndRow_RowMin, TableEndRow_RowMax
|
||||||
window.DrawList:AddRectFilled(row_rect.Min, row_rect.Max, bg_col0)
|
row_min.x = mathMax(table_.WorkRect.Min.x, bg_clip_rect.Min.x); row_min.y = mathMax(bg_y1, bg_clip_rect.Min.y)
|
||||||
|
row_max.x = mathMin(table_.WorkRect.Max.x, bg_clip_rect.Max.x); row_max.y = mathMin(bg_y2, bg_clip_rect.Max.y)
|
||||||
|
if bg_col0 ~= 0 and row_min.y < row_max.y then
|
||||||
|
window.DrawList:AddRectFilled(row_min, row_max, bg_col0)
|
||||||
end
|
end
|
||||||
if bg_col1 ~= 0 and row_rect.Min.y < row_rect.Max.y then
|
if bg_col1 ~= 0 and row_min.y < row_max.y then
|
||||||
window.DrawList:AddRectFilled(row_rect.Min, row_rect.Max, bg_col1)
|
window.DrawList:AddRectFilled(row_min, row_max, bg_col1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2411,8 +2432,8 @@ function ImGui.TableEndRow(table_)
|
|||||||
local column = table_.Columns[cell_data.Column]
|
local column = table_.Columns[cell_data.Column]
|
||||||
local cell_bg_rect = ImGui.TableGetCellBgRect(table_, cell_data.Column)
|
local cell_bg_rect = ImGui.TableGetCellBgRect(table_, cell_data.Column)
|
||||||
cell_bg_rect:ClipWith(table_.BgClipRect)
|
cell_bg_rect:ClipWith(table_.BgClipRect)
|
||||||
cell_bg_rect.Min.x = ImMax(cell_bg_rect.Min.x, column.ClipRect.Min.x) -- So that first column after frozen one gets clipped when scrolling
|
cell_bg_rect.Min.x = mathMax(cell_bg_rect.Min.x, column.ClipRect.Min.x) -- So that first column after frozen one gets clipped when scrolling
|
||||||
cell_bg_rect.Max.x = ImMin(cell_bg_rect.Max.x, column.MaxX)
|
cell_bg_rect.Max.x = mathMin(cell_bg_rect.Max.x, column.MaxX)
|
||||||
if cell_bg_rect.Min.y < cell_bg_rect.Max.y then
|
if cell_bg_rect.Min.y < cell_bg_rect.Max.y then
|
||||||
window.DrawList:AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data.BgColor)
|
window.DrawList:AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data.BgColor)
|
||||||
end
|
end
|
||||||
@@ -2438,7 +2459,7 @@ function ImGui.TableEndRow(table_)
|
|||||||
for column_n = 0, table_.ColumnsCount - 1 do
|
for column_n = 0, table_.ColumnsCount - 1 do
|
||||||
table_.Columns[column_n].NavLayerCurrent = table_.NavLayer
|
table_.Columns[column_n].NavLayerCurrent = table_.NavLayer
|
||||||
end
|
end
|
||||||
local y0 = ImMax(table_.RowPosY2 + 1, table_.InnerClipRect.Min.y)
|
local y0 = mathMax(table_.RowPosY2 + 1, table_.InnerClipRect.Min.y)
|
||||||
table_instance.LastFrozenHeight = y0 - table_.OuterRect.Min.y
|
table_instance.LastFrozenHeight = y0 - table_.OuterRect.Min.y
|
||||||
|
|
||||||
if unfreeze_rows_actual then
|
if unfreeze_rows_actual then
|
||||||
@@ -2446,7 +2467,7 @@ function ImGui.TableEndRow(table_)
|
|||||||
table_.IsUnfrozenRows = true
|
table_.IsUnfrozenRows = true
|
||||||
|
|
||||||
-- BgClipRect starts as table.InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
|
-- BgClipRect starts as table.InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
|
||||||
local min_y = ImMin(y0, table_.InnerClipRect.Max.y)
|
local min_y = mathMin(y0, table_.InnerClipRect.Max.y)
|
||||||
table_.BgClipRect.Min.y = min_y; table_.Bg2ClipRectForDrawCmd.Min.y = min_y
|
table_.BgClipRect.Min.y = min_y; table_.Bg2ClipRectForDrawCmd.Min.y = min_y
|
||||||
table_.BgClipRect.Max.y = table_.InnerClipRect.Max.y; table_.Bg2ClipRectForDrawCmd.Max.y = table_.InnerClipRect.Max.y
|
table_.BgClipRect.Max.y = table_.InnerClipRect.Max.y; table_.Bg2ClipRectForDrawCmd.Max.y = table_.InnerClipRect.Max.y
|
||||||
table_.Bg2DrawChannelCurrent = table_.Bg2DrawChannelUnfrozen
|
table_.Bg2DrawChannelCurrent = table_.Bg2DrawChannelUnfrozen
|
||||||
@@ -2518,6 +2539,7 @@ function ImGui.TableSetColumnIndex(column_n)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- [Public] Append into the next column, wrap and create a new row when already on last column
|
-- [Public] Append into the next column, wrap and create a new row when already on last column
|
||||||
|
@native
|
||||||
function ImGui.TableNextColumn()
|
function ImGui.TableNextColumn()
|
||||||
local g = GImGui
|
local g = GImGui
|
||||||
local table_ = g.CurrentTable
|
local table_ = g.CurrentTable
|
||||||
@@ -2525,28 +2547,33 @@ function ImGui.TableNextColumn()
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
if table_.IsInsideRow and table_.CurrentColumn + 1 < table_.ColumnsCount then
|
local current_column = table_.CurrentColumn
|
||||||
if table_.CurrentColumn ~= -1 then
|
local next_column
|
||||||
|
if table_.IsInsideRow and current_column + 1 < table_.ColumnsCount then
|
||||||
|
if current_column ~= -1 then
|
||||||
ImGui.TableEndCell(table_)
|
ImGui.TableEndCell(table_)
|
||||||
end
|
end
|
||||||
ImGui.TableBeginCell(table_, table_.CurrentColumn + 1)
|
next_column = current_column + 1
|
||||||
else
|
else
|
||||||
ImGui.TableNextRow()
|
ImGui.TableNextRow()
|
||||||
ImGui.TableBeginCell(table_, 0)
|
next_column = 0
|
||||||
end
|
end
|
||||||
|
ImGui.TableBeginCell(table_, next_column)
|
||||||
|
|
||||||
-- Return whether the column is visible. User may choose to skip submitting items based on this return value,
|
-- Return whether the column is visible. User may choose to skip submitting items based on this return value,
|
||||||
-- however they shouldn't skip submitting for columns that may have the tallest contribution to row height.
|
-- however they shouldn't skip submitting for columns that may have the tallest contribution to row height.
|
||||||
return table_.Columns[table_.CurrentColumn].IsRequestOutput
|
return table_.Columns[next_column].IsRequestOutput
|
||||||
end
|
end
|
||||||
|
|
||||||
-- [Internal] Called by TableSetColumnIndex()/TableNextColumn()
|
-- [Internal] Called by TableSetColumnIndex()/TableNextColumn()
|
||||||
-- This is called very frequently, so we need to be mindful of unnecessary overhead.
|
-- This is called very frequently, so we need to be mindful of unnecessary overhead.
|
||||||
-- FIXME-TABLE FIXME-OPT: Could probably shortcut some things for non-active or clipped columns.
|
-- FIXME-TABLE FIXME-OPT: Could probably shortcut some things for non-active or clipped columns.
|
||||||
|
@native
|
||||||
function ImGui.TableBeginCell(table_, column_n)
|
function ImGui.TableBeginCell(table_, column_n)
|
||||||
local g = GImGui
|
local g = GImGui
|
||||||
local column = table_.Columns[column_n]
|
local column = table_.Columns[column_n]
|
||||||
local window = table_.InnerWindow
|
local window = table_.InnerWindow
|
||||||
|
local dc = window.DC -- [Luau] cache
|
||||||
table_.CurrentColumn = column_n
|
table_.CurrentColumn = column_n
|
||||||
|
|
||||||
-- Start position is roughly ~~ CellRect.Min + CellPadding + Indent
|
-- Start position is roughly ~~ CellRect.Min + CellPadding + Indent
|
||||||
@@ -2555,35 +2582,45 @@ function ImGui.TableBeginCell(table_, column_n)
|
|||||||
start_x = start_x + table_.RowIndentOffsetX -- ~~ += window.DC.Indent.x - table.HostIndentX, except we locked it for the row.
|
start_x = start_x + table_.RowIndentOffsetX -- ~~ += window.DC.Indent.x - table.HostIndentX, except we locked it for the row.
|
||||||
end
|
end
|
||||||
|
|
||||||
window.DC.CursorPos.x = start_x
|
local start_y = table_.RowPosY1 + table_.RowCellPaddingY
|
||||||
window.DC.CursorPos.y = table_.RowPosY1 + table_.RowCellPaddingY
|
dc.CursorPos.x = start_x
|
||||||
window.DC.CursorMaxPos.x = window.DC.CursorPos.x
|
dc.CursorPos.y = start_y
|
||||||
window.DC.ColumnsOffset.x = start_x - window.Pos.x - window.DC.Indent.x -- FIXME-WORKRECT
|
dc.CursorMaxPos.x = start_x
|
||||||
window.DC.CursorPosPrevLine.x = window.DC.CursorPos.x -- PrevLine.y is preserved. This allows users to call SameLine() to share LineSize between columns.
|
dc.ColumnsOffset.x = start_x - window.Pos.x - dc.Indent.x -- FIXME-WORKRECT
|
||||||
window.DC.CurrLineTextBaseOffset = table_.RowTextBaseline
|
dc.CursorPosPrevLine.x = start_x -- PrevLine.y is preserved. This allows users to call SameLine() to share LineSize between columns.
|
||||||
window.DC.NavLayerCurrent = column.NavLayerCurrent
|
dc.CurrLineTextBaseOffset = table_.RowTextBaseline
|
||||||
|
dc.NavLayerCurrent = column.NavLayerCurrent
|
||||||
|
|
||||||
-- Note how WorkRect.Max.y is only set once during layout
|
-- Note how WorkRect.Max.y is only set once during layout
|
||||||
window.WorkRect.Min.y = window.DC.CursorPos.y
|
local work_rect = window.WorkRect
|
||||||
window.WorkRect.Min.x = column.WorkMinX
|
work_rect.Min.y = start_y
|
||||||
window.WorkRect.Max.x = column.WorkMaxX
|
work_rect.Min.x = column.WorkMinX
|
||||||
window.DC.ItemWidth = column.ItemWidth
|
work_rect.Max.x = column.WorkMaxX
|
||||||
|
dc.ItemWidth = column.ItemWidth
|
||||||
|
|
||||||
window.SkipItems = column.IsSkipItems
|
local skip_items = column.IsSkipItems
|
||||||
if column.IsSkipItems then
|
window.SkipItems = skip_items
|
||||||
|
if skip_items then
|
||||||
g.LastItemData.ID = 0
|
g.LastItemData.ID = 0
|
||||||
g.LastItemData.StatusFlags = 0
|
g.LastItemData.StatusFlags = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Also see TablePushColumnChannel()
|
-- Also see TablePushColumnChannel()
|
||||||
|
-- [Luau] inline the SetCurrentChannel() early-out (same channel) to skip the method call
|
||||||
|
local splitter = table_.DrawSplitter
|
||||||
if band(table_.Flags, ImGuiTableFlags.NoClip) ~= 0 then
|
if band(table_.Flags, ImGuiTableFlags.NoClip) ~= 0 then
|
||||||
-- FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed.
|
-- FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed.
|
||||||
table_.DrawSplitter:SetCurrentChannel(window.DrawList, TABLE_DRAW_CHANNEL_NOCLIP)
|
if splitter._Current ~= TABLE_DRAW_CHANNEL_NOCLIP then
|
||||||
|
splitter:SetCurrentChannel(window.DrawList, TABLE_DRAW_CHANNEL_NOCLIP)
|
||||||
|
end
|
||||||
--IM_ASSERT(table.DrawSplitter._Current == TABLE_DRAW_CHANNEL_NOCLIP)
|
--IM_ASSERT(table.DrawSplitter._Current == TABLE_DRAW_CHANNEL_NOCLIP)
|
||||||
else
|
else
|
||||||
-- FIXME-TABLE: Could avoid this if draw channel is dummy channel?
|
-- FIXME-TABLE: Could avoid this if draw channel is dummy channel?
|
||||||
ImGui.SetWindowClipRectBeforeSetChannel(window, column.ClipRect)
|
ImGui.SetWindowClipRectBeforeSetChannel(window, column.ClipRect)
|
||||||
table_.DrawSplitter:SetCurrentChannel(window.DrawList, column.DrawChannelCurrent)
|
local channel = column.DrawChannelCurrent
|
||||||
|
if splitter._Current ~= channel then
|
||||||
|
splitter:SetCurrentChannel(window.DrawList, channel)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Logging
|
-- Logging
|
||||||
@@ -2594,31 +2631,34 @@ function ImGui.TableBeginCell(table_, column_n)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- [Internal] Called by TableNextRow()/TableSetColumnIndex()/TableNextColumn()
|
-- [Internal] Called by TableNextRow()/TableSetColumnIndex()/TableNextColumn()
|
||||||
|
@native
|
||||||
function ImGui.TableEndCell(table_)
|
function ImGui.TableEndCell(table_)
|
||||||
local column = table_.Columns[table_.CurrentColumn]
|
local column = table_.Columns[table_.CurrentColumn]
|
||||||
local window = table_.InnerWindow
|
local window = table_.InnerWindow
|
||||||
|
local dc = window.DC -- [Luau] cache; mathMax() inlined as math.max below
|
||||||
|
|
||||||
if window.DC.IsSetPos then
|
if dc.IsSetPos then
|
||||||
ImGui.ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
|
ImGui.ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Report maximum position so we can infer content size per column.
|
-- Report maximum position so we can infer content size per column.
|
||||||
local x = window.DC.CursorMaxPos.x
|
local cursor_max_pos = dc.CursorMaxPos
|
||||||
|
local x = cursor_max_pos.x
|
||||||
if band(table_.RowFlags, ImGuiTableRowFlags.Headers) ~= 0 then
|
if band(table_.RowFlags, ImGuiTableRowFlags.Headers) ~= 0 then
|
||||||
column.ContentMaxXHeadersUsed = ImMax(column.ContentMaxXHeadersUsed, x) -- Useful in case user submit contents in header row that is not a TableHeader() call
|
column.ContentMaxXHeadersUsed = mathMax(column.ContentMaxXHeadersUsed, x) -- Useful in case user submit contents in header row that is not a TableHeader() call
|
||||||
elseif table_.IsUnfrozenRows then
|
elseif table_.IsUnfrozenRows then
|
||||||
column.ContentMaxXUnfrozen = ImMax(column.ContentMaxXUnfrozen, x)
|
column.ContentMaxXUnfrozen = mathMax(column.ContentMaxXUnfrozen, x)
|
||||||
else
|
else
|
||||||
column.ContentMaxXFrozen = ImMax(column.ContentMaxXFrozen, x)
|
column.ContentMaxXFrozen = mathMax(column.ContentMaxXFrozen, x)
|
||||||
end
|
end
|
||||||
if column.IsEnabled then
|
if column.IsEnabled then
|
||||||
table_.RowPosY2 = ImMax(table_.RowPosY2, window.DC.CursorMaxPos.y + table_.RowCellPaddingY)
|
table_.RowPosY2 = mathMax(table_.RowPosY2, cursor_max_pos.y + table_.RowCellPaddingY)
|
||||||
end
|
end
|
||||||
column.ItemWidth = window.DC.ItemWidth
|
column.ItemWidth = dc.ItemWidth
|
||||||
|
|
||||||
-- Propagate text baseline for the entire row
|
-- Propagate text baseline for the entire row
|
||||||
-- FIXME-TABLE: Here we propagate text baseline from the last line of the cell.. instead of the first one.
|
-- FIXME-TABLE: Here we propagate text baseline from the last line of the cell.. instead of the first one.
|
||||||
table_.RowTextBaseline = ImMax(table_.RowTextBaseline, window.DC.PrevLineTextBaseOffset)
|
table_.RowTextBaseline = mathMax(table_.RowTextBaseline, dc.PrevLineTextBaseOffset)
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
@@ -2663,11 +2703,11 @@ end
|
|||||||
|
|
||||||
-- Note this is meant to be stored in column.WidthAuto, please generally use the WidthAuto field
|
-- Note this is meant to be stored in column.WidthAuto, please generally use the WidthAuto field
|
||||||
function ImGui.TableGetColumnWidthAuto(table_, column)
|
function ImGui.TableGetColumnWidthAuto(table_, column)
|
||||||
local content_width_body = ImMax(column.ContentMaxXFrozen, column.ContentMaxXUnfrozen) - column.WorkMinX
|
local content_width_body = mathMax(column.ContentMaxXFrozen, column.ContentMaxXUnfrozen) - column.WorkMinX
|
||||||
local content_width_headers = column.ContentMaxXHeadersIdeal - column.WorkMinX
|
local content_width_headers = column.ContentMaxXHeadersIdeal - column.WorkMinX
|
||||||
local width_auto = content_width_body
|
local width_auto = content_width_body
|
||||||
if band(column.Flags, ImGuiTableColumnFlags.NoHeaderWidth) == 0 then
|
if band(column.Flags, ImGuiTableColumnFlags.NoHeaderWidth) == 0 then
|
||||||
width_auto = ImMax(width_auto, content_width_headers)
|
width_auto = mathMax(width_auto, content_width_headers)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Non-resizable fixed columns preserve their requested width
|
-- Non-resizable fixed columns preserve their requested width
|
||||||
@@ -2677,7 +2717,7 @@ function ImGui.TableGetColumnWidthAuto(table_, column)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return ImMax(width_auto, table_.MinColumnWidth)
|
return mathMax(width_auto, table_.MinColumnWidth)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 'width' = inner column width, without padding
|
-- 'width' = inner column width, without padding
|
||||||
@@ -2693,7 +2733,7 @@ function ImGui.TableSetColumnWidth(column_n, width)
|
|||||||
-- Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded)
|
-- Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded)
|
||||||
IM_ASSERT(table_.MinColumnWidth > 0.0)
|
IM_ASSERT(table_.MinColumnWidth > 0.0)
|
||||||
local min_width = table_.MinColumnWidth
|
local min_width = table_.MinColumnWidth
|
||||||
local max_width = ImMax(min_width, column_0.WidthMax) -- Don't use TableCalcMaxColumnWidth() here as it would rely on MinX from last instance (#7933)
|
local max_width = mathMax(min_width, column_0.WidthMax) -- Don't use TableCalcMaxColumnWidth() here as it would rely on MinX from last instance (#7933)
|
||||||
column_0_width = ImClamp(column_0_width, min_width, max_width)
|
column_0_width = ImClamp(column_0_width, min_width, max_width)
|
||||||
if column_0.WidthGiven == column_0_width or column_0.WidthRequest == column_0_width then
|
if column_0.WidthGiven == column_0_width or column_0.WidthRequest == column_0_width then
|
||||||
return
|
return
|
||||||
@@ -2754,7 +2794,7 @@ function ImGui.TableSetColumnWidth(column_n, width)
|
|||||||
|
|
||||||
-- Resizing from right-side of a Stretch column before a Fixed column forward sizing to left-side of fixed column.
|
-- Resizing from right-side of a Stretch column before a Fixed column forward sizing to left-side of fixed column.
|
||||||
-- (old_a + old_b == new_a + new_b) --> (new_a == old_a + old_b - new_b)
|
-- (old_a + old_b == new_a + new_b) --> (new_a == old_a + old_b - new_b)
|
||||||
local column_1_width = ImMax(column_1.WidthRequest - (column_0_width - column_0.WidthRequest), min_width)
|
local column_1_width = mathMax(column_1.WidthRequest - (column_0_width - column_0.WidthRequest), min_width)
|
||||||
column_0_width = column_0.WidthRequest + column_1.WidthRequest - column_1_width
|
column_0_width = column_0.WidthRequest + column_1.WidthRequest - column_1_width
|
||||||
IM_ASSERT(column_0_width > 0.0 and column_1_width > 0.0)
|
IM_ASSERT(column_0_width > 0.0 and column_1_width > 0.0)
|
||||||
column_0.WidthRequest = column_0_width
|
column_0.WidthRequest = column_0_width
|
||||||
@@ -2970,6 +3010,12 @@ end
|
|||||||
--
|
--
|
||||||
-- This function is particularly tricky to understand.. take a breath.
|
-- This function is particularly tricky to understand.. take a breath.
|
||||||
-- (Lua: splitter channel n lives in splitter._Channels.Data[n + 1]; bit arrays are Lua sets)
|
-- (Lua: splitter channel n lives in splitter._Channels.Data[n + 1]; bit arrays are Lua sets)
|
||||||
|
local TableMergeDrawChannels_MergeGroups = {}
|
||||||
|
for n = 0, 3 do
|
||||||
|
TableMergeDrawChannels_MergeGroups[n] = { ClipRect = ImRect(), ChannelsCount = 0, ChannelsMask = {} }
|
||||||
|
end
|
||||||
|
local TableMergeDrawChannels_RemainingMask = {}
|
||||||
|
local TableMergeDrawChannels_DstTmp = {}
|
||||||
function ImGui.TableMergeDrawChannels(table_)
|
function ImGui.TableMergeDrawChannels(table_)
|
||||||
local splitter = table_.DrawSplitter
|
local splitter = table_.DrawSplitter
|
||||||
local channels = splitter._Channels.Data
|
local channels = splitter._Channels.Data
|
||||||
@@ -2979,12 +3025,18 @@ function ImGui.TableMergeDrawChannels(table_)
|
|||||||
|
|
||||||
-- Track which groups we are going to attempt to merge, and which channels goes into each group.
|
-- Track which groups we are going to attempt to merge, and which channels goes into each group.
|
||||||
local merge_group_mask = 0x00
|
local merge_group_mask = 0x00
|
||||||
local merge_groups = {}
|
-- [Luau] reuse file-level scratch storage (function is not re-entrant, nothing escapes)
|
||||||
|
local merge_groups = TableMergeDrawChannels_MergeGroups
|
||||||
for n = 0, 3 do
|
for n = 0, 3 do
|
||||||
merge_groups[n] = { ClipRect = ImRect(), ChannelsCount = 0, ChannelsMask = {} }
|
local merge_group = merge_groups[n]
|
||||||
|
local cr = merge_group.ClipRect
|
||||||
|
cr.Min.x = 0.0; cr.Min.y = 0.0; cr.Max.x = 0.0; cr.Max.y = 0.0
|
||||||
|
merge_group.ChannelsCount = 0
|
||||||
|
table.clear(merge_group.ChannelsMask)
|
||||||
end
|
end
|
||||||
local max_draw_channels = (4 + table_.ColumnsCount * 2)
|
local max_draw_channels = (4 + table_.ColumnsCount * 2)
|
||||||
local remaining_mask = {}
|
local remaining_mask = TableMergeDrawChannels_RemainingMask
|
||||||
|
table.clear(remaining_mask)
|
||||||
|
|
||||||
-- 1. Scan channels and take note of those which can be merged
|
-- 1. Scan channels and take note of those which can be merged
|
||||||
for column_n = 0, table_.ColumnsCount - 1 do
|
for column_n = 0, table_.ColumnsCount - 1 do
|
||||||
@@ -3011,9 +3063,9 @@ function ImGui.TableMergeDrawChannels(table_)
|
|||||||
if band(column.Flags, ImGuiTableColumnFlags.NoClip) == 0 then
|
if band(column.Flags, ImGuiTableColumnFlags.NoClip) == 0 then
|
||||||
local content_max_x
|
local content_max_x
|
||||||
if not has_freeze_v then
|
if not has_freeze_v then
|
||||||
content_max_x = ImMax(column.ContentMaxXUnfrozen, column.ContentMaxXHeadersUsed) -- No row freeze
|
content_max_x = mathMax(column.ContentMaxXUnfrozen, column.ContentMaxXHeadersUsed) -- No row freeze
|
||||||
elseif merge_group_sub_n == 0 then
|
elseif merge_group_sub_n == 0 then
|
||||||
content_max_x = ImMax(column.ContentMaxXFrozen, column.ContentMaxXHeadersUsed) -- Row freeze: use width before freeze
|
content_max_x = mathMax(column.ContentMaxXFrozen, column.ContentMaxXHeadersUsed) -- Row freeze: use width before freeze
|
||||||
else
|
else
|
||||||
content_max_x = column.ContentMaxXUnfrozen -- Row freeze: use width after freeze
|
content_max_x = column.ContentMaxXUnfrozen -- Row freeze: use width after freeze
|
||||||
end
|
end
|
||||||
@@ -3026,12 +3078,18 @@ function ImGui.TableMergeDrawChannels(table_)
|
|||||||
IM_ASSERT(channel_no < max_draw_channels)
|
IM_ASSERT(channel_no < max_draw_channels)
|
||||||
local merge_group = merge_groups[merge_group_n]
|
local merge_group = merge_groups[merge_group_n]
|
||||||
if merge_group.ChannelsCount == 0 then
|
if merge_group.ChannelsCount == 0 then
|
||||||
merge_group.ClipRect = ImRect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX)
|
local gcr = merge_group.ClipRect -- [Luau] in place (merge_groups are local to this call)
|
||||||
|
gcr.Min.x = FLT_MAX; gcr.Min.y = FLT_MAX; gcr.Max.x = -FLT_MAX; gcr.Max.y = -FLT_MAX
|
||||||
end
|
end
|
||||||
merge_group.ChannelsMask[channel_no] = true
|
merge_group.ChannelsMask[channel_no] = true
|
||||||
merge_group.ChannelsCount = merge_group.ChannelsCount + 1
|
merge_group.ChannelsCount = merge_group.ChannelsCount + 1
|
||||||
local cr = cmds.Data[1].ClipRect
|
local cr = cmds.Data[1].ClipRect
|
||||||
merge_group.ClipRect:Add(ImRect(cr.x, cr.y, cr.z, cr.w))
|
-- [Luau] scalar version of merge_group.ClipRect:Add(ImRect(cr.x, cr.y, cr.z, cr.w))
|
||||||
|
local gmin, gmax = merge_group.ClipRect.Min, merge_group.ClipRect.Max
|
||||||
|
if gmin.x > cr.x then gmin.x = cr.x end
|
||||||
|
if gmin.y > cr.y then gmin.y = cr.y end
|
||||||
|
if gmax.x < cr.z then gmax.x = cr.z end
|
||||||
|
if gmax.y < cr.w then gmax.y = cr.w end
|
||||||
merge_group_mask = bor(merge_group_mask, lshift(1, merge_group_n))
|
merge_group_mask = bor(merge_group_mask, lshift(1, merge_group_n))
|
||||||
until true
|
until true
|
||||||
end
|
end
|
||||||
@@ -3046,7 +3104,8 @@ function ImGui.TableMergeDrawChannels(table_)
|
|||||||
if merge_group_mask ~= 0 then
|
if merge_group_mask ~= 0 then
|
||||||
-- We skip channel 0 (Bg0/Bg1) and 1 (Bg2 frozen) from the shuffling since they won't move - see channels allocation in TableSetupDrawChannels().
|
-- We skip channel 0 (Bg0/Bg1) and 1 (Bg2 frozen) from the shuffling since they won't move - see channels allocation in TableSetupDrawChannels().
|
||||||
local LEADING_DRAW_CHANNELS = 2
|
local LEADING_DRAW_CHANNELS = 2
|
||||||
local dst_tmp = {}
|
local dst_tmp = TableMergeDrawChannels_DstTmp -- [Luau] scratch
|
||||||
|
table.clear(dst_tmp)
|
||||||
for n = LEADING_DRAW_CHANNELS, splitter._Count - 1 do
|
for n = LEADING_DRAW_CHANNELS, splitter._Count - 1 do
|
||||||
remaining_mask[n] = true
|
remaining_mask[n] = true
|
||||||
end
|
end
|
||||||
@@ -3069,16 +3128,16 @@ function ImGui.TableMergeDrawChannels(table_)
|
|||||||
-- FIXME-TABLE FIXME-WORKRECT: We are wasting a merge opportunity on tables without scrolling if column doesn't fit
|
-- FIXME-TABLE FIXME-WORKRECT: We are wasting a merge opportunity on tables without scrolling if column doesn't fit
|
||||||
-- within host clip rect, solely because of the half-padding difference between window.WorkRect and window.InnerClipRect.
|
-- within host clip rect, solely because of the half-padding difference between window.WorkRect and window.InnerClipRect.
|
||||||
if band(merge_group_n, 1) == 0 or not has_freeze_h then
|
if band(merge_group_n, 1) == 0 or not has_freeze_h then
|
||||||
merge_clip_rect.Min.x = ImMin(merge_clip_rect.Min.x, host_rect.Min.x)
|
merge_clip_rect.Min.x = mathMin(merge_clip_rect.Min.x, host_rect.Min.x)
|
||||||
end
|
end
|
||||||
if band(merge_group_n, 2) == 0 or not has_freeze_v then
|
if band(merge_group_n, 2) == 0 or not has_freeze_v then
|
||||||
merge_clip_rect.Min.y = ImMin(merge_clip_rect.Min.y, host_rect.Min.y)
|
merge_clip_rect.Min.y = mathMin(merge_clip_rect.Min.y, host_rect.Min.y)
|
||||||
end
|
end
|
||||||
if band(merge_group_n, 1) ~= 0 then
|
if band(merge_group_n, 1) ~= 0 then
|
||||||
merge_clip_rect.Max.x = ImMax(merge_clip_rect.Max.x, host_rect.Max.x)
|
merge_clip_rect.Max.x = mathMax(merge_clip_rect.Max.x, host_rect.Max.x)
|
||||||
end
|
end
|
||||||
if band(merge_group_n, 2) ~= 0 and band(table_.Flags, ImGuiTableFlags.NoHostExtendY) == 0 then
|
if band(merge_group_n, 2) ~= 0 and band(table_.Flags, ImGuiTableFlags.NoHostExtendY) == 0 then
|
||||||
merge_clip_rect.Max.y = ImMax(merge_clip_rect.Max.y, host_rect.Max.y)
|
merge_clip_rect.Max.y = mathMax(merge_clip_rect.Max.y, host_rect.Max.y)
|
||||||
end
|
end
|
||||||
--ImGui.GetForegroundDrawList():AddRect(merge_group.ClipRect.Min, merge_group.ClipRect.Max, IM_COL32(255, 0, 0, 200), 0.0, 0, 1.0) -- [DEBUG]
|
--ImGui.GetForegroundDrawList():AddRect(merge_group.ClipRect.Min, merge_group.ClipRect.Max, IM_COL32(255, 0, 0, 200), 0.0, 0, 1.0) -- [DEBUG]
|
||||||
--ImGui.GetForegroundDrawList():AddLine(merge_group.ClipRect.Min, merge_clip_rect.Min, IM_COL32(255, 100, 0, 200))
|
--ImGui.GetForegroundDrawList():AddLine(merge_group.ClipRect.Min, merge_clip_rect.Min, IM_COL32(255, 100, 0, 200))
|
||||||
@@ -3096,7 +3155,8 @@ function ImGui.TableMergeDrawChannels(table_)
|
|||||||
|
|
||||||
local channel = channels[n + 1]
|
local channel = channels[n + 1]
|
||||||
IM_ASSERT(channel._CmdBuffer.Size == 1)
|
IM_ASSERT(channel._CmdBuffer.Size == 1)
|
||||||
ImVec4_Copy(channel._CmdBuffer.Data[1].ClipRect, merge_clip_rect:ToVec4())
|
local dst = channel._CmdBuffer.Data[1].ClipRect -- [Luau] scalar copy, no temporary ImVec4
|
||||||
|
dst.x = merge_clip_rect.Min.x; dst.y = merge_clip_rect.Min.y; dst.z = merge_clip_rect.Max.x; dst.w = merge_clip_rect.Max.y
|
||||||
dst_tmp[#dst_tmp + 1] = channel
|
dst_tmp[#dst_tmp + 1] = channel
|
||||||
end
|
end
|
||||||
n = n + 1
|
n = n + 1
|
||||||
@@ -3122,6 +3182,7 @@ function ImGui.TableMergeDrawChannels(table_)
|
|||||||
for i = 1, #dst_tmp do
|
for i = 1, #dst_tmp do
|
||||||
channels[LEADING_DRAW_CHANNELS + i] = dst_tmp[i]
|
channels[LEADING_DRAW_CHANNELS + i] = dst_tmp[i]
|
||||||
end
|
end
|
||||||
|
table.clear(dst_tmp) -- [Luau] don't keep channel references alive in the scratch
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -3152,9 +3213,9 @@ function ImGui.TableDrawBorders(table_)
|
|||||||
-- Draw inner border and resizing feedback
|
-- Draw inner border and resizing feedback
|
||||||
local table_instance = ImGui.TableGetInstanceData(table_, table_.InstanceCurrent)
|
local table_instance = ImGui.TableGetInstanceData(table_, table_.InstanceCurrent)
|
||||||
local border_size = TABLE_BORDER_SIZE
|
local border_size = TABLE_BORDER_SIZE
|
||||||
local draw_y1 = ImMax(table_.InnerRect.Min.y, ((table_.FreezeRowsCount >= 1) and table_.InnerRect.Min.y or table_.WorkRect.Min.y) + table_.AngledHeadersHeight) + ((band(table_.Flags, ImGuiTableFlags.BordersOuterH) ~= 0) and border_size or 0.0)
|
local draw_y1 = mathMax(table_.InnerRect.Min.y, ((table_.FreezeRowsCount >= 1) and table_.InnerRect.Min.y or table_.WorkRect.Min.y) + table_.AngledHeadersHeight) + ((band(table_.Flags, ImGuiTableFlags.BordersOuterH) ~= 0) and border_size or 0.0)
|
||||||
local draw_y2_body = table_.InnerRect.Max.y
|
local draw_y2_body = table_.InnerRect.Max.y
|
||||||
local draw_y2_head = table_.IsUsingHeaders and ImMin(table_.InnerRect.Max.y, ((table_.FreezeRowsCount >= 1) and table_.InnerRect.Min.y or table_.WorkRect.Min.y) + table_instance.LastTopHeadersRowHeight) or draw_y1
|
local draw_y2_head = table_.IsUsingHeaders and mathMin(table_.InnerRect.Max.y, ((table_.FreezeRowsCount >= 1) and table_.InnerRect.Min.y or table_.WorkRect.Min.y) + table_instance.LastTopHeadersRowHeight) or draw_y1
|
||||||
if band(table_.Flags, ImGuiTableFlags.BordersInnerV) ~= 0 then
|
if band(table_.Flags, ImGuiTableFlags.BordersInnerV) ~= 0 then
|
||||||
for order_n = 0, table_.ColumnsCount - 1 do
|
for order_n = 0, table_.ColumnsCount - 1 do
|
||||||
repeat
|
repeat
|
||||||
@@ -3310,7 +3371,7 @@ function ImGui.TableSetColumnSortDirection(column_n, sort_direction, append_to_s
|
|||||||
local sort_order_max = 0
|
local sort_order_max = 0
|
||||||
if append_to_sort_specs then
|
if append_to_sort_specs then
|
||||||
for other_column_n = 0, table_.ColumnsCount - 1 do
|
for other_column_n = 0, table_.ColumnsCount - 1 do
|
||||||
sort_order_max = ImMax(sort_order_max, table_.Columns[other_column_n].SortOrder)
|
sort_order_max = mathMax(sort_order_max, table_.Columns[other_column_n].SortOrder)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -3468,7 +3529,7 @@ function ImGui.TableGetHeaderRowHeight()
|
|||||||
for column_n = 0, table_.ColumnsCount - 1 do
|
for column_n = 0, table_.ColumnsCount - 1 do
|
||||||
if BitTest(table_.EnabledMaskByIndex, column_n) then
|
if BitTest(table_.EnabledMaskByIndex, column_n) then
|
||||||
if band(table_.Columns[column_n].Flags, ImGuiTableColumnFlags.NoHeaderLabel) == 0 then
|
if band(table_.Columns[column_n].Flags, ImGuiTableColumnFlags.NoHeaderLabel) == 0 then
|
||||||
row_height = ImMax(row_height, ImGui.CalcTextSize(ImGui.TableGetColumnName(table_, column_n)).y)
|
row_height = mathMax(row_height, ImGui.CalcTextSize(ImGui.TableGetColumnName(table_, column_n)).y)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -3482,7 +3543,7 @@ function ImGui.TableGetHeaderAngledMaxLabelWidth()
|
|||||||
for column_n = 0, table_.ColumnsCount - 1 do
|
for column_n = 0, table_.ColumnsCount - 1 do
|
||||||
if BitTest(table_.EnabledMaskByIndex, column_n) then
|
if BitTest(table_.EnabledMaskByIndex, column_n) then
|
||||||
if band(table_.Columns[column_n].Flags, ImGuiTableColumnFlags.AngledHeader) ~= 0 then
|
if band(table_.Columns[column_n].Flags, ImGuiTableColumnFlags.AngledHeader) ~= 0 then
|
||||||
width = ImMax(width, ImGui.CalcTextSize(ImGui.TableGetColumnName(table_, column_n), nil, true).x)
|
width = mathMax(width, ImGui.CalcTextSize(ImGui.TableGetColumnName(table_, column_n), nil, true).x)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -3561,8 +3622,11 @@ function ImGui.TableHeader(label)
|
|||||||
|
|
||||||
-- If we already got a row height, there's use that.
|
-- If we already got a row height, there's use that.
|
||||||
-- FIXME-TABLE: Padding problem if the correct outer-padding CellBgRect strays off our ClipRect?
|
-- FIXME-TABLE: Padding problem if the correct outer-padding CellBgRect strays off our ClipRect?
|
||||||
local cell_r = ImGui.TableGetCellBgRect(table_, column_n)
|
-- [Luau] scalar version of: cell_r = TableGetCellBgRect(table_, column_n) (Min.x/Min.y/Max.x/Max.y -> cell_x1/cell_y1/cell_x2/cell_y2)
|
||||||
local label_height = ImMax(label_size.y, table_.RowMinHeight - table_.RowCellPaddingY * 2.0)
|
local cell_x1 = mathMax(column.MinX, table_.WorkRect.Min.x)
|
||||||
|
local cell_x2 = mathMin(column.MaxX, table_.WorkRect.Max.x)
|
||||||
|
local cell_y1, cell_y2 = table_.RowPosY1, table_.RowPosY2
|
||||||
|
local label_height = mathMax(label_size.y, table_.RowMinHeight - table_.RowCellPaddingY * 2.0)
|
||||||
|
|
||||||
-- Calculate ideal size for sort order arrow
|
-- Calculate ideal size for sort order arrow
|
||||||
local w_arrow = 0.0
|
local w_arrow = 0.0
|
||||||
@@ -3583,12 +3647,12 @@ function ImGui.TableHeader(label)
|
|||||||
|
|
||||||
-- We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considered for merging.
|
-- We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considered for merging.
|
||||||
local max_pos_x = label_pos.x + label_size.x + w_sort_text + w_arrow
|
local max_pos_x = label_pos.x + label_size.x + w_sort_text + w_arrow
|
||||||
column.ContentMaxXHeadersUsed = ImMax(column.ContentMaxXHeadersUsed, sort_arrow and cell_r.Max.x or ImMin(max_pos_x, cell_r.Max.x))
|
column.ContentMaxXHeadersUsed = mathMax(column.ContentMaxXHeadersUsed, sort_arrow and cell_x2 or mathMin(max_pos_x, cell_x2))
|
||||||
column.ContentMaxXHeadersIdeal = ImMax(column.ContentMaxXHeadersIdeal, max_pos_x)
|
column.ContentMaxXHeadersIdeal = mathMax(column.ContentMaxXHeadersIdeal, max_pos_x)
|
||||||
|
|
||||||
-- Keep header highlighted when context menu is open.
|
-- Keep header highlighted when context menu is open.
|
||||||
local id = window:GetID(label)
|
local id = window:GetID(label)
|
||||||
local bb = ImRect(cell_r.Min.x, cell_r.Min.y, cell_r.Max.x, ImMax(cell_r.Max.y, cell_r.Min.y + label_height + g.Style.CellPadding.y * 2.0))
|
local bb = ImRect(cell_x1, cell_y1, cell_x2, mathMax(cell_y2, cell_y1 + label_height + g.Style.CellPadding.y * 2.0))
|
||||||
ImGui.ItemSize(ImVec2(0.0, label_height)) -- Don't declare unclipped width, it'll be fed ContentMaxPosHeadersIdeal
|
ImGui.ItemSize(ImVec2(0.0, label_height)) -- Don't declare unclipped width, it'll be fed ContentMaxPosHeadersIdeal
|
||||||
if not ImGui.ItemAdd(bb, id) then
|
if not ImGui.ItemAdd(bb, id) then
|
||||||
return
|
return
|
||||||
@@ -3626,13 +3690,13 @@ function ImGui.TableHeader(label)
|
|||||||
-- ... 2 3 4 ... 2 3 4 (Display order)
|
-- ... 2 3 4 ... 2 3 4 (Display order)
|
||||||
-- - The other constraints are enforced by TableQueueSetColumnDisplayOrder() which might early out.
|
-- - The other constraints are enforced by TableQueueSetColumnDisplayOrder() which might early out.
|
||||||
table_.InstanceInteracted = table_.InstanceCurrent
|
table_.InstanceInteracted = table_.InstanceCurrent
|
||||||
if g.IO.MouseDelta.x < 0.0 and g.IO.MousePos.x < cell_r.Min.x then
|
if g.IO.MouseDelta.x < 0.0 and g.IO.MousePos.x < cell_x1 then
|
||||||
local prev_column = (column.PrevEnabledColumn ~= -1) and table_.Columns[column.PrevEnabledColumn] or nil
|
local prev_column = (column.PrevEnabledColumn ~= -1) and table_.Columns[column.PrevEnabledColumn] or nil
|
||||||
if prev_column then
|
if prev_column then
|
||||||
ImGui.TableQueueSetColumnDisplayOrder(table_, column_n, prev_column.DisplayOrder)
|
ImGui.TableQueueSetColumnDisplayOrder(table_, column_n, prev_column.DisplayOrder)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if g.IO.MouseDelta.x > 0.0 and g.IO.MousePos.x > cell_r.Max.x then
|
if g.IO.MouseDelta.x > 0.0 and g.IO.MousePos.x > cell_x2 then
|
||||||
local next_column = (column.NextEnabledColumn ~= -1) and table_.Columns[column.NextEnabledColumn] or nil
|
local next_column = (column.NextEnabledColumn ~= -1) and table_.Columns[column.NextEnabledColumn] or nil
|
||||||
if next_column then
|
if next_column then
|
||||||
ImGui.TableQueueSetColumnDisplayOrder(table_, column_n, next_column.DisplayOrder)
|
ImGui.TableQueueSetColumnDisplayOrder(table_, column_n, next_column.DisplayOrder)
|
||||||
@@ -3643,10 +3707,10 @@ function ImGui.TableHeader(label)
|
|||||||
-- Sort order arrow
|
-- Sort order arrow
|
||||||
-- - Only clip label for a visible arrow. Auto-fit still accounts for a possible arrow, but when
|
-- - Only clip label for a visible arrow. Auto-fit still accounts for a possible arrow, but when
|
||||||
-- manually sized smaller we don't clip the label for the sake of an arrow that isn't displayed.
|
-- manually sized smaller we don't clip the label for the sake of an arrow that isn't displayed.
|
||||||
local ellipsis_max = ImMax(cell_r.Max.x - (sort_arrow and (w_arrow + w_sort_text) or 0.0), label_pos.x)
|
local ellipsis_max = mathMax(cell_x2 - (sort_arrow and (w_arrow + w_sort_text) or 0.0), label_pos.x)
|
||||||
if band(table_.Flags, ImGuiTableFlags.Sortable) ~= 0 and band(column.Flags, ImGuiTableColumnFlags.NoSort) == 0 then
|
if band(table_.Flags, ImGuiTableFlags.Sortable) ~= 0 and band(column.Flags, ImGuiTableColumnFlags.NoSort) == 0 then
|
||||||
if column.SortOrder ~= -1 then
|
if column.SortOrder ~= -1 then
|
||||||
local x = ImMax(cell_r.Min.x, cell_r.Max.x - w_arrow - w_sort_text)
|
local x = mathMax(cell_x1, cell_x2 - w_arrow - w_sort_text)
|
||||||
local y = label_pos.y
|
local y = label_pos.y
|
||||||
if column.SortOrder > 0 then
|
if column.SortOrder > 0 then
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.Text, 0.70))
|
ImGui.PushStyleColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.Text, 0.70))
|
||||||
|
|||||||
+108
-79
@@ -73,6 +73,8 @@ local IM_U32_MAX = UINT_MAX -- (0xFFFFFFFF)
|
|||||||
--- @param text string
|
--- @param text string
|
||||||
--- @param text_end? int
|
--- @param text_end? int
|
||||||
--- @param flags? ImGuiTextFlags
|
--- @param flags? ImGuiTextFlags
|
||||||
|
local TextEx_bb = ImRect() -- [Luau] scratch, never escapes TextEx()
|
||||||
|
|
||||||
@native
|
@native
|
||||||
function ImGui.TextEx(text, text_end, flags)
|
function ImGui.TextEx(text, text_end, flags)
|
||||||
if not flags then flags = 0 end
|
if not flags then flags = 0 end
|
||||||
@@ -94,23 +96,29 @@ function ImGui.TextEx(text, text_end, flags)
|
|||||||
text_end = #text + 1
|
text_end = #text + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
local text_pos = ImVec2(window.DC.CursorPos.x, window.DC.CursorPos.y + window.DC.CurrLineTextBaseOffset)
|
local dc = window.DC
|
||||||
local wrap_pos_x = window.DC.TextWrapPos
|
local wrap_pos_x = dc.TextWrapPos
|
||||||
local wrap_enabled = (wrap_pos_x >= 0.0)
|
local wrap_enabled = (wrap_pos_x >= 0.0)
|
||||||
if (text_end - 1 <= 2000) or wrap_enabled then
|
if (text_end - 1 <= 2000) or wrap_enabled then
|
||||||
-- Common case
|
-- Common case
|
||||||
local wrap_width = wrap_enabled and ImGui.CalcWrapWidthForPos(window.DC.CursorPos, wrap_pos_x) or 0.0
|
-- [Luau] scalar text_pos, scratch bb (ItemAdd() copies it, RenderTextWrapped() only reads it)
|
||||||
local text_size = ImGui.CalcTextSize(text, text_end, false, wrap_width)
|
local text_pos_x, text_pos_y = dc.CursorPos.x, dc.CursorPos.y + dc.CurrLineTextBaseOffset
|
||||||
|
local wrap_width = wrap_enabled and ImGui.CalcWrapWidthForPos(dc.CursorPos, wrap_pos_x) or 0.0
|
||||||
|
local text_size = ImGui.CalcTextSizeEx(text, 1, text_end, false, wrap_width)
|
||||||
|
|
||||||
local bb = ImRect(text_pos, text_pos + text_size)
|
local bb = TextEx_bb
|
||||||
|
local bb_min, bb_max = bb.Min, bb.Max
|
||||||
|
bb_min.x = text_pos_x; bb_min.y = text_pos_y
|
||||||
|
bb_max.x = text_pos_x + text_size.x; bb_max.y = text_pos_y + text_size.y
|
||||||
ImGui.ItemSize(text_size, 0.0)
|
ImGui.ItemSize(text_size, 0.0)
|
||||||
if not ImGui.ItemAdd(bb, 0) then
|
if not ImGui.ItemAdd(bb, 0) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Render (we don't hide text after ## in this end-user function)
|
-- Render (we don't hide text after ## in this end-user function)
|
||||||
ImGui.RenderTextWrapped(bb.Min, text, text_end, wrap_width)
|
ImGui.RenderTextWrapped(bb_min, text, text_end, wrap_width)
|
||||||
else
|
else
|
||||||
|
local text_pos = ImVec2(dc.CursorPos.x, dc.CursorPos.y + dc.CurrLineTextBaseOffset)
|
||||||
-- Long text!
|
-- Long text!
|
||||||
-- Perform manual coarse clipping to optimize for long multi-line text
|
-- Perform manual coarse clipping to optimize for long multi-line text
|
||||||
-- - From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled.
|
-- - From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled.
|
||||||
@@ -197,7 +205,8 @@ end
|
|||||||
--- @param ... any
|
--- @param ... any
|
||||||
@native
|
@native
|
||||||
function ImGui.TextV(fmt, ...)
|
function ImGui.TextV(fmt, ...)
|
||||||
local window = ImGui.GetCurrentWindow()
|
local window = GImGui.CurrentWindow -- [Luau] inline GetCurrentWindow()
|
||||||
|
window.WriteAccessed = true
|
||||||
if window.SkipItems then
|
if window.SkipItems then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@@ -340,19 +349,18 @@ function ImGui.BulletText(fmt, ...)
|
|||||||
local text_end = #text + 1
|
local text_end = #text + 1
|
||||||
local label_size = ImGui.CalcTextSize(text, text_end, false)
|
local label_size = ImGui.CalcTextSize(text, text_end, false)
|
||||||
local total_size = ImVec2(g.FontSize + ((label_size.x > 0.0) and (label_size.x + style.FramePadding.x * 2) or 0.0), label_size.y) -- Empty text doesn't add padding
|
local total_size = ImVec2(g.FontSize + ((label_size.x > 0.0) and (label_size.x + style.FramePadding.x * 2) or 0.0), label_size.y) -- Empty text doesn't add padding
|
||||||
local pos = ImVec2()
|
-- [Luau] scalar pos, no temporary ImVec2
|
||||||
ImVec2_Copy(pos, window.DC.CursorPos)
|
local pos_x, pos_y = window.DC.CursorPos.x, window.DC.CursorPos.y + window.DC.CurrLineTextBaseOffset
|
||||||
pos.y = pos.y + window.DC.CurrLineTextBaseOffset
|
|
||||||
ImGui.ItemSize(total_size, 0.0)
|
ImGui.ItemSize(total_size, 0.0)
|
||||||
local bb = ImRect(pos, pos + total_size)
|
local bb = ImRect(pos_x, pos_y, pos_x + total_size.x, pos_y + total_size.y)
|
||||||
if not ImGui.ItemAdd(bb, 0) then
|
if not ImGui.ItemAdd(bb, 0) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Render
|
-- Render
|
||||||
local text_col = ImGui.GetColorU32(ImGuiCol.Text)
|
local text_col = ImGui.GetColorU32(ImGuiCol.Text)
|
||||||
ImGui.RenderBullet(window.DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5, g.FontSize * 0.5), text_col)
|
ImGui.RenderBullet(window.DrawList, ImVec2(bb.Min.x + (style.FramePadding.x + g.FontSize * 0.5), bb.Min.y + g.FontSize * 0.5), text_col)
|
||||||
ImGui.RenderText(bb.Min + ImVec2(g.FontSize + style.FramePadding.x * 2, 0.0), text, 1, text_end, false)
|
ImGui.RenderText(ImVec2(bb.Min.x + (g.FontSize + style.FramePadding.x * 2), bb.Min.y + 0.0), text, 1, text_end, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- va_list variants: identical to the varargs versions in Lua
|
-- va_list variants: identical to the varargs versions in Lua
|
||||||
@@ -730,6 +738,8 @@ end
|
|||||||
--- @param size_arg? ImVec2
|
--- @param size_arg? ImVec2
|
||||||
--- @param flags? ImGuiButtonFlags
|
--- @param flags? ImGuiButtonFlags
|
||||||
--- @return bool
|
--- @return bool
|
||||||
|
local ButtonEx_text_min, ButtonEx_text_max = ImVec2(), ImVec2()
|
||||||
|
|
||||||
@native
|
@native
|
||||||
function ImGui.ButtonEx(label, size_arg, flags)
|
function ImGui.ButtonEx(label, size_arg, flags)
|
||||||
if size_arg == nil then size_arg = ImVec2(0, 0) end
|
if size_arg == nil then size_arg = ImVec2(0, 0) end
|
||||||
@@ -746,15 +756,16 @@ function ImGui.ButtonEx(label, size_arg, flags)
|
|||||||
local label_end = ImGui.FindRenderedTextEnd(label)
|
local label_end = ImGui.FindRenderedTextEnd(label)
|
||||||
local label_size = ImGui.CalcTextSize(label, label_end, false)
|
local label_size = ImGui.CalcTextSize(label, label_end, false)
|
||||||
|
|
||||||
local pos = ImVec2() -- Don't modify the cursor!
|
-- [Luau] scalar pos, no temporary ImVec2
|
||||||
ImVec2_Copy(pos, window.DC.CursorPos)
|
local pos_x, pos_y = window.DC.CursorPos.x, window.DC.CursorPos.y -- Don't modify the cursor!
|
||||||
if bit32.band(flags, ImGuiButtonFlags.AlignTextBaseLine) ~= 0 and style.FramePadding.y < window.DC.CurrLineTextBaseOffset then -- Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)
|
local frame_padding = style.FramePadding
|
||||||
pos.y = pos.y + window.DC.CurrLineTextBaseOffset - style.FramePadding.y
|
if bit32.band(flags, ImGuiButtonFlags.AlignTextBaseLine) ~= 0 and frame_padding.y < window.DC.CurrLineTextBaseOffset then -- Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)
|
||||||
|
pos_y = pos_y + window.DC.CurrLineTextBaseOffset - frame_padding.y
|
||||||
end
|
end
|
||||||
local size = ImGui.CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0, label_size.y + style.FramePadding.y * 2.0)
|
local size = ImGui.CalcItemSize(size_arg, label_size.x + frame_padding.x * 2.0, label_size.y + frame_padding.y * 2.0)
|
||||||
|
|
||||||
local bb = ImRect(pos, pos + size)
|
local bb = ImRect(pos_x, pos_y, pos_x + size.x, pos_y + size.y)
|
||||||
ImGui.ItemSize(size, style.FramePadding.y)
|
ImGui.ItemSize(size, frame_padding.y)
|
||||||
if not ImGui.ItemAdd(bb, id) then
|
if not ImGui.ItemAdd(bb, id) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
@@ -776,7 +787,10 @@ function ImGui.ButtonEx(label, size_arg, flags)
|
|||||||
|
|
||||||
-- if (g.LogEnabled)
|
-- if (g.LogEnabled)
|
||||||
-- LogSetNextTextDecoration("[", "]");
|
-- LogSetNextTextDecoration("[", "]");
|
||||||
ImGui.RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, label_end, label_size, style.ButtonTextAlign, bb)
|
local text_min, text_max = ButtonEx_text_min, ButtonEx_text_max -- [Luau] scratch: RenderTextClipped() only reads them
|
||||||
|
text_min.x = bb.Min.x + frame_padding.x; text_min.y = bb.Min.y + frame_padding.y
|
||||||
|
text_max.x = bb.Max.x - frame_padding.x; text_max.y = bb.Max.y - frame_padding.y
|
||||||
|
ImGui.RenderTextClipped(text_min, text_max, label, label_end, label_size, style.ButtonTextAlign, bb)
|
||||||
|
|
||||||
-- Automatically close popups
|
-- Automatically close popups
|
||||||
--if (pressed && !(flags & ImGuiButtonFlags.DontClosePopups) && (window->Flags & ImGuiWindowFlags.Popup))
|
--if (pressed && !(flags & ImGuiButtonFlags.DontClosePopups) && (window->Flags & ImGuiWindowFlags.Popup))
|
||||||
@@ -1179,12 +1193,13 @@ function ImGui.Scrollbar(axis)
|
|||||||
-- Calculate scrollbar bounding box
|
-- Calculate scrollbar bounding box
|
||||||
local bb = ImGui.GetWindowScrollbarRect(window, axis)
|
local bb = ImGui.GetWindowScrollbarRect(window, axis)
|
||||||
local rounding_corners = ImGui.CalcRoundingFlagsForRectInRect(bb, window:Rect(), g.Style.WindowBorderSize)
|
local rounding_corners = ImGui.CalcRoundingFlagsForRectInRect(bb, window:Rect(), g.Style.WindowBorderSize)
|
||||||
local size_visible = window.InnerRect.Max[axis] - window.InnerRect.Min[axis]
|
local k = (axis == ImGuiAxis.X) and "x" or "y" -- [Luau] named key: ImVec2[axis] goes through IM_VEC2.__index
|
||||||
local size_contents = window.ContentSize[axis] + window.WindowPadding[axis] * 2.0
|
local size_visible = window.InnerRect.Max[k] - window.InnerRect.Min[k]
|
||||||
local scroll = window.Scroll[axis]
|
local size_contents = window.ContentSize[k] + window.WindowPadding[k] * 2.0
|
||||||
|
local scroll = window.Scroll[k]
|
||||||
local held
|
local held
|
||||||
held, scroll = ImGui.ScrollbarEx(bb, id, axis, scroll, size_visible, size_contents, rounding_corners)
|
held, scroll = ImGui.ScrollbarEx(bb, id, axis, scroll, size_visible, size_contents, rounding_corners)
|
||||||
window.Scroll[axis] = scroll
|
window.Scroll[k] = scroll
|
||||||
end
|
end
|
||||||
|
|
||||||
-- - Read about ImTextureID/ImTextureRef here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
|
-- - Read about ImTextureID/ImTextureRef here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
|
||||||
@@ -1316,9 +1331,8 @@ function ImGui.Checkbox(label, v)
|
|||||||
local label_end = ImGui.FindRenderedTextEnd(label)
|
local label_end = ImGui.FindRenderedTextEnd(label)
|
||||||
local label_size = ImGui.CalcTextSize(label, label_end, false)
|
local label_size = ImGui.CalcTextSize(label, label_end, false)
|
||||||
|
|
||||||
local square_sz = ImGui.GetFrameHeight()
|
local square_sz = g.FontSize + style.FramePadding.y * 2.0 -- [Luau] inline GetFrameHeight()
|
||||||
local pos = ImVec2()
|
local pos_x, pos_y = window.DC.CursorPos.x, window.DC.CursorPos.y -- [Luau] scalar pos, no temporary ImVec2
|
||||||
ImVec2_Copy(pos, window.DC.CursorPos)
|
|
||||||
|
|
||||||
local total_width
|
local total_width
|
||||||
if label_size.x > 0.0 then
|
if label_size.x > 0.0 then
|
||||||
@@ -1326,7 +1340,7 @@ function ImGui.Checkbox(label, v)
|
|||||||
else
|
else
|
||||||
total_width = square_sz
|
total_width = square_sz
|
||||||
end
|
end
|
||||||
local total_bb = ImRect(pos, pos + ImVec2(total_width, label_size.y + style.FramePadding.y * 2.0))
|
local total_bb = ImRect(pos_x, pos_y, pos_x + total_width, pos_y + (label_size.y + style.FramePadding.y * 2.0))
|
||||||
ImGui.ItemSize(total_bb, style.FramePadding.y)
|
ImGui.ItemSize(total_bb, style.FramePadding.y)
|
||||||
local is_visible = ImGui.ItemAdd(total_bb, id)
|
local is_visible = ImGui.ItemAdd(total_bb, id)
|
||||||
local is_multi_select = (bit32.band(g.LastItemData.ItemFlags, ImGuiItemFlags.IsMultiSelect) ~= 0)
|
local is_multi_select = (bit32.band(g.LastItemData.ItemFlags, ImGuiItemFlags.IsMultiSelect) ~= 0)
|
||||||
@@ -1358,7 +1372,7 @@ function ImGui.Checkbox(label, v)
|
|||||||
ImGui.MarkItemEdited(id)
|
ImGui.MarkItemEdited(id)
|
||||||
end
|
end
|
||||||
|
|
||||||
local check_bb = ImRect(pos, pos + ImVec2(square_sz, square_sz))
|
local check_bb = ImRect(pos_x, pos_y, pos_x + square_sz, pos_y + square_sz)
|
||||||
local mixed_value = (bit32.band(g.LastItemData.ItemFlags, ImGuiItemFlags.MixedValue) ~= 0)
|
local mixed_value = (bit32.band(g.LastItemData.ItemFlags, ImGuiItemFlags.MixedValue) ~= 0)
|
||||||
if is_visible then
|
if is_visible then
|
||||||
ImGui.RenderNavCursor(total_bb, id)
|
ImGui.RenderNavCursor(total_bb, id)
|
||||||
@@ -1379,12 +1393,12 @@ function ImGui.Checkbox(label, v)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y)
|
|
||||||
if g.LogEnabled then
|
if g.LogEnabled then
|
||||||
-- ImGui.LogRenderedText(label_pos, mixed_value and "[~]" or (v and "[x]" or "[ ]"))
|
-- ImGui.LogRenderedText(label_pos, mixed_value and "[~]" or (v and "[x]" or "[ ]"))
|
||||||
end
|
end
|
||||||
|
|
||||||
if is_visible and label_size.x > 0.0 then
|
if is_visible and label_size.x > 0.0 then
|
||||||
|
local label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y) -- [Luau] only built when used
|
||||||
ImGui.RenderText(label_pos, label, 1, label_end, false)
|
ImGui.RenderText(label_pos, label, 1, label_end, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1435,11 +1449,10 @@ function ImGui.RadioButtonEx(label, active)
|
|||||||
local label_end = ImGui.FindRenderedTextEnd(label)
|
local label_end = ImGui.FindRenderedTextEnd(label)
|
||||||
local label_size = ImGui.CalcTextSize(label, label_end, false)
|
local label_size = ImGui.CalcTextSize(label, label_end, false)
|
||||||
|
|
||||||
local square_sz = ImGui.GetFrameHeight()
|
local square_sz = g.FontSize + style.FramePadding.y * 2.0 -- [Luau] inline GetFrameHeight()
|
||||||
local pos = ImVec2()
|
local pos_x, pos_y = window.DC.CursorPos.x, window.DC.CursorPos.y -- [Luau] scalar pos, no temporary ImVec2
|
||||||
ImVec2_Copy(pos, window.DC.CursorPos)
|
local check_bb = ImRect(pos_x, pos_y, pos_x + square_sz, pos_y + square_sz)
|
||||||
local check_bb = ImRect(pos, pos + ImVec2(square_sz, square_sz))
|
local total_bb = ImRect(pos_x, pos_y, pos_x + (square_sz + (label_size.x > 0.0 and style.ItemInnerSpacing.x + label_size.x or 0.0)), pos_y + (label_size.y + style.FramePadding.y * 2.0))
|
||||||
local total_bb = ImRect(pos, pos + ImVec2(square_sz + (label_size.x > 0.0 and style.ItemInnerSpacing.x + label_size.x or 0.0), label_size.y + style.FramePadding.y * 2.0))
|
|
||||||
ImGui.ItemSize(total_bb, style.FramePadding.y)
|
ImGui.ItemSize(total_bb, style.FramePadding.y)
|
||||||
if not ImGui.ItemAdd(total_bb, id) then
|
if not ImGui.ItemAdd(total_bb, id) then
|
||||||
return false
|
return false
|
||||||
@@ -1683,12 +1696,15 @@ end
|
|||||||
-- - Separator()
|
-- - Separator()
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local LAYOUT_ZERO_SIZE = ImVec2(0, 0) -- [Luau] shared read-only: ItemSize() only reads it
|
||||||
|
local LayoutHelpers_size = ImVec2() -- [Luau] scratch for ItemSize() arguments
|
||||||
|
|
||||||
function ImGui.Spacing()
|
function ImGui.Spacing()
|
||||||
local window = ImGui.GetCurrentWindow()
|
local window = ImGui.GetCurrentWindow()
|
||||||
if window.SkipItems then
|
if window.SkipItems then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
ImGui.ItemSize(ImVec2(0, 0))
|
ImGui.ItemSize(LAYOUT_ZERO_SIZE)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param size ImVec2
|
--- @param size ImVec2
|
||||||
@@ -1698,7 +1714,8 @@ function ImGui.Dummy(size)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local bb = ImRect(window.DC.CursorPos, window.DC.CursorPos + size)
|
local cursor = window.DC.CursorPos
|
||||||
|
local bb = ImRect(cursor.x, cursor.y, cursor.x + size.x, cursor.y + size.y) -- [Luau] no temporary ImVec2
|
||||||
ImGui.ItemSize(size)
|
ImGui.ItemSize(size)
|
||||||
ImGui.ItemAdd(bb, 0)
|
ImGui.ItemAdd(bb, 0)
|
||||||
end
|
end
|
||||||
@@ -1755,8 +1772,9 @@ function ImGui.SeparatorEx(flags, thickness)
|
|||||||
-- Vertical separator, for menu bars (use current line height).
|
-- Vertical separator, for menu bars (use current line height).
|
||||||
local y1 = window.DC.CursorPos.y
|
local y1 = window.DC.CursorPos.y
|
||||||
local y2 = window.DC.CursorPos.y + window.DC.CurrLineSize.y
|
local y2 = window.DC.CursorPos.y + window.DC.CurrLineSize.y
|
||||||
local bb = ImRect(ImVec2(window.DC.CursorPos.x, y1), ImVec2(window.DC.CursorPos.x + thickness, y2))
|
local bb = ImRect(window.DC.CursorPos.x, y1, window.DC.CursorPos.x + thickness, y2) -- [Luau] no temporary ImVec2
|
||||||
ImGui.ItemSize(ImVec2(thickness, 0.0))
|
local item_size = LayoutHelpers_size; item_size.x = thickness; item_size.y = 0.0
|
||||||
|
ImGui.ItemSize(item_size)
|
||||||
if not ImGui.ItemAdd(bb, 0) then
|
if not ImGui.ItemAdd(bb, 0) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@@ -1788,8 +1806,9 @@ function ImGui.SeparatorEx(flags, thickness)
|
|||||||
-- This was mostly chosen to allow backward compatibility with user's code assuming zero-height when calculating height for layout (e.g. bottom alignment of a status bar).
|
-- This was mostly chosen to allow backward compatibility with user's code assuming zero-height when calculating height for layout (e.g. bottom alignment of a status bar).
|
||||||
-- In order to handle scaling we need to scale separator thickness and it would not makes sense to have a disparity depending on height.
|
-- In order to handle scaling we need to scale separator thickness and it would not makes sense to have a disparity depending on height.
|
||||||
----float thickness_for_layout = (thickness == 1.0f) ? 0.0f : thickness; // FIXME: See 1.70/1.71 Separator() change: makes legacy 1-px separator not affect layout yet. Should change.
|
----float thickness_for_layout = (thickness == 1.0f) ? 0.0f : thickness; // FIXME: See 1.70/1.71 Separator() change: makes legacy 1-px separator not affect layout yet. Should change.
|
||||||
local bb = ImRect(ImVec2(x1, window.DC.CursorPos.y), ImVec2(x2, window.DC.CursorPos.y + thickness))
|
local bb = ImRect(x1, window.DC.CursorPos.y, x2, window.DC.CursorPos.y + thickness) -- [Luau] no temporary ImVec2
|
||||||
ImGui.ItemSize(ImVec2(0.0, thickness))
|
local item_size = LayoutHelpers_size; item_size.x = 0.0; item_size.y = thickness
|
||||||
|
ImGui.ItemSize(item_size)
|
||||||
|
|
||||||
if ImGui.ItemAdd(bb, 0) then
|
if ImGui.ItemAdd(bb, 0) then
|
||||||
-- Draw
|
-- Draw
|
||||||
@@ -2682,19 +2701,18 @@ end
|
|||||||
-- fmt = "blah blah" -> return ""
|
-- fmt = "blah blah" -> return ""
|
||||||
-- fmt = "%.3f" -> return "%.3f"
|
-- fmt = "%.3f" -> return "%.3f"
|
||||||
-- fmt = "hello %.3f" -> return "%.3f"
|
-- fmt = "hello %.3f" -> return "%.3f"
|
||||||
-- TODO: temporary patch. does not look good
|
-- fmt = "%.3f hello" -> return "%.3f"
|
||||||
--- @param fmt string
|
--- @param fmt string
|
||||||
local function ImParseFormatTrimDecorations(fmt)
|
local function ImParseFormatTrimDecorations(fmt)
|
||||||
local fmt_start = ImParseFormatFindStart(fmt)
|
local fmt_start = ImParseFormatFindStart(fmt)
|
||||||
if string.byte(fmt, fmt_start) ~= 37 then -- '%'
|
if fmt_start == nil or string.byte(fmt, fmt_start) ~= 37 then -- '%'
|
||||||
return ""
|
return ""
|
||||||
end
|
end
|
||||||
--- @cast fmt_start int
|
|
||||||
local fmt_end = ImParseFormatFindEnd(fmt, fmt_start)
|
local fmt_end = ImParseFormatFindEnd(fmt, fmt_start)
|
||||||
if string.byte(fmt, fmt_end) ~= 37 then
|
if fmt_end > #fmt then -- If we only have leading decoration, we don't need to copy the data.
|
||||||
return string.sub(fmt, fmt_start)
|
return string.sub(fmt, fmt_start)
|
||||||
end
|
end
|
||||||
return string.sub(fmt, fmt_start, fmt_end)
|
return string.sub(fmt, fmt_start, fmt_end - 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- - For scanning we need to remove all width and precision fields and flags "%+3.7f" -> "%f". BUT don't strip types like "%I64d" which includes digits. ! "%07I64d" -> "%I64d"
|
-- - For scanning we need to remove all width and precision fields and flags "%+3.7f" -> "%f". BUT don't strip types like "%I64d" which includes digits. ! "%07I64d" -> "%I64d"
|
||||||
@@ -3381,6 +3399,7 @@ end
|
|||||||
-- [Luau] memo of string.format(format, value) for drag/slider value text (same values are redrawn every frame).
|
-- [Luau] memo of string.format(format, value) for drag/slider value text (same values are redrawn every frame).
|
||||||
-- ponytail: whole-memo reset at 4096 entries, LRU if values churn
|
-- ponytail: whole-memo reset at 4096 entries, LRU if values churn
|
||||||
local fmt_memo, fmt_memo_n = {}, 0
|
local fmt_memo, fmt_memo_n = {}, 0
|
||||||
|
local SliderScalar_total_bb, SliderScalar_grab_bb = ImRect(), ImRect() -- [Luau] scratch for SliderScalar()/DragScalar(), never escape
|
||||||
local DRAG_TEXT_ALIGN = ImVec2(0.5, 0.5)
|
local DRAG_TEXT_ALIGN = ImVec2(0.5, 0.5)
|
||||||
function FormatCached(format, v)
|
function FormatCached(format, v)
|
||||||
local m = fmt_memo[format]
|
local m = fmt_memo[format]
|
||||||
@@ -3412,8 +3431,11 @@ function ImGui.DragScalar(label, data_type, data, v_speed, min, max, format, fla
|
|||||||
|
|
||||||
local label_end = ImGui.FindRenderedTextEnd(label)
|
local label_end = ImGui.FindRenderedTextEnd(label)
|
||||||
local label_size = ImGui.CalcTextSize(label, label_end, false)
|
local label_size = ImGui.CalcTextSize(label, label_end, false)
|
||||||
local frame_bb = ImRect(window.DC.CursorPos, window.DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0))
|
local cursor = window.DC.CursorPos
|
||||||
local total_bb = ImRect(frame_bb.Min, frame_bb.Max + ImVec2((label_size.x > 0.0) and (style.ItemInnerSpacing.x + label_size.x) or 0.0, 0.0))
|
local frame_bb = ImRect(cursor.x, cursor.y, cursor.x + w, cursor.y + (label_size.y + style.FramePadding.y * 2.0)) -- [Luau] no temporary ImVec2
|
||||||
|
local total_bb = SliderScalar_total_bb -- [Luau] scratch: ItemSize()/ItemAdd() only read it
|
||||||
|
total_bb.Min.x = frame_bb.Min.x; total_bb.Min.y = frame_bb.Min.y
|
||||||
|
total_bb.Max.x = frame_bb.Max.x + ((label_size.x > 0.0) and (style.ItemInnerSpacing.x + label_size.x) or 0.0); total_bb.Max.y = frame_bb.Max.y + 0.0
|
||||||
|
|
||||||
local temp_input_allowed = (bit32.band(flags, ImGuiSliderFlags.NoInput) == 0)
|
local temp_input_allowed = (bit32.band(flags, ImGuiSliderFlags.NoInput) == 0)
|
||||||
ImGui.ItemSize(total_bb, style.FramePadding.y)
|
ImGui.ItemSize(total_bb, style.FramePadding.y)
|
||||||
@@ -3841,21 +3863,22 @@ function ImGui.SliderBehaviorT(TYPE, SIGNEDTYPE, bb, id, data_type, v, v_min, v_
|
|||||||
local style = g.Style
|
local style = g.Style
|
||||||
|
|
||||||
local axis = (bit32.band(flags, ImGuiSliderFlags.Vertical) ~= 0) and ImGuiAxis.Y or ImGuiAxis.X
|
local axis = (bit32.band(flags, ImGuiSliderFlags.Vertical) ~= 0) and ImGuiAxis.Y or ImGuiAxis.X
|
||||||
|
local axis_k = (axis == ImGuiAxis.X) and "x" or "y" -- [Luau] named key: ImVec2[axis] goes through IM_VEC2.__index
|
||||||
local is_logarithmic = bit32.band(flags, ImGuiSliderFlags.Logarithmic) ~= 0
|
local is_logarithmic = bit32.band(flags, ImGuiSliderFlags.Logarithmic) ~= 0
|
||||||
local is_floating_point = (data_type == ImGuiDataType.Float) or (data_type == ImGuiDataType.Double)
|
local is_floating_point = (data_type == ImGuiDataType.Float) or (data_type == ImGuiDataType.Double)
|
||||||
local v_range_f = (v_min < v_max) and (v_max - v_min) or (v_min - v_max) -- We don't need high precision for what we do with it.
|
local v_range_f = (v_min < v_max) and (v_max - v_min) or (v_min - v_max) -- We don't need high precision for what we do with it.
|
||||||
|
|
||||||
-- Calculate bounds
|
-- Calculate bounds
|
||||||
local grab_padding = 2.0 -- FIXME: Should be part of style.
|
local grab_padding = 2.0 -- FIXME: Should be part of style.
|
||||||
local slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0
|
local slider_sz = (bb.Max[axis_k] - bb.Min[axis_k]) - grab_padding * 2.0
|
||||||
local grab_sz = style.GrabMinSize
|
local grab_sz = style.GrabMinSize
|
||||||
if not is_floating_point and v_range_f >= 0.0 then -- v_range_f < 0 may happen on integer overflows
|
if not is_floating_point and v_range_f >= 0.0 then -- v_range_f < 0 may happen on integer overflows
|
||||||
grab_sz = ImMax(slider_sz / (v_range_f + 1), style.GrabMinSize) -- For integer sliders: if possible have the grab size represent 1 unit
|
grab_sz = ImMax(slider_sz / (v_range_f + 1), style.GrabMinSize) -- For integer sliders: if possible have the grab size represent 1 unit
|
||||||
end
|
end
|
||||||
grab_sz = ImMin(grab_sz, slider_sz)
|
grab_sz = ImMin(grab_sz, slider_sz)
|
||||||
local slider_usable_sz = slider_sz - grab_sz
|
local slider_usable_sz = slider_sz - grab_sz
|
||||||
local slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz * 0.5
|
local slider_usable_pos_min = bb.Min[axis_k] + grab_padding + grab_sz * 0.5
|
||||||
local slider_usable_pos_max = bb.Max[axis] - grab_padding - grab_sz * 0.5
|
local slider_usable_pos_max = bb.Max[axis_k] - grab_padding - grab_sz * 0.5
|
||||||
|
|
||||||
local logarithmic_zero_epsilon = 0.0 -- Only valid when is_logarithmic is true
|
local logarithmic_zero_epsilon = 0.0 -- Only valid when is_logarithmic is true
|
||||||
local zero_deadzone_halfsize = 0.0 -- Only valid when is_logarithmic is true
|
local zero_deadzone_halfsize = 0.0 -- Only valid when is_logarithmic is true
|
||||||
@@ -3875,7 +3898,7 @@ function ImGui.SliderBehaviorT(TYPE, SIGNEDTYPE, bb, id, data_type, v, v_min, v_
|
|||||||
if not g.IO.MouseDown[0] then
|
if not g.IO.MouseDown[0] then
|
||||||
ImGui.ClearActiveID()
|
ImGui.ClearActiveID()
|
||||||
else
|
else
|
||||||
local mouse_abs_pos = g.IO.MousePos[axis]
|
local mouse_abs_pos = g.IO.MousePos[axis_k]
|
||||||
if g.ActiveIdIsJustActivated then
|
if g.ActiveIdIsJustActivated then
|
||||||
local grab_t = ImGui.ScaleRatioFromValueT(data_type, v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize)
|
local grab_t = ImGui.ScaleRatioFromValueT(data_type, v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize)
|
||||||
if axis == ImGuiAxis.Y then
|
if axis == ImGuiAxis.Y then
|
||||||
@@ -3978,8 +4001,10 @@ function ImGui.SliderBehaviorT(TYPE, SIGNEDTYPE, bb, id, data_type, v, v_min, v_
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- [Luau] write out_grab_bb fields directly, no temporary ImRect
|
||||||
|
local og_min, og_max = out_grab_bb.Min, out_grab_bb.Max
|
||||||
if slider_sz < 1.0 then
|
if slider_sz < 1.0 then
|
||||||
ImRect_Copy(out_grab_bb, ImRect(bb.Min, bb.Min))
|
og_min.x = bb.Min.x; og_min.y = bb.Min.y; og_max.x = bb.Min.x; og_max.y = bb.Min.y
|
||||||
else
|
else
|
||||||
-- Output grab position so it can be displayed by the caller
|
-- Output grab position so it can be displayed by the caller
|
||||||
local grab_t = ImGui.ScaleRatioFromValueT(data_type, v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize)
|
local grab_t = ImGui.ScaleRatioFromValueT(data_type, v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize)
|
||||||
@@ -3988,9 +4013,9 @@ function ImGui.SliderBehaviorT(TYPE, SIGNEDTYPE, bb, id, data_type, v, v_min, v_
|
|||||||
end
|
end
|
||||||
local grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t)
|
local grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t)
|
||||||
if axis == ImGuiAxis.X then
|
if axis == ImGuiAxis.X then
|
||||||
ImRect_Copy(out_grab_bb, ImRect(grab_pos - grab_sz * 0.5, bb.Min.y + grab_padding, grab_pos + grab_sz * 0.5, bb.Max.y - grab_padding))
|
og_min.x = grab_pos - grab_sz * 0.5; og_min.y = bb.Min.y + grab_padding; og_max.x = grab_pos + grab_sz * 0.5; og_max.y = bb.Max.y - grab_padding
|
||||||
else
|
else
|
||||||
ImRect_Copy(out_grab_bb, ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz * 0.5, bb.Max.x - grab_padding, grab_pos + grab_sz * 0.5))
|
og_min.x = bb.Min.x + grab_padding; og_min.y = grab_pos - grab_sz * 0.5; og_max.x = bb.Max.x - grab_padding; og_max.y = grab_pos + grab_sz * 0.5
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -4081,9 +4106,11 @@ function ImGui.SliderScalar(label, data_type, data, min, max, format, flags)
|
|||||||
|
|
||||||
local label_end = ImGui.FindRenderedTextEnd(label)
|
local label_end = ImGui.FindRenderedTextEnd(label)
|
||||||
local label_size = ImGui.CalcTextSize(label, label_end, false)
|
local label_size = ImGui.CalcTextSize(label, label_end, false)
|
||||||
local frame_bb = ImRect(window.DC.CursorPos, window.DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0))
|
local cursor = window.DC.CursorPos
|
||||||
|
local frame_bb = ImRect(cursor.x, cursor.y, cursor.x + w, cursor.y + (label_size.y + style.FramePadding.y * 2.0)) -- [Luau] no temporary ImVec2
|
||||||
local total_bb_x = frame_bb.Max.x + ((label_size.x > 0.0) and (style.ItemInnerSpacing.x + label_size.x) or 0.0)
|
local total_bb_x = frame_bb.Max.x + ((label_size.x > 0.0) and (style.ItemInnerSpacing.x + label_size.x) or 0.0)
|
||||||
local total_bb = ImRect(frame_bb.Min, ImVec2(total_bb_x, frame_bb.Max.y))
|
local total_bb = SliderScalar_total_bb -- [Luau] scratch: ItemSize()/ItemAdd() only read it
|
||||||
|
total_bb.Min.x = frame_bb.Min.x; total_bb.Min.y = frame_bb.Min.y; total_bb.Max.x = total_bb_x; total_bb.Max.y = frame_bb.Max.y
|
||||||
|
|
||||||
local temp_input_allowed = bit32.band(flags, ImGuiSliderFlags.NoInput) == 0
|
local temp_input_allowed = bit32.band(flags, ImGuiSliderFlags.NoInput) == 0
|
||||||
ImGui.ItemSize(total_bb, style.FramePadding.y)
|
ImGui.ItemSize(total_bb, style.FramePadding.y)
|
||||||
@@ -4141,7 +4168,7 @@ function ImGui.SliderScalar(label, data_type, data, min, max, format, flags)
|
|||||||
ImGui.RenderFrameBorder(frame_bb.Min, frame_bb.Max, g.Style.FrameRounding)
|
ImGui.RenderFrameBorder(frame_bb.Min, frame_bb.Max, g.Style.FrameRounding)
|
||||||
|
|
||||||
-- Slider behavior
|
-- Slider behavior
|
||||||
local grab_bb = ImRect()
|
local grab_bb = SliderScalar_grab_bb -- [Luau] scratch: fully written by SliderBehavior(), only read below
|
||||||
local value_changed
|
local value_changed
|
||||||
data, value_changed = ImGui.SliderBehavior(frame_bb, id, data_type, data, min, max, format, flags, grab_bb)
|
data, value_changed = ImGui.SliderBehavior(frame_bb, id, data_type, data, min, max, format, flags, grab_bb)
|
||||||
if value_changed then
|
if value_changed then
|
||||||
@@ -5807,15 +5834,15 @@ function ImGui.InputTextEx(label, hint, buf, buf_size, size_arg, flags, callback
|
|||||||
in_len, c = ImStd.ImTextCharFromUtf8(clipboard, i, clipboard_end)
|
in_len, c = ImStd.ImTextCharFromUtf8(clipboard, i, clipboard_end)
|
||||||
i = i + in_len
|
i = i + in_len
|
||||||
c, ret2 = InputTextFilterCharacter(g, state, c, callback, callback_user_data, true)
|
c, ret2 = InputTextFilterCharacter(g, state, c, callback, callback_user_data, true)
|
||||||
if ret2 then
|
if not ret2 then
|
||||||
break
|
break -- continue
|
||||||
end
|
end
|
||||||
|
|
||||||
local c_utf8 = {0, 0, 0, 0, 0}
|
local c_utf8 = {0, 0, 0, 0, 0}
|
||||||
ImStd.ImTextCharToUtf8(c_utf8, c)
|
ImStd.ImTextCharToUtf8(c_utf8, c)
|
||||||
local out_len = ImStd.ImStrlen(c_utf8)
|
local out_len = ImStd.ImStrlen(c_utf8)
|
||||||
clipboard_filtered:resize(clipboard_filtered.Size + out_len)
|
clipboard_filtered:resize(clipboard_filtered.Size + out_len)
|
||||||
ImStd.memmove(clipboard_filtered.Data, clipboard_filtered.Size - out_len, c_utf8, 1, out_len)
|
ImStd.memmove(clipboard_filtered.Data, clipboard_filtered.Size - out_len + 1, c_utf8, 1, out_len)
|
||||||
|
|
||||||
until true
|
until true
|
||||||
end
|
end
|
||||||
@@ -7555,11 +7582,12 @@ function ImGui.TreeNodeBehavior(id, flags, label, label_end)
|
|||||||
-- When not framed, we vertically increase height up to typical framed widget height
|
-- When not framed, we vertically increase height up to typical framed widget height
|
||||||
local display_frame = bit32.band(flags, ImGuiTreeNodeFlags.Framed) ~= 0
|
local display_frame = bit32.band(flags, ImGuiTreeNodeFlags.Framed) ~= 0
|
||||||
local use_frame_padding = display_frame or (bit32.band(flags, ImGuiTreeNodeFlags.FramePadding) ~= 0)
|
local use_frame_padding = display_frame or (bit32.band(flags, ImGuiTreeNodeFlags.FramePadding) ~= 0)
|
||||||
local padding
|
-- [Luau] scalar padding, no temporary ImVec2
|
||||||
|
local padding_x, padding_y
|
||||||
if use_frame_padding then
|
if use_frame_padding then
|
||||||
padding = style.FramePadding
|
padding_x, padding_y = style.FramePadding.x, style.FramePadding.y
|
||||||
else
|
else
|
||||||
padding = ImVec2(style.FramePadding.x, ImMin(window.DC.CurrLineTextBaseOffset, style.FramePadding.y))
|
padding_x, padding_y = style.FramePadding.x, math.min(window.DC.CurrLineTextBaseOffset, style.FramePadding.y)
|
||||||
end
|
end
|
||||||
|
|
||||||
if label_end == nil then
|
if label_end == nil then
|
||||||
@@ -7567,18 +7595,18 @@ function ImGui.TreeNodeBehavior(id, flags, label, label_end)
|
|||||||
end
|
end
|
||||||
local label_size = ImGui.CalcTextSize(label, label_end, false)
|
local label_size = ImGui.CalcTextSize(label, label_end, false)
|
||||||
|
|
||||||
local text_offset_x = g.FontSize + (display_frame and (padding.x * 3) or (padding.x * 2)) -- Collapsing arrow width + Spacing
|
local text_offset_x = g.FontSize + (display_frame and (padding_x * 3) or (padding_x * 2)) -- Collapsing arrow width + Spacing
|
||||||
local text_offset_y = use_frame_padding and ImMax(style.FramePadding.y, window.DC.CurrLineTextBaseOffset) or window.DC.CurrLineTextBaseOffset -- Latch before ItemSize changes it
|
local text_offset_y = use_frame_padding and ImMax(style.FramePadding.y, window.DC.CurrLineTextBaseOffset) or window.DC.CurrLineTextBaseOffset -- Latch before ItemSize changes it
|
||||||
local text_width = g.FontSize + label_size.x + padding.x * 2 -- Include collapsing arrow
|
local text_width = g.FontSize + label_size.x + padding_x * 2 -- Include collapsing arrow
|
||||||
|
|
||||||
local frame_height = label_size.y + padding.y * 2
|
local frame_height = label_size.y + padding_y * 2
|
||||||
local span_all_columns = bit32.band(flags, ImGuiTreeNodeFlags.SpanAllColumns) ~= 0 and (g.CurrentTable ~= nil)
|
local span_all_columns = bit32.band(flags, ImGuiTreeNodeFlags.SpanAllColumns) ~= 0 and (g.CurrentTable ~= nil)
|
||||||
local span_all_columns_label = bit32.band(flags, ImGuiTreeNodeFlags.LabelSpanAllColumns) ~= 0 and (g.CurrentTable ~= nil)
|
local span_all_columns_label = bit32.band(flags, ImGuiTreeNodeFlags.LabelSpanAllColumns) ~= 0 and (g.CurrentTable ~= nil)
|
||||||
local frame_bb = ImRect()
|
local frame_bb = ImRect()
|
||||||
frame_bb.Min.x = span_all_columns and window.ParentWorkRect.Min.x or (bit32.band(flags, ImGuiTreeNodeFlags.SpanFullWidth) ~= 0 and window.WorkRect.Min.x or window.DC.CursorPos.x)
|
frame_bb.Min.x = span_all_columns and window.ParentWorkRect.Min.x or (bit32.band(flags, ImGuiTreeNodeFlags.SpanFullWidth) ~= 0 and window.WorkRect.Min.x or window.DC.CursorPos.x)
|
||||||
frame_bb.Min.y = window.DC.CursorPos.y + (text_offset_y - padding.y)
|
frame_bb.Min.y = window.DC.CursorPos.y + (text_offset_y - padding_y)
|
||||||
frame_bb.Max.x = span_all_columns and window.ParentWorkRect.Max.x or (bit32.band(flags, ImGuiTreeNodeFlags.SpanLabelWidth) ~= 0 and window.DC.CursorPos.x + text_width + padding.x or window.WorkRect.Max.x)
|
frame_bb.Max.x = span_all_columns and window.ParentWorkRect.Max.x or (bit32.band(flags, ImGuiTreeNodeFlags.SpanLabelWidth) ~= 0 and window.DC.CursorPos.x + text_width + padding_x or window.WorkRect.Max.x)
|
||||||
frame_bb.Max.y = window.DC.CursorPos.y + (text_offset_y - padding.y) + frame_height
|
frame_bb.Max.y = window.DC.CursorPos.y + (text_offset_y - padding_y) + frame_height
|
||||||
if display_frame then
|
if display_frame then
|
||||||
local outer_extend = IM_TRUNC(window.WindowPadding.x * 0.5) -- Framed header expand a little outside of current limits
|
local outer_extend = IM_TRUNC(window.WindowPadding.x * 0.5) -- Framed header expand a little outside of current limits
|
||||||
frame_bb.Min.x = frame_bb.Min.x - outer_extend
|
frame_bb.Min.x = frame_bb.Min.x - outer_extend
|
||||||
@@ -7586,7 +7614,8 @@ function ImGui.TreeNodeBehavior(id, flags, label, label_end)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local text_pos = ImVec2(window.DC.CursorPos.x + text_offset_x, window.DC.CursorPos.y + text_offset_y)
|
local text_pos = ImVec2(window.DC.CursorPos.x + text_offset_x, window.DC.CursorPos.y + text_offset_y)
|
||||||
ImGui.ItemSize(ImVec2(text_width, frame_height), padding.y)
|
local item_size = LayoutHelpers_size; item_size.x = text_width; item_size.y = frame_height -- [Luau] scratch, ItemSize() only reads it
|
||||||
|
ImGui.ItemSize(item_size, padding_y)
|
||||||
|
|
||||||
-- For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing
|
-- For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing
|
||||||
local interact_bb = ImRect()
|
local interact_bb = ImRect()
|
||||||
@@ -7670,7 +7699,7 @@ function ImGui.TreeNodeBehavior(id, flags, label, label_end)
|
|||||||
-- allow browsing a tree while preserving selection with code implementing multi-selection patterns.
|
-- allow browsing a tree while preserving selection with code implementing multi-selection patterns.
|
||||||
-- When clicking on the rest of the tree node we always disallow keyboard modifiers.
|
-- When clicking on the rest of the tree node we always disallow keyboard modifiers.
|
||||||
local arrow_hit_x1 = (text_pos.x - text_offset_x) - style.TouchExtraPadding.x
|
local arrow_hit_x1 = (text_pos.x - text_offset_x) - style.TouchExtraPadding.x
|
||||||
local arrow_hit_x2 = (text_pos.x - text_offset_x) + (g.FontSize + padding.x * 2.0) + style.TouchExtraPadding.x
|
local arrow_hit_x2 = (text_pos.x - text_offset_x) + (g.FontSize + padding_x * 2.0) + style.TouchExtraPadding.x
|
||||||
local is_mouse_x_over_arrow = (g.IO.MousePos.x >= arrow_hit_x1 and g.IO.MousePos.x < arrow_hit_x2)
|
local is_mouse_x_over_arrow = (g.IO.MousePos.x >= arrow_hit_x1 and g.IO.MousePos.x < arrow_hit_x2)
|
||||||
|
|
||||||
local is_multi_select = bit32.band(g.LastItemData.ItemFlags, ImGuiItemFlags.IsMultiSelect) ~= 0
|
local is_multi_select = bit32.band(g.LastItemData.ItemFlags, ImGuiItemFlags.IsMultiSelect) ~= 0
|
||||||
@@ -7785,9 +7814,9 @@ function ImGui.TreeNodeBehavior(id, flags, label, label_end)
|
|||||||
if bit32.band(flags, ImGuiTreeNodeFlags.Bullet) ~= 0 then
|
if bit32.band(flags, ImGuiTreeNodeFlags.Bullet) ~= 0 then
|
||||||
ImGui.RenderBullet(window.DrawList, ImVec2(text_pos.x - text_offset_x * 0.60, text_pos.y + g.FontSize * 0.5), text_col)
|
ImGui.RenderBullet(window.DrawList, ImVec2(text_pos.x - text_offset_x * 0.60, text_pos.y + g.FontSize * 0.5), text_col)
|
||||||
elseif not is_leaf then
|
elseif not is_leaf then
|
||||||
ImGui.RenderArrow(window.DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open and ((bit32.band(flags, ImGuiTreeNodeFlags.UpsideDownArrow) ~= 0) and ImGuiDir.Up or ImGuiDir.Down) or ImGuiDir.Right, 1.0)
|
ImGui.RenderArrow(window.DrawList, ImVec2(text_pos.x - text_offset_x + padding_x, text_pos.y), text_col, is_open and ((bit32.band(flags, ImGuiTreeNodeFlags.UpsideDownArrow) ~= 0) and ImGuiDir.Up or ImGuiDir.Down) or ImGuiDir.Right, 1.0)
|
||||||
else -- Leaf without bullet, left-adjusted text
|
else -- Leaf without bullet, left-adjusted text
|
||||||
text_pos.x = text_pos.x - (text_offset_x - padding.x)
|
text_pos.x = text_pos.x - (text_offset_x - padding_x)
|
||||||
end
|
end
|
||||||
if bit32.band(flags, ImGuiTreeNodeFlags.ClipLabelForTrailingButton) ~= 0 then
|
if bit32.band(flags, ImGuiTreeNodeFlags.ClipLabelForTrailingButton) ~= 0 then
|
||||||
frame_bb.Max.x = frame_bb.Max.x - (g.FontSize + style.FramePadding.x)
|
frame_bb.Max.x = frame_bb.Max.x - (g.FontSize + style.FramePadding.x)
|
||||||
@@ -7808,7 +7837,7 @@ function ImGui.TreeNodeBehavior(id, flags, label, label_end)
|
|||||||
if bit32.band(flags, ImGuiTreeNodeFlags.Bullet) ~= 0 then
|
if bit32.band(flags, ImGuiTreeNodeFlags.Bullet) ~= 0 then
|
||||||
ImGui.RenderBullet(window.DrawList, ImVec2(text_pos.x - text_offset_x * 0.5, text_pos.y + g.FontSize * 0.5), text_col)
|
ImGui.RenderBullet(window.DrawList, ImVec2(text_pos.x - text_offset_x * 0.5, text_pos.y + g.FontSize * 0.5), text_col)
|
||||||
elseif not is_leaf then
|
elseif not is_leaf then
|
||||||
ImGui.RenderArrow(window.DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15), text_col, is_open and ((bit32.band(flags, ImGuiTreeNodeFlags.UpsideDownArrow) ~= 0) and ImGuiDir.Up or ImGuiDir.Down) or ImGuiDir.Right, 0.70)
|
ImGui.RenderArrow(window.DrawList, ImVec2(text_pos.x - text_offset_x + padding_x, text_pos.y + g.FontSize * 0.15), text_col, is_open and ((bit32.band(flags, ImGuiTreeNodeFlags.UpsideDownArrow) ~= 0) and ImGuiDir.Up or ImGuiDir.Down) or ImGuiDir.Right, 0.70)
|
||||||
end
|
end
|
||||||
-- if g.LogEnabled then
|
-- if g.LogEnabled then
|
||||||
-- ImGui.LogSetNextTextDecoration(">", nil)
|
-- ImGui.LogSetNextTextDecoration(">", nil)
|
||||||
@@ -7816,7 +7845,7 @@ function ImGui.TreeNodeBehavior(id, flags, label, label_end)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if draw_tree_lines then
|
if draw_tree_lines then
|
||||||
ImGui.TreeNodeDrawLineToChildNode(ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.5))
|
ImGui.TreeNodeDrawLineToChildNode(ImVec2(text_pos.x - text_offset_x + padding_x, text_pos.y + g.FontSize * 0.5))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Label
|
-- Label
|
||||||
@@ -8059,7 +8088,7 @@ end
|
|||||||
function ImGui.Selectable(label, selected, flags, size_arg)
|
function ImGui.Selectable(label, selected, flags, size_arg)
|
||||||
if selected == nil then selected = false end
|
if selected == nil then selected = false end
|
||||||
if flags == nil then flags = 0 end
|
if flags == nil then flags = 0 end
|
||||||
if size_arg == nil then size_arg = ImVec2(0, 0) end
|
if size_arg == nil then size_arg = LAYOUT_ZERO_SIZE end -- [Luau] shared read-only default (only read below)
|
||||||
|
|
||||||
local window = ImGui.GetCurrentWindow()
|
local window = ImGui.GetCurrentWindow()
|
||||||
if (window.SkipItems) then
|
if (window.SkipItems) then
|
||||||
@@ -9327,7 +9356,7 @@ function ImGui.ShrinkWidths(items, first, count, width_excess, width_min)
|
|||||||
for n = 1, count do slice[n] = items[first + n - 1] end
|
for n = 1, count do slice[n] = items[first + n - 1] end
|
||||||
table.sort(slice, function(a, b)
|
table.sort(slice, function(a, b)
|
||||||
if a.Width ~= b.Width then return a.Width > b.Width end
|
if a.Width ~= b.Width then return a.Width > b.Width end
|
||||||
return a.Index < b.Index
|
return a.Index > b.Index -- ShrinkWidthItemComparer: b->Index - a->Index (Width compared exactly: upstream's (int) truncation isn't a strict weak order, which table.sort rejects)
|
||||||
end)
|
end)
|
||||||
for n = 1, count do items[first + n - 1] = slice[n] end
|
for n = 1, count do items[first + n - 1] = slice[n] end
|
||||||
local it = slice
|
local it = slice
|
||||||
|
|||||||
+222
-14
@@ -7,15 +7,34 @@
|
|||||||
# 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).
|
# Roblox target also writes ImGuiTyped.luau (types + require(script.Impl), see tools/gen_types.py).
|
||||||
import os, re
|
#
|
||||||
|
# Build-time optimizations (token rewrites with minify.py's lexer: strings/comments are never touched, every source line
|
||||||
|
# stays on its line so linemap.json still maps errors). IMGUI_BUNDLE_NOOPT=1 turns all off, IMGUI_BUNDLE_NO_<X>=1 one:
|
||||||
|
# A hot library globals (most referenced names assigned at a file's top level) become bundle-level locals declared
|
||||||
|
# before __FILES: every file's assignment/reference then hits an upvalue instead of GETGLOBAL. Fewer names for
|
||||||
|
# LuaJIT (60 upvalues per function). Library globals can't be patched from outside (use the returned table).
|
||||||
|
# B enum folding: the bundle is loaded once headless (Luau CLI) to dump enum-like tables (all values numbers, never
|
||||||
|
# written inside a function) and numeric constants assigned once at top level; reads `ImGuiCol.Text` / `FLT_MAX`
|
||||||
|
# inside function bodies become `0 --[[ImGuiCol.Text]]`. Load-time (top-level) code is left alone.
|
||||||
|
# C `--@hoist` functions in the sources (pure numeric helpers: ImMin, ImMax, ImClamp, ...) are moved to the bundle top
|
||||||
|
# as `local function`s (their source lines become blank) so Luau -O2 can inline them everywhere.
|
||||||
|
# E statement-form `IM_ASSERT(...)` becomes `assert(...)` (fast builtin); expression uses are left alone.
|
||||||
|
import os, re, sys, json, shutil, subprocess, tempfile
|
||||||
|
|
||||||
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
ROOT = os.path.dirname(HERE)
|
||||||
|
sys.path.insert(0, HERE)
|
||||||
|
from minify import TOKEN
|
||||||
|
|
||||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
||||||
import sys
|
|
||||||
NO_DEMO = "--no-demo" in sys.argv
|
NO_DEMO = "--no-demo" in sys.argv
|
||||||
LOVE = False
|
LOVE = False
|
||||||
if "--target" in sys.argv:
|
if "--target" in sys.argv:
|
||||||
i = sys.argv.index("--target"); LOVE = sys.argv[i + 1] == "love"; del sys.argv[i:i + 2]
|
i = sys.argv.index("--target"); LOVE = sys.argv[i + 1] == "love"; del sys.argv[i:i + 2]
|
||||||
sys.argv = [a for a in sys.argv if a != "--no-demo"]
|
sys.argv = [a for a in sys.argv if a != "--no-demo"]
|
||||||
|
OPT = {x: os.environ.get("IMGUI_BUNDLE_NOOPT") != "1" and os.environ.get("IMGUI_BUNDLE_NO_" + x) != "1" for x in "ABCE"}
|
||||||
|
# A limits: Luau 200 locals in the main chunk (+ helpers, __FILES, ...), 200 upvalues per function; LuaJIT 60 upvalues
|
||||||
|
# per function (margin left for nested functions that also capture file locals)
|
||||||
|
MAX_LOCALS, MAX_UPVALUES = 160, (45 if LOVE else 150)
|
||||||
BACKEND = "imgui_impl_love.lua" if LOVE else "imgui_impl_roblox.lua"
|
BACKEND = "imgui_impl_love.lua" if LOVE else "imgui_impl_roblox.lua"
|
||||||
SKIP = {"imgui_impl_roblox.lua", "imgui_impl_roblox_fonts.lua"} if LOVE else {"imgui_impl_love.lua"}
|
SKIP = {"imgui_impl_roblox.lua", "imgui_impl_roblox_fonts.lua"} if LOVE else {"imgui_impl_love.lua"}
|
||||||
FILES = {}
|
FILES = {}
|
||||||
@@ -26,6 +45,7 @@ for d in (ROOT, os.path.join(ROOT, "backends"), os.path.join(ROOT, "fonts")):
|
|||||||
if LOVE: src = re.sub(r"^@native$", "", src, flags=re.M) # keep line numbers
|
if LOVE: src = re.sub(r"^@native$", "", src, flags=re.M) # keep line numbers
|
||||||
FILES[f] = src
|
FILES[f] = src
|
||||||
|
|
||||||
|
ORIG = dict(FILES) # for gen_types
|
||||||
ENTRY = ["imgui.lua", BACKEND] + ([] if NO_DEMO else ["imgui_demo.lua"]) # run order
|
ENTRY = ["imgui.lua", BACKEND] + ([] if NO_DEMO else ["imgui_demo.lua"]) # run order
|
||||||
|
|
||||||
names = set()
|
names = set()
|
||||||
@@ -33,16 +53,207 @@ for src in FILES.values():
|
|||||||
names.update(re.findall(r"^function ([A-Za-z_]\w*)\s*\(", src, re.M))
|
names.update(re.findall(r"^function ([A-Za-z_]\w*)\s*\(", src, re.M))
|
||||||
names.update(re.findall(r"^([A-Za-z_]\w*)\s*=[^=]", src, re.M))
|
names.update(re.findall(r"^([A-Za-z_]\w*)\s*=[^=]", src, re.M))
|
||||||
|
|
||||||
|
# ---- lexer helpers ----
|
||||||
|
KW = set("and break do else elseif end false for function goto if in local nil not or repeat return then true until while".split())
|
||||||
|
|
||||||
|
def lex(src):
|
||||||
|
"""[(kind, text, start)] without whitespace/comments; kinds: name num str op."""
|
||||||
|
toks, pos = [], 0
|
||||||
|
while pos < len(src):
|
||||||
|
m = TOKEN.match(src, pos)
|
||||||
|
k = m.lastgroup
|
||||||
|
if k in ("ceq", "seq"): k = "lcom" if m.group(0).startswith("--") else "lstr"
|
||||||
|
if k not in ("ws", "lcom", "com"): toks.append(("str" if k == "lstr" else k, m.group(0), pos))
|
||||||
|
pos = m.end()
|
||||||
|
return toks
|
||||||
|
|
||||||
|
def fdepths(toks):
|
||||||
|
"""function nesting depth of every token (0 = runs at load time)"""
|
||||||
|
st, fd, out = [], 0, []
|
||||||
|
for k, t, _ in toks:
|
||||||
|
out.append(fd)
|
||||||
|
if k == "name":
|
||||||
|
if t == "function": st.append(1); fd += 1
|
||||||
|
elif t in ("if", "do", "repeat"): st.append(0)
|
||||||
|
elif t in ("end", "until"): fd -= st.pop()
|
||||||
|
assert not st, "unbalanced blocks"
|
||||||
|
return out
|
||||||
|
|
||||||
|
def declared(toks):
|
||||||
|
"""names declared as locals / parameters / loop variables anywhere"""
|
||||||
|
out, n = set(), len(toks)
|
||||||
|
for i, (k, t, _) in enumerate(toks):
|
||||||
|
j = None
|
||||||
|
if t == "local": j = i + 2 if toks[i + 1][1] == "function" else i + 1
|
||||||
|
elif t == "for": j = i + 1
|
||||||
|
elif t == "function":
|
||||||
|
j = i + 1
|
||||||
|
while toks[j][1] != "(": j += 1
|
||||||
|
j += 1
|
||||||
|
while j is not None and j < n and toks[j][0] == "name" and toks[j][1] not in KW:
|
||||||
|
out.add(toks[j][1])
|
||||||
|
j += 1
|
||||||
|
if j < n and toks[j][1] == ",": j += 1
|
||||||
|
else: break
|
||||||
|
return out
|
||||||
|
|
||||||
|
def apply(src, edits):
|
||||||
|
for s, e, r in sorted(edits, reverse=True): src = src[:s] + r + src[e:]
|
||||||
|
return src
|
||||||
|
|
||||||
|
def isref(toks, i):
|
||||||
|
"""toks[i] is a variable read/write (not a field name)"""
|
||||||
|
return toks[i][0] == "name" and (i == 0 or toks[i - 1][1] not in (".", ":"))
|
||||||
|
|
||||||
|
def nexttext(toks, i): return toks[i + 1][1] if i + 1 < len(toks) else ""
|
||||||
|
|
||||||
|
# ---- C: --@hoist helpers ----
|
||||||
|
hoisted, hoisted_names = [], []
|
||||||
|
if OPT["C"]:
|
||||||
|
for f, src in FILES.items():
|
||||||
|
while "--@hoist\n" in src:
|
||||||
|
s = src.index("--@hoist\n") + len("--@hoist\n")
|
||||||
|
toks = lex(src[s:])
|
||||||
|
depth, i = 0, 0
|
||||||
|
while True: # first `function` ... matching end
|
||||||
|
t = toks[i][1]
|
||||||
|
if t == "function" or t in ("if", "do", "repeat"): depth += 1
|
||||||
|
elif t in ("end", "until"):
|
||||||
|
depth -= 1
|
||||||
|
if depth == 0: break
|
||||||
|
i += 1
|
||||||
|
e = s + toks[i][2] + 3
|
||||||
|
text = src[s:e]
|
||||||
|
m = re.search(r"^(local )?function ([A-Za-z_]\w*)", text, re.M)
|
||||||
|
hoisted_names.append(m.group(2))
|
||||||
|
hoisted.append(text[:m.start()] + "local function " + text[m.start() + len(m.group(0)) - len(m.group(2)):])
|
||||||
|
src = src[:s - len("--@hoist\n")] + "\n" + re.sub(r"[^\n]", "", text) + src[e:]
|
||||||
|
FILES[f] = src
|
||||||
|
HOIST_DEPS = "local type, mathMin, mathMax, mathFloor, mathCeil, bitOr, bitLShift = type, math.min, math.max, math.floor, math.ceil, bit32.bor, bit32.lshift"
|
||||||
|
|
||||||
|
# ---- E: statement IM_ASSERT -> assert ----
|
||||||
|
STMT_PREV_KW = {"end", "true", "false", "nil", "break", "then", "do", "else", "repeat"}
|
||||||
|
def rewrite_assert(src):
|
||||||
|
toks, edits = lex(src), []
|
||||||
|
for i, (k, t, p) in enumerate(toks):
|
||||||
|
if t == "IM_ASSERT" and isref(toks, i) and nexttext(toks, i) == "(":
|
||||||
|
pk, pt = (toks[i - 1][0], toks[i - 1][1]) if i else ("op", ";")
|
||||||
|
if (pk == "name" and (pt not in KW or pt in STMT_PREV_KW)) or pk in ("num", "str") or pt in (")", "]", "}", ";", "..."):
|
||||||
|
edits.append((p, p + len(t), "assert"))
|
||||||
|
return apply(src, edits)
|
||||||
|
|
||||||
|
if OPT["E"]:
|
||||||
|
FILES = {f: rewrite_assert(s) for f, s in FILES.items()}
|
||||||
|
hoisted = [rewrite_assert(s) for s in hoisted]
|
||||||
|
|
||||||
|
# ---- B: enum / constant folding ----
|
||||||
|
LUAU = shutil.which("luau") or "/nix/store/2m2ja613nndihc8qm10f2rhq6znp1nh4-luau-0.734/bin/luau"
|
||||||
|
|
||||||
|
def header():
|
||||||
|
return ["local __FILES, __LOADED = {}, {}",
|
||||||
|
"function IM_INCLUDE(name)",
|
||||||
|
" local r = __LOADED[name]",
|
||||||
|
" if r == nil then r = __FILES[name](); if r == nil then r = true end; __LOADED[name] = r end",
|
||||||
|
" return r",
|
||||||
|
"end", ""]
|
||||||
|
|
||||||
|
def numtext(s):
|
||||||
|
v = float(s)
|
||||||
|
if v != v or v in (float("inf"), float("-inf")) or (v == 0 and s.startswith("-")): return None
|
||||||
|
r = str(int(v)) if v.is_integer() and abs(v) < 2 ** 53 else repr(v)
|
||||||
|
return "(%s)" % r if v < 0 else r
|
||||||
|
|
||||||
|
def dump_values():
|
||||||
|
"""{name: number} and {name: {key: number}} of the loaded library (imgui.lua and what it includes)"""
|
||||||
|
src = ["--!optimize 1"] + ([HOIST_DEPS] + hoisted if hoisted else []) + header()
|
||||||
|
for name, s in sorted(FILES.items()): src += ["__FILES[%r] = function()" % name, s, "end"]
|
||||||
|
src += ['IM_INCLUDE("imgui.lua")',
|
||||||
|
'local function D(n, v) if type(v) == "number" then print("C", n, string.format("%.17g", v)) elseif type(v) == "table" then',
|
||||||
|
' for k, x in pairs(v) do print("T", n, type(k) == "string" and k or "?", type(x) == "number" and string.format("%.17g", x) or "?") end end end']
|
||||||
|
src += ["D(%r, %s)" % (n, n) for n in sorted(names)]
|
||||||
|
with tempfile.NamedTemporaryFile("w", suffix=".luau", delete=False) as fp: fp.write("\n".join(src))
|
||||||
|
try:
|
||||||
|
p = subprocess.run([LUAU, fp.name], capture_output=True, text=True, timeout=60)
|
||||||
|
finally:
|
||||||
|
os.unlink(fp.name)
|
||||||
|
if p.returncode: raise RuntimeError("enum dump failed:\n" + p.stdout[-2000:] + p.stderr[-2000:])
|
||||||
|
consts, tables = {}, {}
|
||||||
|
for line in p.stdout.splitlines():
|
||||||
|
a = line.split("\t")
|
||||||
|
if a[0] == "C": consts[a[1]] = a[2]
|
||||||
|
elif a[0] == "T": tables.setdefault(a[1], {})[a[2]] = a[3]
|
||||||
|
tables = {n: t for n, t in tables.items() if "?" not in t and "?" not in t.values()}
|
||||||
|
return consts, tables
|
||||||
|
|
||||||
|
def fold(files):
|
||||||
|
consts, tables = dump_values()
|
||||||
|
lexed = {f: lex(s) for f, s in files.items()}
|
||||||
|
depth = {f: fdepths(t) for f, t in lexed.items()}
|
||||||
|
# tables written inside a function, constants assigned more than once or not at top level: not foldable
|
||||||
|
assigns = {}
|
||||||
|
for f, toks in lexed.items():
|
||||||
|
for i, (k, t, _) in enumerate(toks):
|
||||||
|
if not isref(toks, i): continue
|
||||||
|
if t in tables and depth[f][i] > 0:
|
||||||
|
j = i + 1
|
||||||
|
if nexttext(toks, i) == ".": j = i + 3
|
||||||
|
elif nexttext(toks, i) == "[":
|
||||||
|
b = 0
|
||||||
|
while True:
|
||||||
|
b += {"[": 1, "]": -1}.get(toks[j][1], 0); j += 1
|
||||||
|
if b == 0: break
|
||||||
|
if j < len(toks) and toks[j][1] == "=": tables.pop(t, None)
|
||||||
|
if t in consts and nexttext(toks, i) == "=":
|
||||||
|
assigns[t] = assigns.get(t, 0) + (1 if depth[f][i] == 0 else 2)
|
||||||
|
consts = {n: v for n, v in consts.items() if assigns.get(n) == 1 and numtext(v)}
|
||||||
|
out = {}
|
||||||
|
for f, toks in lexed.items():
|
||||||
|
shadow = declared(toks)
|
||||||
|
edits = []
|
||||||
|
for i, (k, t, p) in enumerate(toks):
|
||||||
|
if depth[f][i] == 0 or not isref(toks, i) or t in shadow: continue
|
||||||
|
if t in tables and nexttext(toks, i) == "." and i + 2 < len(toks):
|
||||||
|
key = toks[i + 2][1]
|
||||||
|
if key in tables[t] and numtext(tables[t][key]) and nexttext(toks, i + 2) not in ("=", ".", ":", "[", "(", "{") and toks[i + 3][0] != "str":
|
||||||
|
edits.append((p, toks[i + 2][2] + len(key), "%s --[[%s.%s]]" % (numtext(tables[t][key]), t, key)))
|
||||||
|
elif t in consts and nexttext(toks, i) not in ("=", ".", ":", "[", "(", "{") and toks[i + 1][0] != "str":
|
||||||
|
edits.append((p, p + len(t), "%s --[[%s]]" % (numtext(consts[t]), t)))
|
||||||
|
out[f] = apply(files[f], edits)
|
||||||
|
return out
|
||||||
|
|
||||||
|
if OPT["B"]:
|
||||||
|
try:
|
||||||
|
allf = dict(FILES); allf.update({"@hoist%d" % i: "local function __h()\n" + s + "\nend" for i, s in enumerate(hoisted)})
|
||||||
|
allf = fold(allf)
|
||||||
|
hoisted = [allf.pop("@hoist%d" % i)[len("local function __h()\n"):-len("\nend")] for i in range(len(hoisted))]
|
||||||
|
FILES = allf
|
||||||
|
except (OSError, RuntimeError) as e:
|
||||||
|
if os.environ.get("IMGUI_BUNDLE_STRICT"): raise # CI: a release must not silently lose the folding
|
||||||
|
print("bundle.py: enum folding skipped (%s)" % str(e).strip()[:300], file=sys.stderr)
|
||||||
|
|
||||||
|
# ---- A: hot globals -> bundle locals ----
|
||||||
|
hot = []
|
||||||
|
if OPT["A"]:
|
||||||
|
count, used = {}, []
|
||||||
|
for src in FILES.values():
|
||||||
|
toks = lex(src)
|
||||||
|
used.append({t for i, (k, t, _) in enumerate(toks) if isref(toks, i)})
|
||||||
|
for i, (k, t, _) in enumerate(toks):
|
||||||
|
if t in names and isref(toks, i): count[t] = count.get(t, 0) + 1
|
||||||
|
# greedy by reference count; each file function's upvalues (hoisted names it uses) stay <= MAX_UPVALUES
|
||||||
|
taken = [len(u & set(hoisted_names + re.findall(r"\w+", HOIST_DEPS.split("=")[0]))) for u in used]
|
||||||
|
for n in sorted((n for n in count if n not in hoisted_names), key=lambda n: (-count[n], n)):
|
||||||
|
if len(hot) == MAX_LOCALS: break
|
||||||
|
if all(taken[i] < MAX_UPVALUES for i, u in enumerate(used) if n in u):
|
||||||
|
hot.append(n)
|
||||||
|
taken = [c + (n in u) for c, u in zip(taken, used)]
|
||||||
|
|
||||||
out = ["--!optimize 2", "-- GENERATED by tools/bundle.py from *.lua, backends/, fonts/ - do not edit", ""]
|
out = ["--!optimize 2", "-- GENERATED by tools/bundle.py from *.lua, backends/, fonts/ - do not edit", ""]
|
||||||
if LOVE:
|
if LOVE:
|
||||||
out = ["-- GENERATED by tools/bundle.py --target love - do not edit", re.search(r"--@compat-begin\n(.*?)--@compat-end", open(os.path.join(ROOT, "backends", "imgui_impl_love.lua")).read(), re.S).group(1), ""]
|
out = ["-- GENERATED by tools/bundle.py --target love - do not edit", re.search(r"--@compat-begin\n(.*?)--@compat-end", open(os.path.join(ROOT, "backends", "imgui_impl_love.lua")).read(), re.S).group(1), ""]
|
||||||
out += [
|
if hot: out.append("local " + ", ".join(hot))
|
||||||
"local __FILES, __LOADED = {}, {}",
|
if hoisted: out += [HOIST_DEPS] + hoisted
|
||||||
"function IM_INCLUDE(name)",
|
out += header()
|
||||||
" local r = __LOADED[name]",
|
|
||||||
" if r == nil then r = __FILES[name](); if r == nil then r = true end; __LOADED[name] = r end",
|
|
||||||
" return r",
|
|
||||||
"end", ""]
|
|
||||||
linemap = [] # (first bundle line, file) for error -> source mapping (tools/test/run.py)
|
linemap = [] # (first bundle line, file) for error -> source mapping (tools/test/run.py)
|
||||||
for name, src in sorted(FILES.items()):
|
for name, src in sorted(FILES.items()):
|
||||||
out.append("__FILES[%r] = function()" % name)
|
out.append("__FILES[%r] = function()" % name)
|
||||||
@@ -58,15 +269,12 @@ for n in sorted(names):
|
|||||||
out.append(" %s = %s," % (n, n))
|
out.append(" %s = %s," % (n, n))
|
||||||
out.append("}")
|
out.append("}")
|
||||||
|
|
||||||
import sys
|
|
||||||
OUT = sys.argv[1] if len(sys.argv) > 1 else os.path.join(ROOT, "build")
|
OUT = sys.argv[1] if len(sys.argv) > 1 else os.path.join(ROOT, "build")
|
||||||
os.makedirs(OUT, exist_ok=True)
|
os.makedirs(OUT, exist_ok=True)
|
||||||
NAME = "imgui_love.lua" if LOVE else "ImGui.luau"
|
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
|
|
||||||
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:
|
if not LOVE:
|
||||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
||||||
from gen_types import wrapper
|
from gen_types import wrapper
|
||||||
open(os.path.join(OUT, "ImGuiTyped.luau"), "w").write(wrapper(FILES))
|
open(os.path.join(OUT, "ImGuiTyped.luau"), "w").write(wrapper(ORIG))
|
||||||
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,559 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# Compares the port against upstream Dear ImGui (tools/test/upstream/*) and writes docs/PORT_STATUS.txt.
|
||||||
|
# python3 tools/compare_upstream.py # print summary, write docs/PORT_STATUS.txt
|
||||||
|
# Ported %: upstream functions (by qualified name, overloads merged) that have a port definition; enum values; struct fields;
|
||||||
|
# public/internal API declarations in imgui.h / imgui_internal.h (namespace ImGui).
|
||||||
|
# Similarity %: for each ported function, both bodies are normalized to a common token stream (comments/types/punctuation
|
||||||
|
# dropped, C++ operators and names mapped to Lua: -> :: . ! && || _Flag) and compared with difflib ratio.
|
||||||
|
# File figures are weighted by upstream body size. Matching is by name, so it's an estimate, not a proof.
|
||||||
|
# Excluded from the %: C++-only (operators, destructors), #if 0, IMGUI_DISABLE_OBSOLETE_FUNCTIONS, OS-specific (#ifdef _WIN32...),
|
||||||
|
# test engine. They're listed separately in the report.
|
||||||
|
import os, re, sys
|
||||||
|
from difflib import SequenceMatcher
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
from minify import TOKEN as LUA_TOKEN
|
||||||
|
|
||||||
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
UP = os.path.join(ROOT, "tools", "test", "upstream")
|
||||||
|
CPP_FILES = ["imgui.cpp", "imgui_widgets.cpp", "imgui_draw.cpp", "imgui_tables.cpp", "imgui_demo.cpp", "imgui.h", "imgui_internal.h"]
|
||||||
|
PORT_FILES = sorted(f for f in os.listdir(ROOT) if f.endswith(".lua"))
|
||||||
|
LUA_CLASS_ALIAS = {"IM_RECT": "ImRect", "IM_VECTOR": "ImVector", "IM_POOL": "ImPool", "IMGUI_VIEWPORT_P": "ImGuiViewportP",
|
||||||
|
"ImGuiTextBufferMT": "ImGuiTextBuffer", "IMGUI_TEXT_INDEX": "ImGuiTextIndex", "IM_PAYLOAD": "ImGuiPayload",
|
||||||
|
"DN": "ImGuiDockNode", "IM_VTXBUF": "ImVector"}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
# C++ side
|
||||||
|
# ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
CPP_TOKEN = re.compile(r"""
|
||||||
|
(?P<nl>\n)
|
||||||
|
| (?P<ws>[ \t\r\f\v]+|\\\n)
|
||||||
|
| (?P<com>//[^\n]*|/\*.*?\*/)
|
||||||
|
| (?P<pp>\#(?:[^\n\\]|\\.|\\\n)*)
|
||||||
|
| (?P<str>(?:u8|u|U|L)?"(?:\\.|[^"\\\n])*"|'(?:\\.|[^'\\\n])*')
|
||||||
|
| (?P<num>0[xX][0-9a-fA-F']+[uUlL]*|(?:\d[\d']*\.?\d*|\.\d+)(?:[eE][+-]?\d+)?[fFuUlL]*)
|
||||||
|
| (?P<name>[A-Za-z_]\w*)
|
||||||
|
| (?P<op>->\*?|::|<<=|>>=|<<|>>|&&|\|\||==|!=|<=|>=|\+\+|--|[-+*/%&|^]=|\.\.\.|.)
|
||||||
|
""", re.S | re.X)
|
||||||
|
|
||||||
|
EXCLUDE_PP = [("if0", re.compile(r"^\s*#\s*if\s+0\b")),
|
||||||
|
("obsolete", re.compile(r"#\s*ifndef\s+IMGUI_DISABLE_OBSOLETE_FUNCTIONS|#\s*if\s+!defined\(IMGUI_DISABLE_OBSOLETE")),
|
||||||
|
("os-specific", re.compile(r"_WIN32|__APPLE__|__linux__|_MSC_VER|__EMSCRIPTEN__|IMGUI_DISABLE_WIN32|__ANDROID__|_XBOX|__MINGW")),
|
||||||
|
("test-engine", re.compile(r"IMGUI_ENABLE_TEST_ENGINE"))]
|
||||||
|
|
||||||
|
def cpp_tokens(src):
|
||||||
|
"""[(kind, text, line, excl)] without whitespace/comments; excl = exclusion tag from enclosing #if, or None."""
|
||||||
|
out, line, stack, sections = [], 1, [], []
|
||||||
|
for m in CPP_TOKEN.finditer(src):
|
||||||
|
kind, text = m.lastgroup, m.group(0)
|
||||||
|
if kind == "nl": line += 1; continue
|
||||||
|
if kind == "com":
|
||||||
|
s = re.match(r"//\s*\[SECTION\]\s*(.+)", text)
|
||||||
|
if s: sections.append((line, s.group(1).strip()))
|
||||||
|
line += text.count("\n"); continue
|
||||||
|
if kind == "ws": line += text.count("\n"); continue
|
||||||
|
if kind == "pp":
|
||||||
|
d = re.match(r"#\s*(\w+)", text); d = d.group(1) if d else ""
|
||||||
|
if d in ("if", "ifdef", "ifndef"):
|
||||||
|
stack.append(next((t for t, r in EXCLUDE_PP if r.search(text)), None))
|
||||||
|
elif d in ("else", "elif") and stack:
|
||||||
|
stack[-1] = None if stack[-1] in ("if0", "obsolete") else stack[-1] # #else of `#if 0` / obsolete is the live path
|
||||||
|
elif d == "endif" and stack:
|
||||||
|
stack.pop()
|
||||||
|
line += text.count("\n"); continue
|
||||||
|
out.append((kind, text, line, next((x for x in reversed(stack) if x), None)))
|
||||||
|
return out, sections
|
||||||
|
|
||||||
|
CTRL = {"if", "for", "while", "switch", "catch", "sizeof", "return", "alignof", "decltype", "IM_ASSERT", "defined"}
|
||||||
|
|
||||||
|
def match_back(toks, i, close, open_):
|
||||||
|
depth = 0
|
||||||
|
while i >= 0:
|
||||||
|
t = toks[i][1]
|
||||||
|
if t == close: depth += 1
|
||||||
|
elif t == open_:
|
||||||
|
depth -= 1
|
||||||
|
if depth == 0: return i
|
||||||
|
i -= 1
|
||||||
|
return -1
|
||||||
|
|
||||||
|
def match_fwd(toks, i):
|
||||||
|
depth = 0
|
||||||
|
pairs = {"{": "}", "(": ")", "[": "]"}
|
||||||
|
open_ = toks[i][1]; close = pairs[open_]
|
||||||
|
while i < len(toks):
|
||||||
|
t = toks[i][1]
|
||||||
|
if t == open_: depth += 1
|
||||||
|
elif t == close:
|
||||||
|
depth -= 1
|
||||||
|
if depth == 0: return i
|
||||||
|
i += 1
|
||||||
|
return len(toks) - 1
|
||||||
|
|
||||||
|
def func_head(toks, i):
|
||||||
|
"""toks[i] is '{'. If it opens a function body, return (class, name, name_tok_index, cpp_only) else None."""
|
||||||
|
j = i - 1
|
||||||
|
while j >= 0 and toks[j][1] in ("const", "noexcept", "override", "final", "IM_FMTARGS", "IM_FMTLIST"):
|
||||||
|
j -= 1
|
||||||
|
if j >= 0 and toks[j][1] == ")" and toks[match_back(toks, j, ")", "(") - 1][1] in ("IM_FMTARGS", "IM_FMTLIST"):
|
||||||
|
j = match_back(toks, j, ")", "(") - 2
|
||||||
|
# constructor initializer list: Foo::Foo(...) : a(1), b{2} {
|
||||||
|
k = j
|
||||||
|
while k >= 0 and toks[k][1] in (")", "}"):
|
||||||
|
o = match_back(toks, k, toks[k][1], "(" if toks[k][1] == ")" else "{")
|
||||||
|
if o <= 1: break
|
||||||
|
if toks[o - 2][1] in (",",) or (toks[o - 2][1] == ":" ):
|
||||||
|
if toks[o - 2][1] == ":": j = o - 3; break
|
||||||
|
k = o - 3; continue
|
||||||
|
break
|
||||||
|
if j < 0 or toks[j][1] != ")": return None
|
||||||
|
o = match_back(toks, j, ")", "(")
|
||||||
|
if o <= 0: return None
|
||||||
|
n = o - 1
|
||||||
|
name = toks[n][1]
|
||||||
|
cpp_only = False
|
||||||
|
if toks[n - 1][1] == "operator" or (n >= 1 and toks[n][0] == "op" and toks[n - 1][1] == "operator"):
|
||||||
|
return (None, "operator" + name, n, True)
|
||||||
|
if toks[n][0] == "op" and n >= 2 and toks[n - 1][1] == "operator": return (None, "operator", n, True)
|
||||||
|
if toks[n][0] != "name" or name in CTRL: return None
|
||||||
|
if toks[n - 1][1] == "~": cpp_only = True; name = "~" + name
|
||||||
|
cls = None
|
||||||
|
if toks[n - 1][1] == "::" or (cpp_only and toks[n - 2][1] == "::"):
|
||||||
|
q = n - 2 if not cpp_only else n - 3
|
||||||
|
cls = toks[q][1]
|
||||||
|
if toks[n - 1][1] == "operator": return (cls, "operator", n, True)
|
||||||
|
return (cls, name, n, cpp_only)
|
||||||
|
|
||||||
|
class UpFunc:
|
||||||
|
def __init__(s, file, cls, name, line, body, section, excl):
|
||||||
|
s.file, s.cls, s.name, s.line, s.body, s.section, s.excl = file, cls, name, line, body, section, excl
|
||||||
|
@property
|
||||||
|
def key(s): return "%s::%s" % (s.cls, s.name) if s.cls else s.name
|
||||||
|
|
||||||
|
def parse_cpp(fname):
|
||||||
|
src = open(os.path.join(UP, fname), encoding="utf-8", errors="replace").read()
|
||||||
|
toks, sections = cpp_tokens(src)
|
||||||
|
funcs, enums, structs, decls = [], {}, {}, {} # decls: namespace ImGui declarations (name -> line)
|
||||||
|
|
||||||
|
def section_at(line):
|
||||||
|
cur = ""
|
||||||
|
for l, s in sections:
|
||||||
|
if l <= line and l > 120: cur = s # skip the index at the top of each file
|
||||||
|
return cur
|
||||||
|
|
||||||
|
def scan(a, b, cls, ns):
|
||||||
|
i = a
|
||||||
|
stmt_start = a
|
||||||
|
while i < b:
|
||||||
|
k, t, line, excl = toks[i]
|
||||||
|
if t == "{":
|
||||||
|
head = func_head(toks, i)
|
||||||
|
e = match_fwd(toks, i)
|
||||||
|
pre = [x[1] for x in toks[stmt_start:i]]
|
||||||
|
if head:
|
||||||
|
hc, hn, ni, cpp_only = head
|
||||||
|
c = hc or cls
|
||||||
|
if cls and hn == cls: c = cls # inline ctor in struct
|
||||||
|
ex = "c++-only" if cpp_only or hn.startswith("~") else toks[ni][3]
|
||||||
|
if c and hn == c and fname.endswith(".h") and e - i < 4: ex = ex # trivial ctor kept
|
||||||
|
funcs.append(UpFunc(fname, c, hn, toks[ni][2], toks[i + 1:e], section_at(toks[ni][2]), ex))
|
||||||
|
elif "namespace" in pre:
|
||||||
|
q = pre.index("namespace")
|
||||||
|
scan(i + 1, e, cls, (pre[q + 1] if len(pre) > q + 1 else ""))
|
||||||
|
elif "enum" in pre:
|
||||||
|
nm = next((x for x in pre[pre.index("enum") + 1:] if re.match(r"[A-Za-z_]\w*$", x) and x not in ("class", "int")), None)
|
||||||
|
vals, j, depth = [], i + 1, 0
|
||||||
|
expect = True
|
||||||
|
while j < e:
|
||||||
|
tt = toks[j][1]
|
||||||
|
if tt in "([{": depth += 1
|
||||||
|
elif tt in ")]}": depth -= 1
|
||||||
|
elif depth == 0 and tt == ",": expect = True
|
||||||
|
elif expect and depth == 0 and toks[j][0] == "name":
|
||||||
|
vals.append((tt, toks[j][2], toks[j][3])); expect = False
|
||||||
|
j += 1
|
||||||
|
if nm: enums[nm] = (vals, line, section_at(line))
|
||||||
|
elif any(x in ("struct", "class", "union") for x in pre) and "=" not in pre:
|
||||||
|
names = [x for x in pre if re.match(r"[A-Za-z_]\w*$", x) and x not in ("struct", "class", "union", "IMGUI_API", "typedef", "final", "public", "private", "alignas")]
|
||||||
|
nm = names[0] if names else cls
|
||||||
|
if nm and "union" not in pre: structs.setdefault(nm, ([], line, section_at(line)))
|
||||||
|
scan(i + 1, e, nm, ns)
|
||||||
|
if nm and pre[:1] != ["union"] and toks[e + 1][1] != ";" and toks[e + 1][0] == "name" and cls:
|
||||||
|
pass
|
||||||
|
i = e + 1
|
||||||
|
if i < b and toks[i][1] == ";": i += 1
|
||||||
|
stmt_start = i
|
||||||
|
continue
|
||||||
|
if t == ";":
|
||||||
|
st = toks[stmt_start:i]
|
||||||
|
txt = [x[1] for x in st]
|
||||||
|
if txt and txt[0] in ("public", "private", "protected") and len(txt) > 1 and txt[1] == ":": st, txt = st[2:], txt[2:]
|
||||||
|
if txt and ns == "ImGui" and cls is None and "(" in txt and txt[0] not in ("typedef", "struct", "enum", "using"):
|
||||||
|
p = txt.index("(")
|
||||||
|
if p > 0 and st[p - 1][0] == "name" and txt[p - 1] not in CTRL:
|
||||||
|
decls.setdefault(txt[p - 1], (st[p - 1][2], st[p - 1][3], section_at(st[p - 1][2])))
|
||||||
|
elif txt and cls and txt[0] not in ("typedef", "friend", "static", "using", "enum", "struct", "class", "template", "IM_MSVC_RUNTIME_CHECKS_OFF", "IM_MSVC_RUNTIME_CHECKS_RESTORE"):
|
||||||
|
eq = txt.index("=") if "=" in txt else len(txt)
|
||||||
|
pa = txt.index("(") if "(" in txt else len(txt)
|
||||||
|
if pa >= eq: # not a method declaration
|
||||||
|
depth = 0
|
||||||
|
for j, x in enumerate(txt[:eq] + [";"]):
|
||||||
|
if x in ("<", "(", "["): depth += 1
|
||||||
|
elif x in (">", ")", "]"): depth -= 1
|
||||||
|
elif depth == 0 and x in (",", ";", ":") or (depth == 0 and x == "[" ):
|
||||||
|
if j > 0 and re.match(r"[A-Za-z_]\w*$", txt[j - 1]) and txt[j - 1] not in ("const", "unsigned", "int", "float"):
|
||||||
|
structs.setdefault(cls, ([], 0, ""))[0].append((txt[j - 1], st[j - 1][2], st[j - 1][3]))
|
||||||
|
if x == "[" and depth == 1 and j > 0 and re.match(r"[A-Za-z_]\w*$", txt[j - 1]):
|
||||||
|
structs.setdefault(cls, ([], 0, ""))[0].append((txt[j - 1], st[j - 1][2], st[j - 1][3]))
|
||||||
|
if eq < len(txt) and re.match(r"[A-Za-z_]\w*$", txt[eq - 1]):
|
||||||
|
structs.setdefault(cls, ([], 0, ""))[0].append((txt[eq - 1], st[eq - 1][2], st[eq - 1][3]))
|
||||||
|
stmt_start = i + 1
|
||||||
|
i += 1
|
||||||
|
scan(0, len(toks), None, "")
|
||||||
|
for k in structs: # dedupe fields
|
||||||
|
seen, out = set(), []
|
||||||
|
for f in structs[k][0]:
|
||||||
|
if f[0] not in seen: seen.add(f[0]); out.append(f)
|
||||||
|
structs[k] = (out,) + structs[k][1:]
|
||||||
|
return funcs, enums, structs, decls
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
# Lua side
|
||||||
|
# ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
def lua_tokens(src):
|
||||||
|
out, line, pos = [], 1, 0
|
||||||
|
while pos < len(src):
|
||||||
|
m = LUA_TOKEN.match(src, pos)
|
||||||
|
kind, text = m.lastgroup, m.group(0)
|
||||||
|
if kind in ("ceq", "seq"): kind = "lcom" if text.startswith("--") else "lstr"
|
||||||
|
if kind not in ("ws", "com", "lcom"):
|
||||||
|
out.append(("str" if kind in ("str", "lstr") else kind, text, line))
|
||||||
|
line += text.count("\n"); pos = m.end()
|
||||||
|
return out
|
||||||
|
|
||||||
|
OPENERS = {"function", "if", "do", "repeat"}
|
||||||
|
CLOSERS = {"end", "until"}
|
||||||
|
|
||||||
|
class PortFunc:
|
||||||
|
alias = None
|
||||||
|
def __init__(s, file, cls, name, line, body):
|
||||||
|
s.file, s.cls, s.name, s.line, s.body = file, cls, name, line, body
|
||||||
|
|
||||||
|
def parse_lua():
|
||||||
|
defs, enum_keys, globals_, fields = [], set(), set(), set()
|
||||||
|
for f in PORT_FILES:
|
||||||
|
toks = lua_tokens(open(os.path.join(ROOT, f), encoding="utf-8").read())
|
||||||
|
n = len(toks)
|
||||||
|
for i, (k, t, line) in enumerate(toks):
|
||||||
|
chain, start = None, None
|
||||||
|
if t == "function" and i + 1 < n and toks[i + 1][0] == "name":
|
||||||
|
j = i + 1; chain = [toks[j][1]]
|
||||||
|
while j + 2 < n and toks[j + 1][1] in (".", ":") and toks[j + 2][0] == "name":
|
||||||
|
chain.append(toks[j + 2][1]); j += 2
|
||||||
|
if j + 1 < n and toks[j + 1][1] == "(": start = j + 1
|
||||||
|
else: chain = None
|
||||||
|
elif k == "name" and i + 3 < n and toks[i + 1][1] == "=" and toks[i + 2][1] == "function" and toks[i + 3][1] == "(":
|
||||||
|
j = i; chain = [t]
|
||||||
|
while j >= 2 and toks[j - 1][1] == "." and toks[j - 2][0] == "name":
|
||||||
|
chain.insert(0, toks[j - 2][1]); j -= 2
|
||||||
|
start = i + 3; i0 = i + 2
|
||||||
|
if chain is None: continue
|
||||||
|
# body: from start's ')' to matching end
|
||||||
|
j = start
|
||||||
|
while toks[j][1] != ")": j += 1
|
||||||
|
body_start, depth, j = j + 1, 1, j + 1
|
||||||
|
while j < n and depth > 0:
|
||||||
|
tt = toks[j][1]
|
||||||
|
if tt in OPENERS and toks[j][0] == "name": depth += 1
|
||||||
|
elif tt in CLOSERS and toks[j][0] == "name": depth -= 1
|
||||||
|
j += 1
|
||||||
|
name = chain[-1]
|
||||||
|
cls = LUA_CLASS_ALIAS.get(chain[-2], chain[-2]) if len(chain) > 1 else None
|
||||||
|
pf = PortFunc(f, cls, name, line, toks[body_start:j - 1])
|
||||||
|
bt = [x[1] for x in pf.body]
|
||||||
|
if len(bt) >= 5 and bt[0] == "return" and bt[-3:] == ["(", "...", ")"] and all(x[0] == "name" or x[1] in (".", ":") for x in pf.body[1:-3]):
|
||||||
|
pf.alias = " ".join(bt[1:-3]) + "(...)"; pf.body = pf.body[1:-3] # forwarder: function(...) return ImGui.X(...) end
|
||||||
|
defs.append(pf)
|
||||||
|
# aliases / factories: ImGui.DragFloat2 = DragFloatN(2) ImFormatString = string.format ImGui.LogTextV = ImGui.LogText
|
||||||
|
for i, (k, t, line) in enumerate(toks):
|
||||||
|
if k != "name" or (i and toks[i - 1][2] == line) or t == "local": continue
|
||||||
|
j, chain = i, [t]
|
||||||
|
while j + 2 < n and toks[j + 1][1] == "." and toks[j + 2][0] == "name":
|
||||||
|
chain.append(toks[j + 2][1]); j += 2
|
||||||
|
if j + 2 >= n or toks[j + 1][1] != "=" or toks[j + 2][0] != "name" or toks[j + 2][1] in ("function", "nil", "true", "false", "not"): continue
|
||||||
|
if len(chain) == 1 and not re.match(r"(Im|STB|IMSTB|stb)", t): continue
|
||||||
|
if len(chain) > 1 and chain[0] not in ("ImGui", "ImStd", "ImStb", "MT") and chain[-2] not in LUA_CLASS_ALIAS: continue
|
||||||
|
rhs = [x for x in toks[j + 2:] if x[2] == toks[j + 2][2]]
|
||||||
|
cls = LUA_CLASS_ALIAS.get(chain[-2], chain[-2]) if len(chain) > 1 else None
|
||||||
|
pf = PortFunc(f, cls, chain[-1], line, rhs); pf.alias = " ".join(x[1] for x in rhs[:6])
|
||||||
|
defs.append(pf)
|
||||||
|
# enum tables / globals / fields
|
||||||
|
depth_stack = []
|
||||||
|
for i, (k, t, line) in enumerate(toks):
|
||||||
|
if t == "{":
|
||||||
|
owner = None
|
||||||
|
if i >= 2 and toks[i - 1][1] == "=" and toks[i - 2][0] == "name": owner = toks[i - 2][1]
|
||||||
|
depth_stack.append(owner)
|
||||||
|
elif t == "}":
|
||||||
|
if depth_stack: depth_stack.pop()
|
||||||
|
elif k == "name" and i + 1 < n and toks[i + 1][1] == "=":
|
||||||
|
prev = toks[i - 1][1] if i else ""
|
||||||
|
if prev in ("{", ",", ";") or (prev == "." ) or (prev not in ("local", "for") and toks[i - 1][0] != "name" and prev not in ("]", ")")):
|
||||||
|
fields.add(t)
|
||||||
|
if prev in ("{", ",") and depth_stack and depth_stack[-1]: enum_keys.add((depth_stack[-1], t))
|
||||||
|
if prev == "." and i >= 2: enum_keys.add((toks[i - 2][1], t))
|
||||||
|
if prev not in (".", "local", ",") and not depth_stack: globals_.add(t)
|
||||||
|
elif k == "str" and i >= 1 and toks[i - 1][1] == "[" and i + 2 < n and toks[i + 1][1] == "]" and toks[i + 2][1] == "=" and depth_stack and depth_stack[-1]:
|
||||||
|
enum_keys.add((depth_stack[-1], toks[i][1].strip("\"'")))
|
||||||
|
return defs, enum_keys, globals_, fields
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
# Normalization + similarity
|
||||||
|
# ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
CPP_DROP = {"const", "static", "float", "int", "unsigned", "signed", "bool", "char", "void", "auto", "double", "size_t", "short", "long",
|
||||||
|
"ImU8", "ImU16", "ImU32", "ImU64", "ImS8", "ImS16", "ImS32", "ImS64", "ImWchar", "ImGuiID", "struct", "inline", "IMGUI_API",
|
||||||
|
"constexpr", "template", "typename", "IM_UNUSED", "this", ";", "{", "}", "(", ")", "&", "*", "ImGuiContext", "ImGuiWindow", "ImGuiStyle",
|
||||||
|
"ImGuiIO", "ImDrawList", "ImFont", "ImRect", "ImVec2", "ImVec4", "case", "break", "default", ":", "[", "]"}
|
||||||
|
LUA_DROP = {"local", "then", "do", "end", "self", "(", ")", ";", "{", "}", "&", "*", "[", "]", ":", "bit32", "math", "string", "table"}
|
||||||
|
LUA_MAP = {"~=": "!=", "not": "!", "and": "&&", "or": "||", "nil": "NULL", "elseif": "else if", "band": "&", "bor": "|", "bxor": "^",
|
||||||
|
"bnot": "~", "lshift": "<<", "rshift": ">>", "btest": "&", "ImMax": "ImMax", "ImMin": "ImMin"}
|
||||||
|
CPP_MAP = {"->": ".", "::": ".", "nullptr": "NULL", "++": "+=", "--": "-=", "IM_COUNTOF": "#", "IM_ARRAYSIZE": "#", "true": "true", "false": "false"}
|
||||||
|
|
||||||
|
def norm_num(t):
|
||||||
|
try:
|
||||||
|
if t.lower().startswith("0x"): return str(int(t.rstrip("uUlL").replace("'", ""), 16))
|
||||||
|
return repr(float(t.rstrip("fFuUlL").replace("'", "")))
|
||||||
|
except ValueError: return t
|
||||||
|
|
||||||
|
def split_enum(t):
|
||||||
|
m = re.match(r"(Im[A-Za-z]+)_(\w+)$", t)
|
||||||
|
return [m.group(1), ".", m.group(2)] if m and not t.endswith("_") else [t]
|
||||||
|
|
||||||
|
def norm_cpp(body):
|
||||||
|
out = []
|
||||||
|
for k, t, _, _ in body:
|
||||||
|
if k == "num": out.append(norm_num(t)); continue
|
||||||
|
if k == "str": out.append("S"); continue
|
||||||
|
t = CPP_MAP.get(t, t)
|
||||||
|
if t in CPP_DROP: continue
|
||||||
|
out.extend(split_enum(t) if k == "name" else [t])
|
||||||
|
o2 = []
|
||||||
|
for i, t in enumerate(out): # drop the implicit ImGui:: qualifier (C++ calls inside the namespace omit it)
|
||||||
|
if t == "ImGui" and i + 1 < len(out) and out[i + 1] == ".": continue
|
||||||
|
if t == "." and o2 and o2[-1] in (".",): continue
|
||||||
|
o2.append(t)
|
||||||
|
return [t for i, t in enumerate(o2) if not (t == "." and (i == 0 or o2[i - 1] in ("(", ",", "=", "return")))]
|
||||||
|
|
||||||
|
def norm_lua(body):
|
||||||
|
out = []
|
||||||
|
for k, t, _ in body:
|
||||||
|
if k == "num": out.append(norm_num(t)); continue
|
||||||
|
if k == "str": out.append("S"); continue
|
||||||
|
if t in LUA_DROP: continue
|
||||||
|
t = LUA_MAP.get(t, t)
|
||||||
|
out.extend(t.split(" ") if " " in t else [t])
|
||||||
|
o2 = []
|
||||||
|
for i, t in enumerate(out):
|
||||||
|
if t == "ImGui" and i + 1 < len(out) and out[i + 1] == ".": continue
|
||||||
|
if t == "." and (not o2 or o2[-1] in ("&", "|", "^", "~", "<<", ">>", "=", ",", "return", "if", "while", "!")): continue
|
||||||
|
o2.append(t)
|
||||||
|
return o2
|
||||||
|
|
||||||
|
def similarity(a, b):
|
||||||
|
if not a and not b: return 1.0
|
||||||
|
return SequenceMatcher(None, a, b, autojunk=False).ratio()
|
||||||
|
|
||||||
|
def is_stub(port_norm, up_norm):
|
||||||
|
real = [t for t in port_norm if t not in ("return", "NULL", "false", "true", "0.0", "S", "IM_ASSERT", "!", ",", "=", "0")]
|
||||||
|
return len(up_norm) > 12 and (len(real) < 3 or not any(re.match(r"[A-Za-z_]\w*$", t) for t in real))
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
def main():
|
||||||
|
defs, enum_keys, lua_globals, lua_fields = parse_lua()
|
||||||
|
by_name = {}
|
||||||
|
for d in sorted(defs, key=lambda d: d.alias is not None): by_name.setdefault(d.name, []).append(d)
|
||||||
|
port_classes = {d.cls for d in defs if d.cls}
|
||||||
|
|
||||||
|
def find(u):
|
||||||
|
m = re.match(r"I?M?STB_TEXTEDIT_(\w+?)(_IMPL)?$", u.name)
|
||||||
|
if m: # STB_TEXTEDIT_INSERTCHARS / IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL -> ImStb.TEXTEDIT_*
|
||||||
|
c = [d for d in by_name.get("TEXTEDIT_" + m.group(1), []) if d.cls == "ImStb"]
|
||||||
|
return c[0] if c else None
|
||||||
|
cands = by_name.get(u.name, [])
|
||||||
|
if u.cls and u.cls not in ("ImGui",):
|
||||||
|
exact = [d for d in cands if d.cls == u.cls]
|
||||||
|
if exact: return exact[0]
|
||||||
|
if u.name == u.cls: # constructor: function ImGuiStyle() / MT.X.new
|
||||||
|
c = [d for d in cands if d.cls is None] or [d for d in by_name.get("new", []) if d.cls == u.cls]
|
||||||
|
return c[0] if c else None
|
||||||
|
if u.cls in port_classes: return None
|
||||||
|
loose = [d for d in cands if d.cls in (None, "ImGui")] if len(cands) == 1 else []
|
||||||
|
return loose[0] if loose else None
|
||||||
|
pref = [d for d in cands if d.cls == "ImGui"] + [d for d in cands if d.cls is None] + [d for d in cands if d.cls in ("ImStd", "ImStb")]
|
||||||
|
return pref[0] if pref else None
|
||||||
|
|
||||||
|
up_names = set()
|
||||||
|
for f in CPP_FILES:
|
||||||
|
for u in parse_cpp(f)[0]: up_names.add(u.name)
|
||||||
|
def expand(p, depth=0, seen=()):
|
||||||
|
"""Port body with calls to port-only helpers in the same file inlined (the port splits some big functions)."""
|
||||||
|
out, body = [], p.body
|
||||||
|
for i, x in enumerate(body):
|
||||||
|
out.append(x)
|
||||||
|
if depth < 3 and x[0] == "name" and i + 1 < len(body) and body[i + 1][1] == "(" and x[1] not in up_names and (i == 0 or body[i - 1][1] not in (".", ":")):
|
||||||
|
h = [d for d in by_name.get(x[1], []) if d.file == p.file and not d.alias and d is not p and d.name not in seen]
|
||||||
|
if h: out.extend(expand(h[0], depth + 1, seen + (p.name,)))
|
||||||
|
return out
|
||||||
|
|
||||||
|
report, summary = [], []
|
||||||
|
all_up = {}
|
||||||
|
enums_all, structs_all, decls_by_file = {}, {}, {}
|
||||||
|
for f in CPP_FILES:
|
||||||
|
funcs, enums, structs, decls = parse_cpp(f)
|
||||||
|
merged = {}
|
||||||
|
for u in funcs: # merge overloads (keep the largest body for similarity)
|
||||||
|
if u.key not in merged or len(u.body) > len(merged[u.key].body):
|
||||||
|
if u.key in merged and merged[u.key].excl is None: u.excl = None
|
||||||
|
merged[u.key] = u
|
||||||
|
elif u.excl is None: merged[u.key].excl = None
|
||||||
|
all_up[f] = list(merged.values())
|
||||||
|
for k, v in enums.items(): enums_all.setdefault(k, v)
|
||||||
|
for k, v in structs.items():
|
||||||
|
if k in structs_all: structs_all[k][0].extend(x for x in v[0] if x[0] not in {y[0] for y in structs_all[k][0]})
|
||||||
|
else: structs_all[k] = v
|
||||||
|
decls_by_file[f] = decls
|
||||||
|
|
||||||
|
tot = {"n": 0, "have": 0, "stub": 0, "size": 0, "size_have": 0, "simw": 0.0, "size_sim": 0}
|
||||||
|
for f in CPP_FILES:
|
||||||
|
rows = []
|
||||||
|
st = {"n": 0, "have": 0, "stub": 0, "size": 0, "size_have": 0, "simw": 0.0, "size_sim": 0}
|
||||||
|
for u in sorted(all_up[f], key=lambda u: u.line):
|
||||||
|
un = norm_cpp(u.body)
|
||||||
|
if u.excl:
|
||||||
|
rows.append((u.section, "[-]", "", u, None, u.excl)); continue
|
||||||
|
st["n"] += 1; st["size"] += len(un)
|
||||||
|
p = find(u)
|
||||||
|
if p is None:
|
||||||
|
rows.append((u.section, "[ ]", "", u, None, "")); continue
|
||||||
|
body = p.body
|
||||||
|
if p.alias: # ImGui.X = ImGui.Y / Factory(n): compare against the target's body when it is a plain reference
|
||||||
|
tgt = [x[1] for x in p.body if x[0] == "name"]
|
||||||
|
t = by_name.get(tgt[-1]) if tgt and all(x[1] == "." or x[0] == "name" for x in p.body) else None
|
||||||
|
t = [d for d in (t or []) if not d.alias]
|
||||||
|
body = t[0].body if t else None
|
||||||
|
if body is None: # alias of a Lua builtin / factory: ported, but no body to compare
|
||||||
|
st["have"] += 1; st["size_have"] += len(un)
|
||||||
|
rows.append((u.section, "[x]", " n/a", u, p, "= " + p.alias)); continue
|
||||||
|
pn = norm_lua(expand(p) if not p.alias else body)
|
||||||
|
sim = similarity(un, pn)
|
||||||
|
stub = is_stub(pn, un) and not p.alias
|
||||||
|
st["have"] += 1; st["size_have"] += len(un); st["simw"] += sim * len(un); st["size_sim"] += len(un)
|
||||||
|
if stub: st["stub"] += 1
|
||||||
|
rows.append((u.section, "[~]" if stub else "[x]", "%3.0f%%" % (100 * sim), u, p, "stub" if stub else ("= " + p.alias if p.alias else "")))
|
||||||
|
for k in tot: tot[k] += st[k]
|
||||||
|
summary.append((f, st))
|
||||||
|
report.append((f, rows))
|
||||||
|
|
||||||
|
def pct(a, b): return "%5.1f%%" % (100.0 * a / b) if b else " - "
|
||||||
|
|
||||||
|
# enums / fields / API declarations
|
||||||
|
enum_rows, enum_n, enum_have = [], 0, 0
|
||||||
|
for nm, (vals, line, sec) in sorted(enums_all.items(), key=lambda x: x[0]):
|
||||||
|
base = re.sub(r"(Private)?_$", "", nm)
|
||||||
|
missing, have, counted = [], 0, 0
|
||||||
|
for v, vline, excl in vals:
|
||||||
|
if excl or v.endswith("_COUNT") and False: continue
|
||||||
|
pre, _, key = v.partition("_")
|
||||||
|
ok = v in lua_globals or (pre, key) in enum_keys or (base, key) in enum_keys or (nm, v) in enum_keys or (base, v) in enum_keys
|
||||||
|
if not ok and not key: ok = v in lua_globals
|
||||||
|
enum_n += 1; counted += 1
|
||||||
|
if ok: have += 1; enum_have += 1
|
||||||
|
else: missing.append(v)
|
||||||
|
enum_rows.append((nm, have, counted, missing))
|
||||||
|
|
||||||
|
field_rows, field_n, field_have = [], 0, 0
|
||||||
|
for nm, (fl, line, sec) in sorted(structs_all.items()):
|
||||||
|
fl = [x for x in fl if not x[2]]
|
||||||
|
if not fl: continue
|
||||||
|
missing = [x[0] for x in fl if x[0] not in lua_fields]
|
||||||
|
field_n += len(fl); field_have += len(fl) - len(missing)
|
||||||
|
field_rows.append((nm, len(fl) - len(missing), len(fl), missing))
|
||||||
|
|
||||||
|
api_rows = []
|
||||||
|
port_names = set(by_name)
|
||||||
|
for f, label in (("imgui.h", "Public API (imgui.h, namespace ImGui)"), ("imgui_internal.h", "Internal API (imgui_internal.h, namespace ImGui)")):
|
||||||
|
d = {k: v for k, v in decls_by_file[f].items() if not v[1]}
|
||||||
|
have = [k for k in d if k in port_names]
|
||||||
|
api_rows.append((label, len(have), len(d), sorted((k for k in d if k not in port_names), key=lambda k: d[k][0]), d))
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------ summary (stdout + file)
|
||||||
|
head = []
|
||||||
|
head.append("%-22s %9s %9s %9s %11s %9s" % ("upstream file", "funcs", "ported", "by size", "similarity", "stubs"))
|
||||||
|
for f, s in summary:
|
||||||
|
head.append("%-22s %9d %9s %9s %11s %9d" % (f, s["n"], pct(s["have"], s["n"]), pct(s["size_have"], s["size"]),
|
||||||
|
pct(s["simw"], s["size_sim"]), s["stub"]))
|
||||||
|
head.append("%-22s %9d %9s %9s %11s %9d" % ("TOTAL", tot["n"], pct(tot["have"], tot["n"]), pct(tot["size_have"], tot["size"]),
|
||||||
|
pct(tot["simw"], tot["size_sim"]), tot["stub"]))
|
||||||
|
head.append("")
|
||||||
|
head.append("upstream code reproduced (similarity x ported size / all upstream size): %s" % pct(tot["simw"] / max(1, tot["size_sim"]) * tot["size_have"], tot["size"]))
|
||||||
|
for label, h, n, _, _ in api_rows: head.append("%-52s %4d / %4d %s" % (label, h, n, pct(h, n)))
|
||||||
|
head.append("%-52s %4d / %4d %s" % ("Enum values (imgui.h + imgui_internal.h)", enum_have, enum_n, pct(enum_have, enum_n)))
|
||||||
|
head.append("%-52s %4d / %4d %s" % ("Struct fields (imgui.h + imgui_internal.h)", field_have, field_n, pct(field_have, field_n)))
|
||||||
|
print("\n".join(head))
|
||||||
|
|
||||||
|
L = []
|
||||||
|
L.append("PORT STATUS: imgui-luau vs upstream Dear ImGui (docking, %s)" % re.search(r'IMGUI_VERSION\s+"([^"]+)"', open(os.path.join(UP, "imgui.h")).read()).group(1))
|
||||||
|
L.append("Generated by tools/compare_upstream.py - do not edit. Upstream sources: tools/test/upstream/.")
|
||||||
|
L.append("")
|
||||||
|
L.append("Columns: funcs = upstream function definitions (overloads merged), excluding [-] rows")
|
||||||
|
L.append(" ported = share that has a port definition with the same name")
|
||||||
|
L.append(" by size = same, weighted by upstream body size (tokens)")
|
||||||
|
L.append(" similarity = normalized token similarity of the ported bodies (difflib ratio), weighted by size")
|
||||||
|
L.append(" stubs = ported but (nearly) empty in the port")
|
||||||
|
L.append("Name-based matching: a renamed port function shows as [ ], a same-named one counts even if it differs. Treat as estimates.")
|
||||||
|
L.append("Port-only helpers called from a ported function (same file) are inlined before comparing, since the port splits some big functions.")
|
||||||
|
L.append("Known low scores that are not missing code: data-driven port code (e.g. DemoWindowTables' section table), the older 1.92")
|
||||||
|
L.append("polyline stroker (AddPolyline), Lua-native replacements (ImGuiStorage, ImStr*, ImFormatString -> string.format).")
|
||||||
|
L.append("")
|
||||||
|
L += head
|
||||||
|
L.append("")
|
||||||
|
L.append("Legend: [x] ported (sim %) [~] stub [ ] not ported [-] excluded (c++-only / obsolete / os-specific / #if 0 / test-engine)")
|
||||||
|
for f, rows in report:
|
||||||
|
L.append("")
|
||||||
|
L.append("=" * 100)
|
||||||
|
L.append("%s" % f)
|
||||||
|
L.append("=" * 100)
|
||||||
|
cur = None
|
||||||
|
for sec, mark, sim, u, p, note in rows:
|
||||||
|
if sec != cur:
|
||||||
|
cur = sec; L.append(""); L.append(" -- %s" % (sec or "(top)"))
|
||||||
|
where = "%s:%d" % (p.file, p.line) if p else ""
|
||||||
|
L.append(" %s %5s %-58s %-32s %s" % (mark, sim, u.key, where, note))
|
||||||
|
L.append("")
|
||||||
|
L.append("=" * 100)
|
||||||
|
L.append("API declarations not ported (by upstream line)")
|
||||||
|
L.append("=" * 100)
|
||||||
|
for label, h, n, missing, d in api_rows:
|
||||||
|
L.append(""); L.append(" %s: %d / %d" % (label, h, n))
|
||||||
|
cur = None
|
||||||
|
for k in missing:
|
||||||
|
if d[k][2] != cur: cur = d[k][2]; L.append(" -- %s" % (cur or "(top)"))
|
||||||
|
L.append(" [ ] ImGui::%s" % k)
|
||||||
|
L.append("")
|
||||||
|
L.append("=" * 100)
|
||||||
|
L.append("Enums (values ported / total; missing listed)")
|
||||||
|
L.append("=" * 100)
|
||||||
|
for nm, h, n, missing in enum_rows:
|
||||||
|
if not n: continue
|
||||||
|
L.append(" %-40s %4d / %4d%s" % (nm, h, n, (" missing: " + ", ".join(missing)) if missing else ""))
|
||||||
|
L.append("")
|
||||||
|
L.append("=" * 100)
|
||||||
|
L.append("Struct fields (name found in the port / total; missing listed). Field names are matched globally, so common names may over-count.")
|
||||||
|
L.append("=" * 100)
|
||||||
|
for nm, h, n, missing in field_rows:
|
||||||
|
L.append(" %-40s %4d / %4d%s" % (nm, h, n, (" missing: " + ", ".join(missing)) if missing else ""))
|
||||||
|
out = os.path.join(ROOT, "docs", "PORT_STATUS.txt")
|
||||||
|
open(out, "w").write("\n".join(L) + "\n")
|
||||||
|
print("\nwrote docs/PORT_STATUS.txt")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
+1
-1
@@ -68,7 +68,7 @@ def main():
|
|||||||
ap.add_argument("--quiet", action="store_true", help="no random input (scripted tests drive io themselves)")
|
ap.add_argument("--quiet", action="store_true", help="no random input (scripted tests drive io themselves)")
|
||||||
args = ap.parse_args()
|
args = ap.parse_args()
|
||||||
|
|
||||||
code, out = run(args, "")
|
code, out = run(args, os.environ.get("RUN_FLAGS", ""))
|
||||||
print(out if os.environ.get("FULL_OUTPUT") else out[-4000:])
|
print(out if os.environ.get("FULL_OUTPUT") else out[-4000:])
|
||||||
sys.exit(code)
|
sys.exit(code)
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
-- Backend pixel oracle: composites the ScreenGui (like t_composite) at checkpoints and prints "PIX <frame> <hash>".
|
||||||
|
-- Phases: resize (bottom-right, then top-left), move, scroll, hover, typing, new font sizes (glyph baking), colour drag.
|
||||||
|
-- Each phase ends with a few still frames, then a checkpoint. Run with RUN_FLAGS="ARG_FORCE=true" for a full redraw
|
||||||
|
-- every frame: incremental output must match it exactly (and match the previous backend version).
|
||||||
|
local G = require(game:GetService("ReplicatedStorage"):WaitForChild("ImGui"))
|
||||||
|
local ImGui, ImVec2, ImVec4 = G.ImGui, G.ImVec2, G.ImVec4
|
||||||
|
ImGui.CreateContext()
|
||||||
|
G.ImGui_ImplRoblox.Init()
|
||||||
|
G.ImGui_ImplRoblox.SetRenderRate(0)
|
||||||
|
if ARG_TILELIMIT then ImGui.GetIO().BackendPlatformUserData.TileLimit = ARG_TILELIMIT end
|
||||||
|
local io = ImGui.GetIO()
|
||||||
|
local W, H = 1280, 760
|
||||||
|
local col = ImVec4(0.4, 0.7, 0.2, 0.5)
|
||||||
|
local buf = ImGui.NewTextBuffer("type here", 128)
|
||||||
|
local slider = 0.3
|
||||||
|
|
||||||
|
local function composite(gui)
|
||||||
|
local screen = buffer.create(W * H * 3)
|
||||||
|
local function walk(inst, ox, oy, kx0, ky0, kx1, ky1)
|
||||||
|
local kids = inst:GetChildren()
|
||||||
|
table.sort(kids, function(a, b) return (a.ZIndex or 1) < (b.ZIndex or 1) end)
|
||||||
|
for _, c in ipairs(kids) do
|
||||||
|
if c.Visible ~= false then
|
||||||
|
local x, y = ox + (c.Position and c.Position.X or 0), oy + (c.Position and c.Position.Y or 0)
|
||||||
|
local cx0, cy0, cx1, cy1 = kx0, ky0, kx1, ky1
|
||||||
|
if c.ClipsDescendants then
|
||||||
|
cx0, cy0 = math.max(cx0, x), math.max(cy0, y)
|
||||||
|
cx1, cy1 = math.min(cx1, x + c.Size.X), math.min(cy1, y + c.Size.Y)
|
||||||
|
end
|
||||||
|
if c.ClassName == "ImageLabel" then
|
||||||
|
local img = c.ImageContent.Object
|
||||||
|
local px, sz = img.Pixels, img.Size.X
|
||||||
|
for ty = 0, sz - 1 do
|
||||||
|
local sy = y + ty
|
||||||
|
if sy >= cy0 and sy < cy1 then
|
||||||
|
for tx = 0, sz - 1 do
|
||||||
|
local sx = x + tx
|
||||||
|
if sx >= cx0 and sx < cx1 then
|
||||||
|
local o = (ty * sz + tx) * 4
|
||||||
|
local a = buffer.readu8(px, o + 3)
|
||||||
|
if a > 0 then
|
||||||
|
local d = (sy * W + sx) * 3
|
||||||
|
local fa = a / 255
|
||||||
|
for ch = 0, 2 do
|
||||||
|
local v = buffer.readu8(px, o + ch) * fa + buffer.readu8(screen, d + ch) * (1 - fa)
|
||||||
|
buffer.writeu8(screen, d + ch, math.floor(v + 0.5))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
walk(c, x, y, cx0, cy0, cx1, cy1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
walk(gui, 0, 0, 0, 0, W, H)
|
||||||
|
local h = 0
|
||||||
|
for i = 0, W * H * 3 - 4, 4 do h = (h * 31 + buffer.readu32(screen, i)) % 2147483647 end
|
||||||
|
return h
|
||||||
|
end
|
||||||
|
|
||||||
|
-- phase list: name, frames of action (then 6 still frames + checkpoint)
|
||||||
|
local PHASES = { { "start", 4 }, { "resize_br", 24 }, { "resize_tl", 24 }, { "move", 24 }, { "scroll", 30 }, { "hover", 30 },
|
||||||
|
{ "type", 24 }, { "fonts", 24 }, { "color", 20 } }
|
||||||
|
local STILL = ARG_STILL or (ARG_TILELIMIT and 40 or 6) -- tile limit: deferred tiles need more frames to settle
|
||||||
|
local PT = {}
|
||||||
|
local pi, pf = 1, 0
|
||||||
|
local pos, size, scroll, fsize = ImVec2(40, 40), ImVec2(460, 520), 0, 13
|
||||||
|
local frame = 0
|
||||||
|
local typing = "Hello, glyphs! 0123 xyz"
|
||||||
|
|
||||||
|
game:GetService("RunService").Stepped:Connect(function()
|
||||||
|
local ph = PHASES[pi]
|
||||||
|
if not ph then return end
|
||||||
|
frame += 1; pf += 1
|
||||||
|
local name, n = ph[1], ph[2]
|
||||||
|
local act = pf <= n
|
||||||
|
G.ImGui_ImplRoblox.NewFrame()
|
||||||
|
io.DeltaTime = 1 / 60
|
||||||
|
io:AddMousePosEvent(1200, 740)
|
||||||
|
if act then
|
||||||
|
if name == "resize_br" then size = ImVec2(460 + pf * 7, 520 + pf * 5)
|
||||||
|
elseif name == "resize_tl" then pos = ImVec2(40 + pf * 3, 40 + pf * 2); size = ImVec2(size.x - 3, size.y - 2)
|
||||||
|
elseif name == "move" then pos = ImVec2(pos.x + 5, pos.y + 3)
|
||||||
|
elseif name == "scroll" then scroll += 9
|
||||||
|
elseif name == "hover" then io:AddMousePosEvent(pos.x + 30 + (pf * 17) % 300, pos.y + 40 + (pf * 23) % 400)
|
||||||
|
elseif name == "type" then
|
||||||
|
if pf == 1 then io:AddMousePosEvent(pos.x + 120, pos.y + 37); io:AddMouseButtonEvent(0, true)
|
||||||
|
elseif pf == 2 then io:AddMouseButtonEvent(0, false)
|
||||||
|
else io:AddInputCharacter(string.byte(typing, (pf - 3) % #typing + 1)) end
|
||||||
|
elseif name == "fonts" then fsize = 13 + pf * 0.75
|
||||||
|
elseif name == "color" then
|
||||||
|
local p = ImVec2(700 + pf * 4, 120 + pf * 3)
|
||||||
|
io:AddMousePosEvent(p.x, p.y); io:AddMouseButtonEvent(0, pf < n)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ImGui.NewFrame()
|
||||||
|
ImGui.SetNextWindowPos(pos, G.ImGuiCond.Always); ImGui.SetNextWindowSize(size, G.ImGuiCond.Always)
|
||||||
|
ImGui.Begin("Stress")
|
||||||
|
ImGui.InputText("##in", buf, 128)
|
||||||
|
if name == "scroll" or scroll > 0 then ImGui.SetScrollY(scroll) end
|
||||||
|
for i = 1, 12 do ImGui.Button("Button " .. i); ImGui.SameLine(); slider = ImGui.SliderFloat("##s" .. i, slider, 0, 1) end
|
||||||
|
if ImGui.BeginTable("tbl", 4, bit32.bor(G.ImGuiTableFlags.Borders, G.ImGuiTableFlags.RowBg)) then
|
||||||
|
for r = 1, 30 do ImGui.TableNextRow(); for c = 1, 4 do ImGui.TableNextColumn(); ImGui.Text("r%d c%d", r, c) end end
|
||||||
|
ImGui.EndTable()
|
||||||
|
end
|
||||||
|
ImGui.PushFont(nil, fsize); ImGui.Text("Font size %.2f AaBbCc", fsize); ImGui.PopFont()
|
||||||
|
for i = 1, 30 do ImGui.TextWrapped("Line %d: the quick brown fox jumps over the lazy dog.", i) end
|
||||||
|
ImGui.End()
|
||||||
|
ImGui.SetNextWindowPos(ImVec2(660, 60), G.ImGuiCond.Always); ImGui.SetNextWindowSize(ImVec2(500, 620), G.ImGuiCond.Always)
|
||||||
|
ImGui.Begin("Pickers")
|
||||||
|
ImGui.ColorPicker4("bar", col, G.ImGuiColorEditFlags.PickerHueBar)
|
||||||
|
ImGui.End()
|
||||||
|
ImGui.Render()
|
||||||
|
local clk = os.realclock or os.clock
|
||||||
|
local t0 = clk()
|
||||||
|
G.ImGui_ImplRoblox.RenderDrawData(ImGui.GetDrawData())
|
||||||
|
if act then PT[name] = (PT[name] or 0) + (clk() - t0) * 1000 end
|
||||||
|
if pf == n then -- mid-action (not settled): only exact without a redraw budget
|
||||||
|
print("PIXA", name, frame, composite(game:GetService("Players").LocalPlayer.PlayerGui:FindFirstChild("ImGui")))
|
||||||
|
end
|
||||||
|
if pf == n + STILL then
|
||||||
|
local gui = game:GetService("Players").LocalPlayer.PlayerGui:FindFirstChild("ImGui")
|
||||||
|
print("PIX", name, frame, composite(gui))
|
||||||
|
pi += 1; pf = 0
|
||||||
|
if not PHASES[pi] then
|
||||||
|
for _, q in PHASES do print(string.format("PIXT %-10s %7.2f ms/frame", q[1], PT[q[1]] / q[2])) end
|
||||||
|
print("PIXELS DONE")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "4dbug/imgui-luau"
|
name = "4dbug/imgui-luau"
|
||||||
version = "0.1.3"
|
version = "0.2.0"
|
||||||
registry = "https://github.com/UpliftGames/wally-index"
|
registry = "https://github.com/UpliftGames/wally-index"
|
||||||
realm = "shared"
|
realm = "shared"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|||||||
Reference in New Issue
Block a user