Comments in benilla-ui: one line of fact each, no history, no record numbers (#143)

Part of #41: the comments of `benilla-ui` (the Lua UI engine, its FrameXML loader and its widgets) are rewritten to the rule in `docs/METHOD.md`, which is what the code does and the reference fact behind it, in a line. 43,140 comment lines become 15,161.

- **The mechanical strip.** The citations that were only a private record number go first, and each edit is shown to be a pure removal.
- **Wrong facts corrected.** The rewrite corrects the comments that stated wrong facts. Among them:
  - `0x64a480` is the case-sensitive compare (a byte `repz cmpsb`), and `0x64a4c0` the case-insensitive one.
  - A frame's id sits at `+0xb0`, not `+0xb4`. `EnableKeyboard`/`IsKeyboardEnabled` are `0x776ec0`/`0x776f90`.
  - A region's unpinned edge has no fallback to its owner's rect: the owner supplies only the scale.
  - `$parent` expands to the nearest named ancestor, or "Top" when none is named (`0x76c5b0`), not simply to the parent.
  - A StatusBar fills from its internal normalised fraction, not from `GetValue()`.
  - The addresses of `StartDuel` and `StartDuelUnit` were swapped.
  - Many "untraced" notes now give the traced reference fact: the drag threshold, the per-grip sizing edges, the empty-`autoFocus` skip, and `SetTexCoordModifiesRect`'s default.
  - Stock FrameXML line citations are checked against the stock 1.12 files and corrected. `ref-` file names become the stock file names.
- **Deviation labels.** "Deviation:" now marks only a choice with a reason. A surface that exists only for the retired Era addon target says so plainly: it is not 1.12.
- **Verification.** Every file strips to identical code, and `cargo fmt --check` is clean. Nine second readers compared every file old against new, restored the facts the rewrite had dropped, and checked each new citation against the code, the stock files, vmangos and the reference.

Behaviour differences the rewrite surfaced are filed as their own issues (#131–#142), and more are being validated. The comments here state what the code does. String literals are untouched; they are #92.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sam Sung
2026-09-25 01:58:49 +07:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 8505187668
commit fd386e75ae
233 changed files with 14108 additions and 42094 deletions
+64 -128
View File
@@ -1,20 +1,13 @@
//! The frame `Backdrop` mechanism — byte-faithful to the 1.12.1 client. A backdrop is the tiled
//! bg + 8-piece border a `<Backdrop>` element or Lua `SetBackdrop`
//! installs on a frame (the tooltip/dialog/panel plate). This module owns the **data** ([`Backdrop`])
//! and the **geometry** ([`pieces`]); the Lua verbs live in [`super::object`] and the XML parse in
//! [`crate::loader`]. The renderer is the app's job — [`pieces`] hands it screen-space quads with
//! explicit per-corner UVs.
//! The frame backdrop as the 1.12 client builds it: the tiled background and 8-piece border that a
//! `<Backdrop>` element or `SetBackdrop` installs. This module owns the data ([`Backdrop`]) and the
//! geometry ([`pieces`], screen-space quads with per-corner UVs); the app renders them.
use crate::layout::Rect;
/// The default `edgeSize`, **logical** px: the ctor writes device `DAT_0081c9b0 = 0.025`, which is
/// logical 32 through the ×1/1024·0.8 logical→device transform (`+0x48`, and
/// the usage string `edgeSize = 32`). Our layout is already in logical px, so we carry 32 directly.
/// The default `edgeSize`, logical 32: the constructor writes device 0.025 (`DAT_0081c9b0`).
pub const DEFAULT_EDGE_SIZE: f32 = 32.0;
/// The `BackgroundInsets` — the bg texture is the frame rect inset by these (the border is *not*
/// inset; it sits flush inside the frame rect). Default all 0 (`0x77e5f0`). Field order matches the
/// struct offsets `+0x50 top / +0x54 bottom / +0x58 left / +0x5c right`.
/// `BackgroundInsets`: the background's inset from the frame rect; the border is never inset.
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Insets {
pub left: f32,
@@ -23,37 +16,32 @@ pub struct Insets {
pub bottom: f32,
}
/// A frame's installed backdrop (the 0x68-byte struct at `frame+0x1ac`). The
/// field set is *exactly* what the compiled reader accepts — `bgFile, edgeFile, tile, tileSize,
/// edgeSize, insets` — plus the two colors; no other keys exist in the compiled reader
/// (`0x7776e0`). Colors default to opaque
/// **white** (the ctor `+0x60/+0x64 = 0xffffffff`); the XML `<Color>`/`<BorderColor>` *parser*
/// separately defaults a *present-but-partial* element to black, which the loader applies.
/// A frame's installed backdrop (the 0x68-byte struct at `frame+0x1ac`): exactly the keys the
/// reader accepts (`0x7776e0`) plus the two colours. The colours default to opaque white; the XML
/// parser defaults a present but partial `<Color>` or `<BorderColor>` to black.
#[derive(Clone, Debug, PartialEq)]
pub struct Backdrop {
/// The tiled background texture (`+0x24`); `None`/empty ⇒ no bg piece.
/// The background texture (`+0x24`); none or empty draws no background.
pub bg_file: Option<String>,
/// The 8-slice border texture (`+0x30`); `None`/empty ⇒ no border pieces.
/// The 8-slice border texture (`+0x30`); none or empty draws no border.
pub edge_file: Option<String>,
/// `tile` (`+0x40`): tile the bg (else stretch it). Default false.
/// Tile the background, else stretch it (`+0x40`).
pub tile: bool,
/// `tileSize` (`+0x4c`), logical px; `0.0` ⇒ use `edge_size` as the tile period (`0x77f0c0`).
/// Default 0.
/// `tileSize` in logical px (`+0x4c`); 0 tiles at `edge_size` (`0x77f0c0`).
pub tile_size: f32,
/// `edgeSize` (`+0x48`), logical px — the border piece size and the edge tiling period. Default 32.
/// `edgeSize` in logical px (`+0x48`): the border piece size and the edge tiling period.
pub edge_size: f32,
/// `BackgroundInsets` (`+0x50..0x5c`). Default 0.
/// `BackgroundInsets` (`+0x50..0x5c`).
pub insets: Insets,
/// bg vertex color (`+0x60`, `SetBackdropColor`). Default opaque white.
/// The background's vertex colour (`+0x60`, `SetBackdropColor`).
pub bg_color: [f32; 4],
/// border vertex color (`+0x64`, `SetBackdropBorderColor`; all 8 pieces, never the bg). Default white.
/// All 8 border pieces' vertex colour (`+0x64`, `SetBackdropBorderColor`).
pub border_color: [f32; 4],
}
impl Default for Backdrop {
fn default() -> Self {
// The ctor (`0x77e5f0`): files empty, tile 0, tileSize 0,
// edgeSize logical 32, insets 0, both colors 0xffffffff.
// The constructor (`0x77e5f0`).
Self {
bg_file: None,
edge_file: None,
@@ -76,59 +64,39 @@ impl Backdrop {
}
}
/// One drawable backdrop piece (the bg, or one of the 8 border pieces). Carries four explicit corner
/// **positions** (not a rect) so the shipped BR-inset bug's slant is representable, plus four explicit
/// per-corner **UVs** (the TOP/BOTTOM edges are rotated 90°, `0x77f0c0`). Both arrays are in
/// `[top-left, top-right, bottom-right, bottom-left]` screen order (y-up, the layout convention).
/// One drawable piece, the background or one of the 8 border pieces: explicit corners, not a rect,
/// so the bottom-right inset quirk can slant it, and per-corner UVs, since the top and bottom edges
/// are rotated 90° (`0x77f0c0`). Both arrays run `[TL, TR, BR, BL]`, y-up.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct BackdropPiece {
/// Corner positions in screen px, y-up: `[TL, TR, BR, BL]`.
/// Corner positions in screen px.
pub corners: [[f32; 2]; 4],
/// Corner UVs (texture space), same `[TL, TR, BR, BL]` order.
/// Corner UVs in texture space.
pub uvs: [[f32; 2]; 4],
/// The texture needs REPEAT (wrap) addressing — the edge strips run UVs `[0..N]` and a tiled bg
/// `[0..w/period]`. The corners' UVs stay in `[0,1]` but share the edge texture, so they carry
/// `true` too (one texture identity ⇒ the 9 pieces batch, `0x77e8d0`'s shared edge texture).
/// The texture needs repeat addressing. The corners stay in `[0,1]` but share the edge texture,
/// so they carry `true` too and the border batches as one texture (`0x77e8d0`).
pub tile: bool,
/// The bg piece (`true`) draws with [`Backdrop::bg_file`]/`bg_color`; a border piece (`false`)
/// with `edge_file`/`border_color`. The app resolves the path + tint from the [`Backdrop`].
/// Drawn with `bg_file` and `bg_color`, else with `edge_file` and `border_color`.
pub is_bg: bool,
}
const SLICE: f32 = 0.125;
/// Pull a piece's UV rect half a texel inward on every axis whose range is **bounded** — i.e. does
/// not tile — so bilinear magnification cannot reach across an atlas slice boundary.
///
/// The 8 border pieces live side by side in ONE texture, each occupying a [`SLICE`] of `u`, and the
/// sampler is Linear+Repeat. A quad whose `u` ends exactly on a slice boundary samples, at its own
/// edge, precisely the seam between two texels — so bilinear returns a 50/50 blend of this slice's
/// last column and the **neighbouring piece's first**. Half a texel of inset moves that edge sample
/// onto the texel's centre instead, which is the whole fix.
///
/// It is invisible at the reference's own 1024×768, where the 32-texel slice draws into a 16 px
/// border and the bleed is subpixel. We draw the same slice into ~40 device px at 1440p — a 1.25×
/// **magnification**, which gives the boundary texel a visible band of its own. In this atlas
/// (`ChatBubble-Backdrop`, 256×32) the column next to the TOP slice is `(255,251,255)` at alpha 0,
/// so what leaked in was **white**: the pale horizontal line the director reported through the
/// speech bubble (1402), with a fainter twin under it from the BOTTOM slice's own boundary.
///
/// The bounded-axis test is what keeps this from breaking the tiling it sits next to: `u` always
/// spans one slice and is always inset, while an edge strip's `v` runs `[0, run]` and only gets the
/// inset when `run ≤ 1`. A strip that genuinely repeats keeps its exact period — insetting a tiling
/// axis would make every period land a fraction short and walk the art along the run.
/// Pull a piece's UVs half a texel inward on every axis that does not tile, so bilinear
/// magnification cannot sample the neighbouring slice of the shared border atlas (in
/// `ChatBubble-Backdrop`, a white alpha-0 column: a pale line through the speech bubble). A tiling
/// axis, a strip's `v` past 1, is left exact, or each period would land short and walk the art.
#[must_use]
pub fn inset_atlas_bleed(uvs: [[f32; 2]; 4], tex_w: f32, tex_h: f32) -> [[f32; 2]; 4] {
let mut out = uvs;
for (axis, tex) in [(0usize, tex_w), (1usize, tex_h)] {
if tex < 2.0 {
continue; // a 1-texel axis has no interior to inset toward
continue; // a 1-texel axis has no interior
}
let (lo, hi) = uvs.iter().fold((f32::MAX, f32::MIN), |(lo, hi), c| {
(lo.min(c[axis]), hi.max(c[axis]))
});
// A tiling axis is left exactly alone (see above); `1.0` is one whole period, which is
// bounded and does get the inset — that is the corner pieces, whose `v` is `[0,1]`.
// A range of exactly 1, the corners' `v`, is bounded and gets the inset.
if hi - lo > 1.0 + f32::EPSILON {
continue;
}
@@ -143,8 +111,7 @@ pub fn inset_atlas_bleed(uvs: [[f32; 2]; 4], tex_w: f32, tex_h: f32) -> [[f32; 2
out
}
/// Axis-aligned corners `[TL, TR, BR, BL]` (y-up) for a piece spanning `x∈[x0,x1]`, `y∈[y0,y1]`
/// (`y1` = top). The bg overrides its own corners to reproduce the BR-inset bug.
/// Axis-aligned corners `[TL, TR, BR, BL]`, y-up.
fn aa_corners(x0: f32, x1: f32, y_bottom: f32, y_top: f32) -> [[f32; 2]; 4] {
[
[x0, y_top], // TL
@@ -154,15 +121,14 @@ fn aa_corners(x0: f32, x1: f32, y_bottom: f32, y_top: f32) -> [[f32; 2]; 4] {
]
}
/// Compute the backdrop's drawable pieces for a frame whose resolved rect is `frame` (y-up), in the
/// client's paint order: the bg (layer BACKGROUND) first, then the 8 border pieces (layer BORDER) —
/// `0x77e8d0`. Empty when neither file is set. All geometry is `0x77e8d0`; all UVs `0x77f0c0`.
/// The drawable pieces for a frame's resolved rect (y-up), in paint order: the background (layer
/// `BACKGROUND`), then the 8 border pieces (layer `BORDER`). Geometry `0x77e8d0`, UVs `0x77f0c0`.
pub fn pieces(frame: Rect, bd: &Backdrop) -> Vec<BackdropPiece> {
let mut out = Vec::with_capacity(9);
let e = bd.edge_size;
let (l, r, b, t) = (frame.left, frame.right, frame.bottom, frame.top);
// ── The background piece (`0x77e8d0`, the bg anchor row) ───────────────────────────────────
// ── The background piece ──
if bd.has_bg() {
let (il, ir, it, ib) = (
bd.insets.left,
@@ -170,18 +136,15 @@ pub fn pieces(frame: Rect, bd: &Backdrop) -> Vec<BackdropPiece> {
bd.insets.top,
bd.insets.bottom,
);
// The four SetPoint anchors, y-up. **⚠ BR uses the TOP inset**, not bottom
// — the shipped 1.12.1 bug ("the BR-inset quirk", bytes `8b 46 50` @ 0x77e9ae:
// `SetPoint(8, frame+0x24, 8, −right, +top, 0)` loads `+0x50` = top, not `+0x54` = bottom).
// Invisible whenever top==bottom (every shipping backdrop); reproduced for fidelity.
// The four anchors, y-up. Bottom-right takes the top inset, as the reference does
// (`0x77e9ae` loads `+0x50`, top, not `+0x54`); invisible when the two are equal.
let corners = [
[l + il, t - it], // TL ← frame TL (+left, −top)
[r - ir, t - it], // TR ← frame TR (−right, −top)
[r - ir, b + it], // BR ← frame BR (−right, +top) ⚠ bug: top inset
[l + il, b + ib], // BL ← frame BL (+left, +bottom)
[l + il, t - it],
[r - ir, t - it],
[r - ir, b + it], // BR: the top inset
[l + il, b + ib],
];
// Tiled bg UVs (`0x77f0c0`): period = tileSize, or edgeSize when tileSize==0. When not
// tiled, no SetTexCoord runs and the bg keeps [0,1] (stretch).
// Untiled, no `SetTexCoord` runs and the background stretches over `[0,1]` (`0x77f0c0`).
let uvs = if bd.tile {
let period = if bd.tile_size != 0.0 { bd.tile_size } else { e };
let bg_w = (r - ir) - (l + il);
@@ -199,14 +162,12 @@ pub fn pieces(frame: Rect, bd: &Backdrop) -> Vec<BackdropPiece> {
});
}
// ── The 8 border pieces (`0x77e8d0` edge rows + `0x77f0c0` UV rows) ────────────────────────
// ── The 8 border pieces ──
if bd.has_border() {
// Run lengths (`0x77f0c0`): the edge strips tile `run` periods between the two corner
// squares (no upper clamp — they TILE, never stretch); clamped to 0 below 2·edgeSize.
// Edge strips tile `run` periods between the corners, 0 below two edges (`0x77f0c0`).
let w_run = ((r - l) / e - 2.0).max(0.0);
let h_run = ((t - b) / e - 2.0).max(0.0);
// Helper: push an axis-aligned edge/corner piece.
let mut edge = |x0: f32, x1: f32, y0: f32, y1: f32, uvs: [[f32; 2]; 4]| {
out.push(BackdropPiece {
corners: aa_corners(x0, x1, y0, y1),
@@ -216,7 +177,7 @@ pub fn pieces(frame: Rect, bd: &Backdrop) -> Vec<BackdropPiece> {
});
};
// LEFT (slice 0): upright, v ∈ [0, h_run].
// LEFT, slice 0, upright.
edge(
l,
l + e,
@@ -224,7 +185,7 @@ pub fn pieces(frame: Rect, bd: &Backdrop) -> Vec<BackdropPiece> {
t - e,
[[0.0, 0.0], [SLICE, 0.0], [SLICE, h_run], [0.0, h_run]],
);
// RIGHT (slice 1): upright.
// RIGHT, slice 1, upright.
edge(
r - e,
r,
@@ -237,8 +198,7 @@ pub fn pieces(frame: Rect, bd: &Backdrop) -> Vec<BackdropPiece> {
[SLICE, h_run],
],
);
// TOP (slice 2): ROTATED — atlas-u→screen-Y (0.25 top, 0.375 bottom), atlas-v→screen-X
// reversed (w_run at the LEFT end, 0 at the RIGHT).
// TOP, slice 2, rotated: atlas `u` runs down the screen, atlas `v` right to left.
edge(
l + e,
r - e,
@@ -251,7 +211,7 @@ pub fn pieces(frame: Rect, bd: &Backdrop) -> Vec<BackdropPiece> {
[3.0 * SLICE, w_run],
],
);
// BOTTOM (slice 3): same rotation as TOP.
// BOTTOM, slice 3, rotated as TOP.
edge(
l + e,
r - e,
@@ -264,7 +224,7 @@ pub fn pieces(frame: Rect, bd: &Backdrop) -> Vec<BackdropPiece> {
[4.0 * SLICE, w_run],
],
);
// Corners (slices 4..7): upright, v ∈ [0,1].
// The corners, slices 4 to 7, upright.
let corner_uv = |i: f32| {
let u0 = i * SLICE;
let u1 = (i + 1.0) * SLICE;
@@ -283,21 +243,16 @@ pub fn pieces(frame: Rect, bd: &Backdrop) -> Vec<BackdropPiece> {
mod tests {
use super::*;
/// The bleed inset moves each BOUNDED axis's edge sample onto a texel CENTRE, and leaves a
/// tiling axis bit-exact. Pinned against the real `ChatBubble-Backdrop` geometry (256×32, eight
/// 32-texel slices) because that is the atlas whose white alpha-0 column put a line through the
/// speech bubble (1402).
/// On the `ChatBubble-Backdrop` geometry: 256×32, eight 32-texel slices.
#[test]
fn the_bleed_inset_lands_on_texel_centres_and_spares_tiling() {
const W: f32 = 256.0;
const H: f32 = 32.0;
let half_u = 0.5 / W;
// A CORNER: u = one slice, v = [0,1]. Both axes are bounded, so both inset.
let corner = [[0.5, 0.0], [0.625, 0.0], [0.625, 1.0], [0.5, 1.0]];
let got = inset_atlas_bleed(corner, W, H);
// In texel coordinates the edges now sit on 128.5 / 159.5 — the centres of the first and
// last texels of the slice, which is exactly what stops bilinear reaching texel 127.
// 128.5 and 159.5 are the centres of the slice's first and last texels.
let us: Vec<f32> = got.iter().map(|c| c[0] * W).collect();
assert!((us[0] - 128.5).abs() < 1e-3, "left u at {} texels", us[0]);
assert!((us[1] - 159.5).abs() < 1e-3, "right u at {} texels", us[1]);
@@ -305,8 +260,6 @@ mod tests {
assert!((vs[0] - 0.5).abs() < 1e-3, "top v at {} texels", vs[0]);
assert!((vs[2] - 31.5).abs() < 1e-3, "bottom v at {} texels", vs[2]);
// A TILING edge strip: u = one slice (bounded → inset), v = [0, 8] (tiling → untouched).
// Touching v would make every period land a fraction short and walk the art along the run.
let strip = [[0.25, 8.0], [0.25, 0.0], [0.375, 0.0], [0.375, 8.0]];
let got = inset_atlas_bleed(strip, W, H);
for (g, s) in got.iter().zip(strip.iter()) {
@@ -315,7 +268,6 @@ mod tests {
assert!((got[0][0] - (0.25 + half_u)).abs() < 1e-6);
assert!((got[2][0] - (0.375 - half_u)).abs() < 1e-6);
// The inset NEVER crosses a slice boundary — the property the whole thing exists for.
for i in 0u8..8 {
let (u0, u1) = (f32::from(i) * SLICE, f32::from(i + 1) * SLICE);
let piece = [[u0, 0.0], [u1, 0.0], [u1, 1.0], [u0, 1.0]];
@@ -328,7 +280,6 @@ mod tests {
}
}
// Degenerate inputs are identities, not panics or inversions.
assert_eq!(inset_atlas_bleed(corner, 0.0, 0.0), corner);
let sliver = [[0.0, 0.0], [0.001, 0.0], [0.001, 1.0], [0.0, 1.0]];
assert_eq!(
@@ -355,62 +306,53 @@ mod tests {
}
}
// The primary piece-geometry check: a 200×100 frame, edgeSize 16, insets 5/5/5/5 (the tooltip's
// shape). Frame y-up rect = [bottom 0, left 0, top 100, right 200].
// The tooltip's shape on a 200×100 frame (`Rect::new` takes bottom, left, top, right).
#[test]
fn piece_geometry_200x100_edge16_inset5() {
let frame = Rect::new(0.0, 0.0, 100.0, 200.0);
let bd = tooltip_backdrop(16.0, 5.0);
let ps = pieces(frame, &bd);
// bg + 8 border pieces.
assert_eq!(ps.len(), 9);
// bg: inset by 5 on every side; with symmetric insets the BR bug is invisible (rect exact).
// Symmetric insets hide the bottom-right quirk.
let bg = ps[0];
assert!(bg.is_bg);
assert_eq!(bg.corners[0], [5.0, 95.0]); // TL (left+5, top-5)
assert_eq!(bg.corners[0], [5.0, 95.0]); // TL
assert_eq!(bg.corners[1], [195.0, 95.0]); // TR
assert_eq!(bg.corners[2], [195.0, 5.0]); // BR (bottom+top_inset = 0+5)
assert_eq!(bg.corners[3], [5.0, 5.0]); // BL (bottom+bottom_inset = 0+5)
// Tiled bg UVs: period = tileSize 16; bg is 190×90 ⇒ 11.875 × 5.625 tiles.
assert_eq!(bg.corners[2], [195.0, 5.0]); // BR
assert_eq!(bg.corners[3], [5.0, 5.0]); // BL
assert_eq!(bg.uvs[0], [0.0, 0.0]);
assert!((bg.uvs[1][0] - 190.0 / 16.0).abs() < 1e-4);
assert!((bg.uvs[2][1] - 90.0 / 16.0).abs() < 1e-4);
// The border pieces are LEFT, RIGHT, TOP, BOTTOM, TL, TR, BL, BR (indices 1..9). The whole
// border sits INSIDE the frame rect, flush, insets untouched.
// Border pieces 1 to 8: LEFT, RIGHT, TOP, BOTTOM, TL, TR, BL, BR, flush inside the frame.
let left = ps[1];
assert!(!left.is_bg && left.tile);
// LEFT edge spans x∈[0,16], y∈[16,84] (b+e .. t-e); corners [TL,TR,BR,BL] y-up.
assert_eq!(
left.corners,
[[0.0, 84.0], [16.0, 84.0], [16.0, 16.0], [0.0, 16.0]]
);
// h_run = 100/16 - 2 = 4.25; LEFT UVs upright, v ∈ [0, 4.25].
// h_run = 100/16 - 2 = 4.25.
assert_eq!(left.uvs[0], [0.0, 0.0]);
assert_eq!(left.uvs[1], [0.125, 0.0]);
assert!((left.uvs[3][1] - 4.25).abs() < 1e-4);
// TOP (index 3): ROTATED. x∈[16,184], y∈[84,100]. w_run = 200/16 - 2 = 10.5.
// TOP, rotated; w_run = 200/16 - 2 = 10.5.
let top = ps[3];
assert_eq!(top.corners[0], [16.0, 100.0]); // TL
assert_eq!(top.corners[2], [184.0, 84.0]); // BR
// atlas-u→screen-Y: top row u=0.25, bottom row u=0.375.
assert_eq!(top.uvs[0][0], 0.25); // TL u
assert_eq!(top.uvs[3][0], 0.375); // BL u
// atlas-v→screen-X reversed: LEFT end v=w_run, RIGHT end v=0.
assert!((top.uvs[0][1] - 10.5).abs() < 1e-4); // TL v = w_run
assert_eq!(top.uvs[1][1], 0.0); // TR v = 0
// BOTTOMRIGHT corner (index 8): slice 7, upright, v∈[0,1], x∈[184,200], y∈[0,16].
// BOTTOMRIGHT, slice 7, upright.
let br = ps[8];
assert_eq!(br.corners[0], [184.0, 16.0]); // TL
assert_eq!(br.uvs[0], [0.875, 0.0]);
assert_eq!(br.uvs[2], [1.0, 1.0]);
}
// The BR-inset quirk (bytes 8b 46 50 @ 0x77e9ae): with top != bottom the bg's
// BOTTOMRIGHT corner rides the TOP inset, not the bottom. Asymmetric insets make it assertable.
#[test]
fn bg_bottomright_rides_top_inset_bug() {
let frame = Rect::new(0.0, 0.0, 100.0, 200.0);
@@ -424,21 +366,16 @@ mod tests {
..tooltip_backdrop(16.0, 0.0)
};
let bg = pieces(frame, &bd)[0];
// BL uses the (correct) bottom inset: y = bottom + 9.
assert_eq!(bg.corners[3], [3.0, 9.0]);
// BR uses the TOP inset (the bug): y = bottom + 7, NOT bottom + 9.
// BR takes the top inset, 7, not the bottom's 9.
assert_eq!(bg.corners[2], [196.0, 7.0]);
// The other three edges are regular: TL/TR ride the top inset (−7), left/right insets applied.
assert_eq!(bg.corners[0], [3.0, 93.0]); // TL (left+3, top-7)
assert_eq!(bg.corners[1], [196.0, 93.0]); // TR (right-4, top-7)
assert_eq!(bg.corners[0], [3.0, 93.0]); // TL
assert_eq!(bg.corners[1], [196.0, 93.0]); // TR
}
// Run clamp at tiny sizes: a frame narrower/shorter than 2·edgeSize clamps both runs to 0 (no
// negative tiling), so the edge strips carry a zero-length run (`0x77f0c0`, clamped to 0 if
// < 0).
#[test]
fn run_clamps_to_zero_below_two_edges() {
// 20×20 frame, edgeSize 16 ⇒ width/16 - 2 = -0.75 → 0.
// 20/16 - 2 = -0.75.
let frame = Rect::new(0.0, 0.0, 20.0, 20.0);
let bd = tooltip_backdrop(16.0, 5.0);
let ps = pieces(frame, &bd);
@@ -448,7 +385,6 @@ mod tests {
assert_eq!(top.uvs[0][1], 0.0, "w_run clamped to 0"); // TL v
}
// No files ⇒ no pieces; bg-only ⇒ 1; border-only ⇒ 8.
#[test]
fn presence_gates_pieces() {
let frame = Rect::new(0.0, 0.0, 100.0, 100.0);