breadbox: replace backdrop window with full-screen single window
The backdrop approach failed because a GTK4 window with no child widget gets an empty Wayland input region, so pointer events passed straight through it rather than being delivered to the GestureClick handler. Replace with a single full-screen window (anchored to all 4 edges, transparent background). The launcher UI (vbox) is 700 px wide, halign=Center, valign=Start — visually identical to before. A GestureClick on the window closes it when (x,y) falls outside the vbox's allocation rectangle; clicks inside the vbox reach child widgets normally and don't trigger the close path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
afd1f8f9e2
commit
7df0003c2c
1 changed files with 31 additions and 28 deletions
59
src/main.rs
59
src/main.rs
|
|
@ -328,45 +328,35 @@ fn run_ui(entries: Vec<(String, String)>) {
|
|||
gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION,
|
||||
);
|
||||
|
||||
// Full-screen transparent backdrop at Layer::Top — catches clicks
|
||||
// outside the launcher and closes it. Sits below the launcher
|
||||
// (Layer::Overlay) so it never intercepts clicks on the UI itself.
|
||||
let backdrop = ApplicationWindow::builder()
|
||||
.application(app)
|
||||
.build();
|
||||
backdrop.init_layer_shell();
|
||||
backdrop.set_layer(Layer::Top);
|
||||
backdrop.set_keyboard_mode(KeyboardMode::None);
|
||||
for edge in [Edge::Top, Edge::Bottom, Edge::Left, Edge::Right] {
|
||||
backdrop.set_anchor(edge, true);
|
||||
}
|
||||
|
||||
// Main launcher window
|
||||
// Single full-screen window covers the entire monitor. The window
|
||||
// background is transparent; only the launcher vbox is visible.
|
||||
// Clicks outside the vbox are detected via coordinate check and
|
||||
// close the window. KeyboardMode::Exclusive keeps focus stable so
|
||||
// pointer-leave events never steal it away.
|
||||
let window = ApplicationWindow::builder()
|
||||
.application(app)
|
||||
.default_width(700)
|
||||
.build();
|
||||
window.init_layer_shell();
|
||||
window.set_layer(Layer::Overlay);
|
||||
// Exclusive: compositor won't hand focus to another window on pointer
|
||||
// leave, so the backdrop (not a focus event) handles click-outside.
|
||||
window.set_keyboard_mode(KeyboardMode::Exclusive);
|
||||
window.set_anchor(Edge::Top, true);
|
||||
window.set_exclusive_zone(-1);
|
||||
for edge in [Edge::Top, Edge::Bottom, Edge::Left, Edge::Right] {
|
||||
window.set_anchor(edge, true);
|
||||
}
|
||||
window.set_exclusive_zone(0);
|
||||
|
||||
// Shared close: dismisses both windows and cleans up the PID file.
|
||||
let close_all: Rc<dyn Fn()> = Rc::new({
|
||||
let w = window.clone();
|
||||
let b = backdrop.clone();
|
||||
move || {
|
||||
cleanup_pid();
|
||||
w.close();
|
||||
b.close();
|
||||
}
|
||||
});
|
||||
|
||||
let vbox = GBox::new(Orientation::Vertical, 0);
|
||||
vbox.add_css_class("launcher-bg");
|
||||
vbox.set_halign(gtk4::Align::Center);
|
||||
vbox.set_valign(gtk4::Align::Start);
|
||||
vbox.set_size_request(700, -1);
|
||||
|
||||
let search = SearchEntry::new();
|
||||
search.set_placeholder_text(Some("breadbox"));
|
||||
|
|
@ -504,16 +494,29 @@ fn run_ui(entries: Vec<(String, String)>) {
|
|||
}
|
||||
});
|
||||
|
||||
// Backdrop click: user clicked outside the launcher
|
||||
let close_bd = Rc::clone(&close_all);
|
||||
let click = gtk4::GestureClick::new();
|
||||
click.connect_released(move |_, _, _, _| close_bd());
|
||||
backdrop.add_controller(click);
|
||||
// Close when clicking outside the launcher box.
|
||||
// connect_pressed fires before child widgets handle the click, so
|
||||
// (x, y) are window-relative and always available. Clicks inside the
|
||||
// vbox are within its allocation and are ignored; everything outside
|
||||
// (the transparent full-screen area) dismisses the launcher.
|
||||
let close_outside = Rc::clone(&close_all);
|
||||
let vbox_ref = vbox.clone();
|
||||
let outside_click = gtk4::GestureClick::new();
|
||||
outside_click.connect_pressed(move |_, _, x, y| {
|
||||
let a = vbox_ref.allocation();
|
||||
if x < a.x() as f64
|
||||
|| x > (a.x() + a.width()) as f64
|
||||
|| y < a.y() as f64
|
||||
|| y > (a.y() + a.height()) as f64
|
||||
{
|
||||
close_outside();
|
||||
}
|
||||
});
|
||||
window.add_controller(outside_click);
|
||||
|
||||
// Safety net: clean up PID if the window is destroyed by the compositor
|
||||
window.connect_destroy(|_| cleanup_pid());
|
||||
|
||||
backdrop.present();
|
||||
window.present();
|
||||
search.grab_focus();
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue