VSync joins the video options, the chat dock goes quiet
- VSync is a player setting on the Graphics page now — 1.12's own gxVSync, persisted like every other CVar — instead of a dev-HUD checkbox - the perf HUD collapses to one surface that says everything once, at a deliberate, governed width; the badge stops crying wolf - the idle chat dock stops repainting itself every frame — the fade applier gets the gate the reference carries twice over, and hovering still reveals the hidden tabs - the tick can name who spent it: per-handler self/total attribution at the one seam where every handler fires Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -648,3 +648,71 @@ fn fcf_select_dock_frame_selects_by_frame_and_raises_for_an_undocked_one() {
|
||||
"an undocked window raises — benilla has no dock model for it (0288 §2)"
|
||||
);
|
||||
}
|
||||
|
||||
/// **The idle dock writes nothing** (decision 1396). `FCF_OnUpdate`'s apply block used to run
|
||||
/// unconditionally — ~24 `getglobal`s, two table builds and ~20 `SetAlpha` calls every frame of
|
||||
/// every session, writing the values that were already there, at 36 µs/frame (1395 measured it as
|
||||
/// the largest single handler in the client).
|
||||
///
|
||||
/// The probe is a sentinel the applier can never write: it only ever writes `{0, 0.5, 1.0} × reveal`,
|
||||
/// and `reveal` is pinned at 0 with the cursor away from the dock. If the gate is removed, the very
|
||||
/// next tick overwrites 0.42 with 0 and this goes red.
|
||||
#[test]
|
||||
fn an_idle_dock_stops_rewriting_the_tab_alpha_every_frame() {
|
||||
let mut s = chat_frame();
|
||||
s.mouse_move(1500.0, 850.0); // far from the dock: no hover, so `reveal` stays 0
|
||||
for _ in 0..8 {
|
||||
s.tick(0.016); // let the label measurements land and the applier reach its resting write
|
||||
s.resolve();
|
||||
}
|
||||
|
||||
s.run("ChatFrame1Tab:SetAlpha(0.42)").unwrap();
|
||||
for _ in 0..10 {
|
||||
s.tick(0.016);
|
||||
s.resolve();
|
||||
}
|
||||
assert!(s.errors().is_empty(), "{:?}", s.errors());
|
||||
|
||||
let alpha: f64 = s.eval("return ChatFrame1Tab:GetAlpha()").unwrap();
|
||||
assert!(
|
||||
(alpha - 0.42).abs() < 1e-6,
|
||||
"a settled dock must not rewrite its tab alpha — got {alpha}"
|
||||
);
|
||||
}
|
||||
|
||||
/// The control for the guard above: the gate must not cost the reveal it is gating. A stationary
|
||||
/// cursor over the dock for longer than `CHAT_TAB_SHOW_DELAY` fades the selected tab to full.
|
||||
#[test]
|
||||
fn hovering_the_dock_still_reveals_the_tabs() {
|
||||
let mut s = chat_frame();
|
||||
s.mouse_move(1500.0, 850.0);
|
||||
for _ in 0..4 {
|
||||
s.tick(0.016);
|
||||
s.resolve();
|
||||
}
|
||||
assert!(
|
||||
s.eval::<f64>("return ChatFrame1Tab:GetAlpha()").unwrap() < 1e-6,
|
||||
"the dock starts concealed"
|
||||
);
|
||||
|
||||
// The dock's own centre, asked of the layout rather than hardcoded.
|
||||
let (x, y): (f32, f32) = s
|
||||
.eval(
|
||||
"return (ChatFrame1:GetLeft() + ChatFrame1:GetRight()) / 2, \
|
||||
(ChatFrame1:GetBottom() + ChatFrame1:GetTop()) / 2",
|
||||
)
|
||||
.unwrap();
|
||||
s.mouse_move(x, y);
|
||||
// 0.2 s stationary to arm the reveal, then the 0.15 s ramp — 25 frames covers both with room.
|
||||
for _ in 0..25 {
|
||||
s.tick(0.016);
|
||||
s.resolve();
|
||||
}
|
||||
assert!(s.errors().is_empty(), "{:?}", s.errors());
|
||||
|
||||
let alpha: f64 = s.eval("return ChatFrame1Tab:GetAlpha()").unwrap();
|
||||
assert!(
|
||||
(alpha - 1.0).abs() < 1e-6,
|
||||
"a stationary hover reveals the selected tab at full alpha — got {alpha}"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -218,6 +218,17 @@ impl Default for UiScaleCvar {
|
||||
|
||||
/// The shipped default (the reference's slider spans 0.64..1.0; 1.0 read oversized to the
|
||||
/// director's eye — the taste call this dial exists for).
|
||||
///
|
||||
/// **It also happens to be what the reference itself lands on above ~853 px tall**, which we did
|
||||
/// not know when it was chosen (wow-re `system/ui/scratch/modelframe-uiscale-law.md`, out of the
|
||||
/// autocast-shine thread): `0x492f70` computes `max((H > 768) ? 768/H : 1.0, 0.9)` and is called
|
||||
/// **both** on a mode set and from the `useUiScale`-OFF leg (`0x4908ad`) — and OFF is the shipped
|
||||
/// default (`0x8430c0` = `"0"`). So the reference is 1.0 at 768 and below, 0.96 at 1280×800, and
|
||||
/// **0.9 at 1080p and 1440p**. Our flat 0.9 therefore agrees with it on every modern window and
|
||||
/// diverges only *below* ~853 px tall, where the reference is nearer 1.0. Left alone deliberately:
|
||||
/// this constant is a director taste call, and the divergence is confined to window heights we
|
||||
/// do not ship against. The reference's own law is written here so the next session inherits it
|
||||
/// rather than re-deriving it.
|
||||
pub(crate) const DEFAULT_UI_SCALE: f32 = 0.9;
|
||||
|
||||
/// The app's `uiScale`: `WOW_UI_SCALE=` if set (clamped to the plausible dial range), else
|
||||
|
||||
@@ -822,6 +822,56 @@ fn the_graphics_page_reads_the_cvar_table_on_select() {
|
||||
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
|
||||
}
|
||||
|
||||
/// **Vertical Sync** (1394) — the Graphics page's third row, and the first option in this window
|
||||
/// that reaches the *window* rather than a gameplay or UI knob. It is 1.12's own Video Options
|
||||
/// checkbox 5 (`gxVSync`), which lived on the perf HUD as a dev checkbox until it turned out to be
|
||||
/// unreachable in a player build (the HUD is `#[cfg(feature = "dev")]`) and to be a setting rather
|
||||
/// than an instrument in the first place.
|
||||
///
|
||||
/// The row is deliberately **not** deferred, unlike the UI Scale slider above it: the reference's
|
||||
/// row carries `gxRestart = 1` because its device could not swap the presentation interval live,
|
||||
/// and wgpu can — so the click commits, like every other checkbox here.
|
||||
#[test]
|
||||
fn the_vertical_sync_row_reads_and_writes_the_present_mode_cvar() {
|
||||
let mut s = audio_harness();
|
||||
s.set_cvar_host("gxVSync", "0");
|
||||
let mut s = harness_on(s);
|
||||
s.run("ShowUIPanel(OptionsFrame)").unwrap();
|
||||
s.run("OptionsFrameCategoryListRowGraphics:Click()")
|
||||
.unwrap();
|
||||
|
||||
// Read from the table, not from a restated default: the harness seeded it off.
|
||||
assert!(!s
|
||||
.eval::<bool>("return OptionsFrameContainerBodyGraphicsRowVerticalSyncCheck:GetChecked()")
|
||||
.unwrap());
|
||||
assert_eq!(
|
||||
s.eval::<String>("return OptionsFrameContainerBodyGraphicsRowVerticalSyncLabel:GetText()")
|
||||
.unwrap(),
|
||||
"Vertical Sync"
|
||||
);
|
||||
let _ = s.take_cvar_changes();
|
||||
|
||||
// A click commits immediately — nothing stages it behind Apply.
|
||||
s.run("OptionsFrameContainerBodyGraphicsRowVerticalSyncCheck:Click()")
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
s.take_cvar_changes(),
|
||||
vec![("gxVSync".to_string(), "1".to_string())],
|
||||
"the checkbox writes the cvar on click, not on Apply"
|
||||
);
|
||||
|
||||
// Defaults walks it back to the registered "1" — which `cvars::tests` welds to
|
||||
// `VideoConfig::default()`, and `video::tests` welds in turn to the window's boot mode.
|
||||
s.run("OptionsFrameContainerBodyGraphicsRowVerticalSyncCheck:Click()")
|
||||
.unwrap();
|
||||
let _ = s.take_cvar_changes();
|
||||
s.run("OptionsFrameContainerDefaults:Click()").unwrap();
|
||||
assert!(s
|
||||
.eval::<bool>("return OptionsFrameContainerBodyGraphicsRowVerticalSyncCheck:GetChecked()")
|
||||
.unwrap());
|
||||
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
|
||||
}
|
||||
|
||||
/// The dropdown row (0992, the first): the capsule reads the CVar on select (text from the
|
||||
/// row's own entries), the capsule button opens the shared list at the OWNER's effective scale
|
||||
/// (the kit's 0992 uiScale correction — the window rides SetScale) with the current value's
|
||||
@@ -1647,10 +1697,11 @@ fn every_row_tooltip_key_resolves_in_the_real_global_strings() {
|
||||
);
|
||||
checked += 1;
|
||||
}
|
||||
// 19 CVar rows (Social's two bubble switches are 1139's; Status Bar Text, Mouse Sensitivity
|
||||
// and Max Camera Distance 1140's) + the Combat page's 14 saved-variable rows (1134) + the Interface
|
||||
// page's 4 (3 from 1136, Buff Durations 1139) and the Action Bars page's 1 (1136).
|
||||
assert_eq!(checked, 38, "every row but one carries a live 1.12 key");
|
||||
// 20 CVar rows (Social's two bubble switches are 1139's; Status Bar Text, Mouse Sensitivity
|
||||
// and Max Camera Distance 1140's; Graphics' Vertical Sync is 1394's) + the Combat page's 14
|
||||
// saved-variable rows (1134) + the Interface page's 4 (3 from 1136, Buff Durations 1139) and
|
||||
// the Action Bars page's 1 (1136).
|
||||
assert_eq!(checked, 39, "every row but one carries a live 1.12 key");
|
||||
assert_eq!(
|
||||
untipped,
|
||||
vec!["ControlsRowAutoLoot".to_string()],
|
||||
@@ -1739,10 +1790,10 @@ fn every_flavor_of_row_raises_its_plate_from_the_page_it_lives_on() {
|
||||
s.errors()
|
||||
);
|
||||
}
|
||||
// 19 of the 20 CVar rows (Social's two are 1139's; Status Bar Text, Mouse Sensitivity and
|
||||
// Max Camera Distance 1140's), plus the Combat page's 14 saved-variable rows (1134), the Interface page's 4
|
||||
// (1136, + Buff Durations 1139) and Action Bars' 1 (1136).
|
||||
assert_eq!(raised, 38, "every row but Auto Loot has a 1.12 description");
|
||||
// 20 of the 21 CVar rows (Social's two are 1139's; Status Bar Text, Mouse Sensitivity and
|
||||
// Max Camera Distance 1140's; Vertical Sync 1394's), plus the Combat page's 14 saved-variable
|
||||
// rows (1134), the Interface page's 4 (1136, + Buff Durations 1139) and Action Bars' 1 (1136).
|
||||
assert_eq!(raised, 39, "every row but Auto Loot has a 1.12 description");
|
||||
}
|
||||
|
||||
/// The **Combat page** (decision 1134) — the first rows in this window whose store is a
|
||||
|
||||
Reference in New Issue
Block a user