notifications: re-apply toast click-through after the surface::apply migration

feature/theme-spotlight branched before main's 297207a ('notifications:
stop toast popups from stealing focus or blocking clicks') and the
notification popup was rewritten in this branch to use
surface::apply() for its layer-shell setup, which sets only
anchor/margin/width/layer — no input region — and left the window on
KeyboardMode::OnDemand. Merging this branch would have silently
reintroduced the original bug with no merge conflict to flag it.

Re-apply the fix on top of surface::apply(): add surface::click_through(),
an opt-in helper that sets an empty layer-shell input region on map so
every pointer event passes through to whatever's underneath, and call
it from the toast's create_window(). history.rs's window (the genuinely
interactive notification-history view) is untouched and correctly
keeps OnDemand and normal hit-testing.

Also switch the toast itself from KeyboardMode::OnDemand to
KeyboardMode::None. The toast's card layout does build action buttons
and, when a notification carries an inline-reply hint, a GtkEntry —
but with an empty input region nothing on the toast is ever clickable
or focusable regardless of keyboard mode, so OnDemand only offered a
focus capability with no way to trigger it. Those controls remain
reachable from the history window, which is opened deliberately and
keeps real hit-testing.
This commit is contained in:
Breadway 2026-08-25 16:19:49 +08:00
parent 1d30818510
commit 110ad2c6f9
2 changed files with 40 additions and 3 deletions

View file

@ -172,9 +172,21 @@ fn create_window() -> gtk4::Window {
window.init_layer_shell(); window.init_layer_shell();
window.set_namespace(Some("breadbar-notif")); window.set_namespace(Some("breadbar-notif"));
crate::surface::apply(&window, "breadbar-notif"); crate::surface::apply(&window, "breadbar-notif");
// OnDemand so an inline-reply GtkEntry can take keys without the popup // Toasts are purely informational: they never grab keyboard focus...
// stealing every keystroke the rest of the time. window.set_keyboard_mode(KeyboardMode::None);
window.set_keyboard_mode(KeyboardMode::OnDemand); // ...and click through entirely, via an empty input region — every
// pointer event passes straight to whatever's underneath instead of
// hitting the toast. `make_card` below does build action buttons and,
// when a notification carries INLINE_REPLY_KEY, a reply `GtkEntry` —
// but with no input region reaching the toast at all, neither is ever
// clickable or focusable from here regardless of keyboard mode, so
// OnDemand would grant a focus capability nothing can trigger. Those
// controls are only reachable from the history window (history.rs),
// which is opened deliberately and correctly keeps OnDemand + normal
// hit-testing. If the toast itself grows real click interactivity
// later, this needs to become a real (non-empty) input region sized to
// just the interactive rows, not a blanket revert to OnDemand.
crate::surface::click_through(&window);
crate::theme::bind_auto(&window); crate::theme::bind_auto(&window);
window window
} }

View file

@ -86,3 +86,28 @@ pub fn apply(window: &gtk4::Window, namespace: &str) {
window.set_size_request(px, -1); window.set_size_request(px, -1);
} }
} }
/// 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.
///
/// Must be applied in a `connect_map` handler: the surface (and therefore
/// `window.surface()`) doesn't exist until the window is mapped.
///
/// 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: &gtk4::Window) {
window.connect_map(|win| {
if let Some(surface) = win.surface() {
surface.set_input_region(Some(&gtk4::cairo::Region::create()));
}
});
}