ui: the HIGHLIGHT draw layer is the highlight state, and starts off
A texture an addon made with `CreateTexture(nil, "HIGHLIGHT")` drew all the
time. Only the region `SetHighlightTexture` installs was hover-gated, through
`ButtonState::region_visible`; anything else an addon left in layer 4 was an
ordinary region that nothing ever hid.
In the reference that layer is the highlight *state*, not a place to keep
highlight art, and four things move it:
OnEnter 0x76b6a0 test [frame+0xf8]; push 4; call 0x76a710 EnableDrawLayer
OnLeave 0x76b6f0 test [frame+0xf8]; push 4; call 0x76a730 DisableDrawLayer
Lock 0x76c2e0 writes [frame+0xf8], then enables; on an unlock disables
only when this frame is not the mouse focus ([root+0x7c])
Disable 0x7791bb disables it outright
`0x77983c` settles what the layer means: a Button picks its label's font
instance by reading that layer's own enabled flag at [frame+0x1a8], which is
[frame+0x198] + 4*4, and takes the highlight font object at +0x3b8 when it is
set. So the layer starts off, and every path that can highlight a frame moves
it -- taking whatever the addon put there along.
What this was costing: hand-built column headers and list rows, which are
everywhere. The <Below Average> guild market's Browse page puts
UI-Character-Tab-Highlight in that layer on each of its nine sort headers, so
every header wore a permanent blue glow bar, and its Statistics page showed all
six filter buttons lit at once instead of the two that were selected.
The nameplate glow lives in layer 4 too, and its hover feed drove only the
region's shown flag. Its own OnEnter override chains back into the base notify
(0x7cb8a5 -> 0x76b6a0), so it now moves the layer with it; addons still read
mouseover from glow:IsShown().
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
This commit is contained in:
@@ -13,7 +13,7 @@ use crate::justify::Justify;
|
|||||||
use crate::order::DrawLayer;
|
use crate::order::DrawLayer;
|
||||||
use crate::widget::{
|
use crate::widget::{
|
||||||
ButtonFont, ButtonState, ButtonVisualState, FrameHandle, FrameKind, KindState, RegionHandle,
|
ButtonFont, ButtonState, ButtonVisualState, FrameHandle, FrameKind, KindState, RegionHandle,
|
||||||
RegionKind,
|
RegionKind, HIGHLIGHT_OFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(in crate::script) const REG_BUTTON_METHODS: &str = "__benilla_button_methods";
|
pub(in crate::script) const REG_BUTTON_METHODS: &str = "__benilla_button_methods";
|
||||||
@@ -192,27 +192,70 @@ fn with_button<T>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Draw layer 4, HIGHLIGHT, as a whole -- not only the region `SetHighlightTexture` made.
|
||||||
|
///
|
||||||
|
/// The reference keeps the highlight *state* in that layer's own enabled flag
|
||||||
|
/// (`[frame+0x198] + 4*4`), which is why `0x77983c` reads it to decide whether a Button's label
|
||||||
|
/// takes the highlight font object. Everything that can highlight a frame therefore moves the
|
||||||
|
/// layer, and anything an addon left in it moves with them. See [`HIGHLIGHT_OFF`].
|
||||||
|
pub(super) fn set_highlight_layer(model: &mut Model, h: FrameHandle, on: bool) {
|
||||||
|
if let Some(frame) = model.arena.frame_mut(h) {
|
||||||
|
if on {
|
||||||
|
frame.disabled_layers &= !HIGHLIGHT_OFF;
|
||||||
|
} else {
|
||||||
|
frame.disabled_layers |= HIGHLIGHT_OFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether `LockHighlight()` is holding the layer on for `h`. Only a Button has the method, so
|
||||||
|
/// anything else answers false.
|
||||||
|
fn highlight_locked(model: &Model, h: FrameHandle) -> bool {
|
||||||
|
matches!(
|
||||||
|
model.arena.frame(h).map(|f| &f.kind_state),
|
||||||
|
Some(KindState::Button(bs)) if bs.locked_highlight
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The highlight half of a hover edge: `OnEnter` (`0x76b6a0`) enables the layer and `OnLeave`
|
||||||
|
/// (`0x76b6f0`) disables it, each after `test [frame+0xf8]` skips it while `LockHighlight` holds.
|
||||||
|
/// A disabled Button reaches neither notify (`0x779490`/`0x7794e0` guard on `[+0x328]`), so the
|
||||||
|
/// layer does not move there either -- and it is already off, from `Disable()`.
|
||||||
|
pub(super) fn hover_highlight(model: &mut Model, h: Option<FrameHandle>, entering: bool) {
|
||||||
|
let Some(h) = h else { return };
|
||||||
|
if !hover_notify_runs(model, Some(h)) || highlight_locked(model, h) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
set_highlight_layer(model, h, entering);
|
||||||
|
}
|
||||||
|
|
||||||
/// `Enable()`/`Disable()`. `Disable` (`0x77ffd0`) reaches `SetEnabled` (`0x779160`), which also
|
/// `Enable()`/`Disable()`. `Disable` (`0x77ffd0`) reaches `SetEnabled` (`0x779160`), which also
|
||||||
/// turns draw layer 4, HIGHLIGHT, off (`0x7791bb`) in the array `DisableDrawLayer` writes, so every
|
/// turns draw layer 4, HIGHLIGHT, off (`0x7791bb`) in the array `DisableDrawLayer` writes, so every
|
||||||
/// region in that layer goes dark, not only the HighlightTexture. `Enable()` here turns it back
|
/// region in that layer goes dark, not only the HighlightTexture. `Enable()` restores it only on a
|
||||||
/// on; the reference restores it only on a hovered button, by re-running its `<OnEnter>`
|
/// hovered button, by re-running its `<OnEnter>` (`0x779183`), and never while `LockHighlight`
|
||||||
/// (`0x779183`), and never while `LockHighlight` holds (`[frame+0xf8]`).
|
/// holds (`[frame+0xf8]`) -- a button enabled under the cursor lights up, one enabled anywhere
|
||||||
|
/// else does not.
|
||||||
fn set_enabled(lua: &Lua, this: &Table, on: bool) -> mlua::Result<()> {
|
fn set_enabled(lua: &Lua, this: &Table, on: bool) -> mlua::Result<()> {
|
||||||
let h = frame_handle_of(lua, this)?;
|
let h = frame_handle_of(lua, this)?;
|
||||||
let mut model = lua.app_data_mut::<Model>().expect("model app_data");
|
let mut model = lua.app_data_mut::<Model>().expect("model app_data");
|
||||||
|
let hovered = model.mouseover == Some(h);
|
||||||
let frame = model
|
let frame = model
|
||||||
.arena
|
.arena
|
||||||
.frame_mut(h)
|
.frame_mut(h)
|
||||||
.ok_or_else(|| mlua::Error::runtime("stale frame handle"))?;
|
.ok_or_else(|| mlua::Error::runtime("stale frame handle"))?;
|
||||||
match &mut frame.kind_state {
|
let locked = match &mut frame.kind_state {
|
||||||
KindState::Button(bs) => bs.set_enabled(on),
|
KindState::Button(bs) => {
|
||||||
|
bs.set_enabled(on);
|
||||||
|
bs.locked_highlight
|
||||||
|
}
|
||||||
_ => return Err(mlua::Error::runtime("not a Button")),
|
_ => return Err(mlua::Error::runtime("not a Button")),
|
||||||
};
|
};
|
||||||
let bit = 1u8 << DrawLayer::Highlight.index();
|
|
||||||
if on {
|
if on {
|
||||||
frame.disabled_layers &= !bit;
|
if hovered || locked {
|
||||||
|
frame.disabled_layers &= !HIGHLIGHT_OFF;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
frame.disabled_layers |= bit;
|
frame.disabled_layers |= HIGHLIGHT_OFF;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -796,17 +839,30 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
|||||||
)?,
|
)?,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// LockHighlight (`0x782840`) and UnlockHighlight: the highlight shows regardless of hover.
|
// LockHighlight (`0x782840`) and UnlockHighlight (`0x782920`), both through `0x76c2e0`: the
|
||||||
|
// highlight shows regardless of hover. That function writes `[frame+0xf8]` and then moves draw
|
||||||
|
// layer 4 with it -- enabling it on a lock, and on an unlock disabling it *unless this frame is
|
||||||
|
// the mouse focus* (`cmp [root+0x7c], ecx`), so unlocking under the cursor leaves it lit.
|
||||||
m.set(
|
m.set(
|
||||||
"LockHighlight",
|
"LockHighlight",
|
||||||
lua.create_function(|lua, this: Table| {
|
lua.create_function(|lua, this: Table| {
|
||||||
with_button(lua, &this, |bs| bs.locked_highlight = true)
|
let h = frame_handle_of(lua, &this)?;
|
||||||
|
with_button(lua, &this, |bs| bs.locked_highlight = true)?;
|
||||||
|
let mut model = lua.app_data_mut::<Model>().expect("model app_data");
|
||||||
|
set_highlight_layer(&mut model, h, true);
|
||||||
|
Ok(())
|
||||||
})?,
|
})?,
|
||||||
)?;
|
)?;
|
||||||
m.set(
|
m.set(
|
||||||
"UnlockHighlight",
|
"UnlockHighlight",
|
||||||
lua.create_function(|lua, this: Table| {
|
lua.create_function(|lua, this: Table| {
|
||||||
with_button(lua, &this, |bs| bs.locked_highlight = false)
|
let h = frame_handle_of(lua, &this)?;
|
||||||
|
with_button(lua, &this, |bs| bs.locked_highlight = false)?;
|
||||||
|
let mut model = lua.app_data_mut::<Model>().expect("model app_data");
|
||||||
|
if model.mouseover != Some(h) {
|
||||||
|
set_highlight_layer(&mut model, h, false);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
})?,
|
})?,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|||||||
@@ -138,6 +138,9 @@ pub(super) fn fire_visibility_changes(lua: &Lua, changed: Vec<FrameHandle>) {
|
|||||||
let notified = super::button::hover_notify_runs(&model, hidden_hover);
|
let notified = super::button::hover_notify_runs(&model, hidden_hover);
|
||||||
if let Some(m) = hidden_hover {
|
if let Some(m) = hidden_hover {
|
||||||
model.mouseover = None;
|
model.mouseover = None;
|
||||||
|
// The leave carries its half of the highlight with it: the layer must not stay enabled
|
||||||
|
// on a frame that is no longer under the cursor, or it comes back lit on the next show.
|
||||||
|
super::button::hover_highlight(&mut model, Some(m), false);
|
||||||
if model.drag.as_ref().is_some_and(|d| d.source == m) {
|
if model.drag.as_ref().is_some_and(|d| d.source == m) {
|
||||||
model.drag = None;
|
model.drag = None;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -893,6 +893,11 @@ impl Plate {
|
|||||||
model.region_data.entry(name).or_default().vertex_color = Some(c);
|
model.region_data.entry(name).or_default().vertex_color = Some(c);
|
||||||
// The glow really shows, as addons read mouseover from it; it only paints nothing.
|
// The glow really shows, as addons read mouseover from it; it only paints nothing.
|
||||||
model.region_data.entry(glow).or_default().hidden = !state.hovered;
|
model.region_data.entry(glow).or_default().hidden = !state.hovered;
|
||||||
|
// The glow lives in draw layer 4, HIGHLIGHT, which is off until something highlights
|
||||||
|
// the frame. The plate's own `OnEnter` override chains back into the base notify
|
||||||
|
// (`0x7cb8a5` -> `0x76b6a0`), so the hover that shows the region enables the layer
|
||||||
|
// under it; the region's shown flag alone would leave it dark.
|
||||||
|
super::button::set_highlight_layer(model, frame, state.hovered);
|
||||||
}
|
}
|
||||||
|
|
||||||
if last.as_ref().is_none_or(|l| {
|
if last.as_ref().is_none_or(|l| {
|
||||||
|
|||||||
@@ -237,6 +237,10 @@ impl UiScript {
|
|||||||
// is still the hover: the reference moves `[root+0x7c]` before either notify.
|
// is still the hover: the reference moves `[root+0x7c]` before either notify.
|
||||||
let old_id = old_id.filter(|_| button::hover_notify_runs(&model, old_handle));
|
let old_id = old_id.filter(|_| button::hover_notify_runs(&model, old_handle));
|
||||||
let enter_id = new_id.filter(|_| button::hover_notify_runs(&model, new_handle));
|
let enter_id = new_id.filter(|_| button::hover_notify_runs(&model, new_handle));
|
||||||
|
// Both notifies move draw layer 4, HIGHLIGHT, before the Lua handler runs, so an
|
||||||
|
// `<OnEnter>` that reads the frame already sees the highlighted state.
|
||||||
|
button::hover_highlight(&mut model, old_handle, false);
|
||||||
|
button::hover_highlight(&mut model, new_handle, true);
|
||||||
(old_id, drag_start, enter_id, slider_change, color_change)
|
(old_id, drag_start, enter_id, slider_change, color_change)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -223,6 +223,164 @@ fn disabling_a_button_takes_its_whole_highlight_layer() {
|
|||||||
assert!(s.errors().is_empty(), "{:?}", s.errors());
|
assert!(s.errors().is_empty(), "{:?}", s.errors());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Draw layer 4, HIGHLIGHT, is the highlight *state*, not a place for art that is always on: the
|
||||||
|
/// base hover notifies toggle the whole layer (`0x76b6a0` enables, `0x76b6f0` disables), which is
|
||||||
|
/// why a Button picks its highlight font by reading the layer's own flag (`0x77983c` tests
|
||||||
|
/// `[frame+0x1a8]`). So it starts off, and a region an addon creates in it stays dark until the
|
||||||
|
/// cursor arrives. Hand-built column headers and list rows all over the addon world depend on it:
|
||||||
|
/// EGM's guild market puts `UI-Character-Tab-Highlight` there and never hides it itself.
|
||||||
|
#[test]
|
||||||
|
fn a_highlight_layer_region_is_dark_until_the_cursor_is_on_the_frame() {
|
||||||
|
let mut s = script();
|
||||||
|
s.set_screen_size(800.0, 600.0);
|
||||||
|
s.run(
|
||||||
|
r#"
|
||||||
|
local f = CreateFrame("Frame", "HiLayer")
|
||||||
|
f:SetPoint("BOTTOMLEFT", 0, 0); f:SetWidth(100); f:SetHeight(100)
|
||||||
|
f:EnableMouse(true)
|
||||||
|
local base = f:CreateTexture(nil, "ARTWORK")
|
||||||
|
base:SetAllPoints(f); base:SetTexture("Interface\\Base.blp")
|
||||||
|
local hi = f:CreateTexture(nil, "HIGHLIGHT")
|
||||||
|
hi:SetAllPoints(f); hi:SetTexture("Interface\\Hi.blp")
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
s.resolve();
|
||||||
|
|
||||||
|
let art = |s: &UiScript| -> Vec<String> {
|
||||||
|
s.extract()
|
||||||
|
.iter()
|
||||||
|
.filter(|q| s.quad_owner_name(q.target).as_deref() == Some("HiLayer"))
|
||||||
|
.filter_map(|q| match &q.content {
|
||||||
|
QuadContent::Texture { path: Some(p), .. } => Some(p.clone()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
s.mouse_move(400.0, 400.0);
|
||||||
|
assert_eq!(
|
||||||
|
art(&s),
|
||||||
|
vec!["Interface\\Base.blp".to_string()],
|
||||||
|
"the layer is off at rest, so only the ARTWORK region draws"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
s.eval::<i64>(r#"return HiLayer:IsShown()"#).unwrap(),
|
||||||
|
1,
|
||||||
|
"the frame itself is shown; it is the layer that is off"
|
||||||
|
);
|
||||||
|
|
||||||
|
s.mouse_move(50.0, 50.0);
|
||||||
|
let v = art(&s);
|
||||||
|
assert!(
|
||||||
|
v.contains(&"Interface\\Hi.blp".to_string()),
|
||||||
|
"the cursor on the frame enables the layer: {v:?}"
|
||||||
|
);
|
||||||
|
|
||||||
|
s.mouse_move(400.0, 400.0);
|
||||||
|
assert_eq!(
|
||||||
|
art(&s),
|
||||||
|
vec!["Interface\\Base.blp".to_string()],
|
||||||
|
"and leaving disables it again"
|
||||||
|
);
|
||||||
|
assert!(s.errors().is_empty(), "{:?}", s.errors());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `LockHighlight`/`UnlockHighlight` (`0x782840`/`0x782920`) both reach `0x76c2e0`, which writes
|
||||||
|
/// `[frame+0xf8]` and then moves layer 4 with it -- enabling it on a lock, and on an unlock
|
||||||
|
/// disabling it *unless this frame is the mouse focus* (`cmp [root+0x7c], ecx`). That last branch
|
||||||
|
/// is what keeps a selected trade-skill row lit when the cursor happens to be on it.
|
||||||
|
#[test]
|
||||||
|
fn lock_highlight_drives_the_layer_and_unlocking_under_the_cursor_leaves_it_lit() {
|
||||||
|
let mut s = script();
|
||||||
|
s.set_screen_size(800.0, 600.0);
|
||||||
|
s.run(
|
||||||
|
r#"
|
||||||
|
local b = CreateFrame("Button", "LockBtn")
|
||||||
|
b:SetPoint("BOTTOMLEFT", 0, 0); b:SetWidth(100); b:SetHeight(100)
|
||||||
|
local hi = b:CreateTexture(nil, "HIGHLIGHT")
|
||||||
|
hi:SetAllPoints(b); hi:SetTexture("Interface\\Hi.blp")
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
s.resolve();
|
||||||
|
|
||||||
|
let lit = |s: &UiScript| -> bool {
|
||||||
|
s.extract()
|
||||||
|
.iter()
|
||||||
|
.filter(|q| s.quad_owner_name(q.target).as_deref() == Some("LockBtn"))
|
||||||
|
.any(|q| {
|
||||||
|
matches!(&q.content, QuadContent::Texture { path: Some(p), .. }
|
||||||
|
if p == "Interface\\Hi.blp")
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
s.mouse_move(400.0, 400.0);
|
||||||
|
assert!(!lit(&s), "off at rest");
|
||||||
|
|
||||||
|
s.run("LockBtn:LockHighlight()").unwrap();
|
||||||
|
assert!(lit(&s), "a lock lights it with the cursor elsewhere");
|
||||||
|
|
||||||
|
s.mouse_move(50.0, 50.0);
|
||||||
|
s.run("LockBtn:UnlockHighlight()").unwrap();
|
||||||
|
assert!(
|
||||||
|
lit(&s),
|
||||||
|
"unlocking under the cursor leaves it lit: the hover owns it now"
|
||||||
|
);
|
||||||
|
|
||||||
|
s.mouse_move(400.0, 400.0);
|
||||||
|
assert!(!lit(&s), "and leaving finally takes it");
|
||||||
|
|
||||||
|
s.run("LockBtn:LockHighlight() LockBtn:UnlockHighlight()")
|
||||||
|
.unwrap();
|
||||||
|
assert!(!lit(&s), "unlocking away from the cursor disables it");
|
||||||
|
assert!(s.errors().is_empty(), "{:?}", s.errors());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Enable()` does not light a button on its own: the reference restores the layer only by
|
||||||
|
/// re-running `<OnEnter>` on a hovered button (`0x779183`), so a form that enables its Submit
|
||||||
|
/// button when the fields are filled does not leave it glowing from across the screen.
|
||||||
|
#[test]
|
||||||
|
fn enable_lights_the_highlight_only_under_the_cursor() {
|
||||||
|
let mut s = script();
|
||||||
|
s.set_screen_size(800.0, 600.0);
|
||||||
|
s.run(
|
||||||
|
r#"
|
||||||
|
local b = CreateFrame("Button", "EnBtn")
|
||||||
|
b:SetPoint("BOTTOMLEFT", 0, 0); b:SetWidth(100); b:SetHeight(100)
|
||||||
|
local hi = b:CreateTexture(nil, "HIGHLIGHT")
|
||||||
|
hi:SetAllPoints(b); hi:SetTexture("Interface\\Hi.blp")
|
||||||
|
b:Disable()
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
s.resolve();
|
||||||
|
|
||||||
|
let lit = |s: &UiScript| -> bool {
|
||||||
|
s.extract()
|
||||||
|
.iter()
|
||||||
|
.filter(|q| s.quad_owner_name(q.target).as_deref() == Some("EnBtn"))
|
||||||
|
.any(|q| {
|
||||||
|
matches!(&q.content, QuadContent::Texture { path: Some(p), .. }
|
||||||
|
if p == "Interface\\Hi.blp")
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
s.mouse_move(400.0, 400.0);
|
||||||
|
s.run("EnBtn:Enable()").unwrap();
|
||||||
|
assert!(!lit(&s), "enabled away from the cursor stays dark");
|
||||||
|
|
||||||
|
s.mouse_move(50.0, 50.0);
|
||||||
|
assert!(lit(&s), "and the cursor arriving lights it");
|
||||||
|
|
||||||
|
s.run("EnBtn:Disable() EnBtn:Enable()").unwrap();
|
||||||
|
assert!(
|
||||||
|
lit(&s),
|
||||||
|
"enabled under the cursor comes back lit, as the re-run OnEnter would"
|
||||||
|
);
|
||||||
|
assert!(s.errors().is_empty(), "{:?}", s.errors());
|
||||||
|
}
|
||||||
|
|
||||||
/// `CButton::OnMouseDown` shows the pushed texture when the button is registered for that mouse
|
/// `CButton::OnMouseDown` shows the pushed texture when the button is registered for that mouse
|
||||||
/// button, up or down (`0x77924b`: `[this+0x330] & (m | m << 8)`), whether or not a click fires.
|
/// button, up or down (`0x77924b`: `[this+0x330] & (m | m << 8)`), whether or not a click fires.
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -136,6 +136,20 @@ pub use kinds::{
|
|||||||
|
|
||||||
// ── Frame and Region nodes ───────────────────────────────────────────────────────────────────
|
// ── Frame and Region nodes ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// [`Frame::disabled_layers`] with layer 4, HIGHLIGHT, off: a frame's resting state.
|
||||||
|
///
|
||||||
|
/// In the reference that layer is the highlight state itself, not merely where highlight art
|
||||||
|
/// lives. The base hover notifies toggle the whole layer -- `OnEnter` (`0x76b6a0`) enables it
|
||||||
|
/// (`0x76a710`), `OnLeave` (`0x76b6f0`) disables it (`0x76a730`), both skipped while
|
||||||
|
/// `LockHighlight` holds (`[frame+0xf8]`) -- `LockHighlight` drives it directly (`0x76c2e0`), and
|
||||||
|
/// `SetEnabled(false)` turns it off outright (`0x7791bb`). A Button even picks its label's font
|
||||||
|
/// instance by reading the layer's own flag: `0x77983c` tests `[frame+0x1a8]`, which is
|
||||||
|
/// `[frame+0x198] + 4*4`, and takes the highlight font object at `+0x3b8` when it is set.
|
||||||
|
///
|
||||||
|
/// So it has to start off. An addon writing `CreateTexture(nil, "HIGHLIGHT")` is relying on that
|
||||||
|
/// -- a hand-built column header or list row puts its hover art there and never hides it itself.
|
||||||
|
pub const HIGHLIGHT_OFF: u8 = 1 << DrawLayer::Highlight.index();
|
||||||
|
|
||||||
/// A frame node, the modelled subset of `CSimpleFrame`. The `effective_*` fields and the tree
|
/// A frame node, the modelled subset of `CSimpleFrame`. The `effective_*` fields and the tree
|
||||||
/// links belong to the [`WidgetArena`] mutators: change state through them so it propagates.
|
/// links belong to the [`WidgetArena`] mutators: change state through them so it propagates.
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
@@ -144,6 +158,9 @@ pub struct Frame {
|
|||||||
/// Layers switched off by `DisableDrawLayer` (`0x775680`) and back on by `EnableDrawLayer`
|
/// Layers switched off by `DisableDrawLayer` (`0x775680`) and back on by `EnableDrawLayer`
|
||||||
/// (`0x7755b0`), bit `n` for [`DrawLayer::index`] `n`: a frame-level mask, so each region
|
/// (`0x7755b0`), bit `n` for [`DrawLayer::index`] `n`: a frame-level mask, so each region
|
||||||
/// keeps its own shown state underneath.
|
/// keeps its own shown state underneath.
|
||||||
|
///
|
||||||
|
/// Layer 4, HIGHLIGHT, starts here set -- that layer is the client's highlight *state*, not a
|
||||||
|
/// place to put art that is always on. See [`HIGHLIGHT_OFF`].
|
||||||
pub disabled_layers: u8,
|
pub disabled_layers: u8,
|
||||||
/// The global name, which a later duplicate carries without owning ([`WidgetArena::lookup`]).
|
/// The global name, which a later duplicate carries without owning ([`WidgetArena::lookup`]).
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
@@ -468,7 +485,7 @@ impl WidgetArena {
|
|||||||
let effective_alpha = alpha;
|
let effective_alpha = alpha;
|
||||||
|
|
||||||
let frame = Frame {
|
let frame = Frame {
|
||||||
disabled_layers: 0,
|
disabled_layers: HIGHLIGHT_OFF,
|
||||||
kind,
|
kind,
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
parent,
|
parent,
|
||||||
|
|||||||
Reference in New Issue
Block a user