notifications: click-through-except-buttons input region + push-down entrance
surface::click_through set a completely empty input region on the toast so every pointer event passed through -- including to the toast's own action buttons and inline-reply entry. Replace it with surface::set_hit_region(window, widgets), which sets the input region to the union of the given widgets' rectangles instead of empty; everywhere else on the surface stays click-through exactly as before. popup.rs recomputes this via refresh_hit_region every time the card set could have changed (shown, dismissed, expired -- routed through the one dismiss() function) and keeps recomputing every frame for HIT_TRACK_MS afterward, since a card's own entrance animation or the stack's push-down reflow can still be moving a button on the frame the change happens. collect_interactive walks the real widget tree for GtkButton/GtkEntry rather than tracking a flat list, so it can't drift out of sync with make_card's structure. build_window's connect_map handles the first-map race the same way surface::click_through used to. Also: spring_in_card grows a newly shown card's height from 0 to its natural size via bread_theme::anim::spring_to (same technique as main.rs's animate_drawer_height), so the existing stack gets pushed down smoothly instead of jumping. KeyboardMode::None is unchanged on the toast; history.rs's OnDemand mode is untouched. Still to do: the actual dismiss button in make_card (this commit wires the mechanism that will hit-test it, but no card has one yet).
This commit is contained in:
parent
ba4e3480d4
commit
3eada87c7d
4 changed files with 216 additions and 40 deletions
|
|
@ -87,27 +87,50 @@ pub fn apply(window: >k4::Window, namespace: &str) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Makes `window` fully click-through: an empty layer-shell input region
|
||||
/// means every pointer event passes to whatever is underneath instead of
|
||||
/// being consumed by this surface. Deliberately opt-in, not part of
|
||||
/// `apply()` — like `exclusive` zone and `keyboard` mode (see the module
|
||||
/// doc comment), this isn't a `[surfaces.*]` schema concept, and most of
|
||||
/// breadbar's satellites (history, the panel, the dismiss-scrim) genuinely
|
||||
/// need real hit-testing. Only a purely-informational surface — today just
|
||||
/// the notification toast — should call this.
|
||||
/// Sets `window`'s input region to the union of `widgets`' current
|
||||
/// allocations, each measured relative to `window` itself (the surface's
|
||||
/// own coordinate space, same as `bar::workspaces::button_geom`'s own
|
||||
/// `compute_bounds` call relative to its Fixed host) — everywhere else on
|
||||
/// the surface stays click-through. An empty (or all-invisible/all-
|
||||
/// unallocated) `widgets` slice is not a special case:
|
||||
/// `Region::create_rectangles(&[])` is already the fully click-through
|
||||
/// empty region — passing `&[]` here is exactly the old blanket
|
||||
/// `Region::create()` this function replaces (see git history around
|
||||
/// `notifications/popup.rs`'s "stop toast popups from stealing focus or
|
||||
/// blocking clicks" fix, and NOTIFICATION INTERACTION #B in the current
|
||||
/// task notes: a toast must never block clicks or steal focus from
|
||||
/// whatever's underneath it EXCEPT on its own buttons — an all-empty
|
||||
/// region made those unreachable too).
|
||||
///
|
||||
/// Must be applied in a `connect_map` handler: the surface (and therefore
|
||||
/// `window.surface()`) doesn't exist until the window is mapped.
|
||||
/// Only meaningful after `window.surface()` exists (i.e. from
|
||||
/// `connect_map` onward — the surface doesn't exist before the window is
|
||||
/// mapped) and after `widgets` have a real allocation — a widget with no
|
||||
/// allocation yet (`compute_bounds` returning `None`) is simply skipped
|
||||
/// rather than contributing a garbage rectangle, so a call made one frame
|
||||
/// too early just yields a smaller-than-intended region for that one frame
|
||||
/// rather than a wrong one.
|
||||
///
|
||||
/// This exists so a future migration of a surface's window-setup code
|
||||
/// (like the one from hand-rolled layer-shell calls to this module) can't
|
||||
/// silently drop a click-through requirement the way `breadbar-notif` did
|
||||
/// once already — see the git history of `notifications/popup.rs` around
|
||||
/// the "stop toast popups from stealing focus or blocking clicks" fix.
|
||||
pub fn click_through(window: >k4::Window) {
|
||||
window.connect_map(|win| {
|
||||
if let Some(surface) = win.surface() {
|
||||
surface.set_input_region(Some(>k4::cairo::Region::create()));
|
||||
}
|
||||
});
|
||||
/// Callers are responsible for RECOMPUTING this every time the hittable
|
||||
/// set could have moved: a widget added or removed, or a layout pass (an
|
||||
/// entrance animation, a push-down reflow) still in flight. A stale region
|
||||
/// either swallows clicks meant for the window below or leaves a real
|
||||
/// button dead.
|
||||
pub fn set_hit_region(window: >k4::Window, widgets: &[gtk4::Widget]) {
|
||||
let Some(surface) = window.surface() else {
|
||||
return;
|
||||
};
|
||||
let rects: Vec<gtk4::cairo::RectangleInt> = widgets
|
||||
.iter()
|
||||
.filter(|w| w.is_visible())
|
||||
.filter_map(|w| {
|
||||
let b = w.compute_bounds(window)?;
|
||||
Some(gtk4::cairo::RectangleInt::new(
|
||||
b.x().floor() as i32,
|
||||
b.y().floor() as i32,
|
||||
b.width().ceil() as i32,
|
||||
b.height().ceil() as i32,
|
||||
))
|
||||
})
|
||||
.collect();
|
||||
surface.set_input_region(Some(>k4::cairo::Region::create_rectangles(&rects)));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue