Heya, I’ve been using GTK-rs to make a custom start menu for my Linux desktop. Its been a smooth process with everything “just working” for the most part. I’m currently trying to make it so that if i click off of the menu, it closes. However, I’ve tried every event I can think of, and none of them are firing when I click on a different window.
Heres how I’m initializing my window:
// In gtk-rs, you set the window type with type_()
// because type is a reserved keyword in Rust.
// ALSO: using a popup window is NECESSARY for what I'm doing,
// because otherwise the window will be manhandled by BSPWM.
let win = gtk::ApplicationWindowBuilder::new()
.application(app)
.type_(gtk::WindowType::Popup)
.title("launcher")
.default_width(400)
// This is changed later to make the window look like its expanding.
.default_height(0)
.decorated(false)
.build();
win.set_keep_above(true);
win.stick();
Heres a list of a few functions I’ve tried:
connect_focus_out_event
connect_grab_broken_event
connect_grab_notify
Either I’m doing something really dumb, or GTK doesn’t fire these events for popup windows, in which case I’m out of luck. If anyone knows a solution, I’d be really appreciative.
Can you provide a small, runnable application that shows the wrong behaviour? That would make it easier to understand the whole context and suggest a solution
This is the smallest example I could come up with, sorry I didn’t include one to begin with.
extern crate gtk;
extern crate gio;
use std::env;
use std::process::{ exit };
use gtk::prelude::*;
use gio::prelude::*;
fn main() {
let app = gtk::Application::new(
Some("oss.k4rakara.example"),
gio::ApplicationFlags::FLAGS_NONE)
.expect("Failed to init GTK.");
app.connect_activate(|app| {
let win = gtk::ApplicationWindowBuilder::new()
.application(app)
.type_(gtk::WindowType::Popup)
.title("Example")
.default_width(100)
.default_height(100)
.decorated(false)
.build();
win.set_keep_above(true);
win.stick();
// Any combo of these doesn't seem to make a difference...
win.grab_focus();
win.grab_add();
// This `connect` event doesn't seem to work...
win.connect_focus_out_event(|_, _| -> gtk::Inhibit {
println!("Window unfocused!");
gtk::Inhibit(false)
});
// ... Or this one ...
win.connect_grab_broken_event(|_, _| -> gtk::Inhibit {
println!("Grab broken!");
gtk::Inhibit(false)
});
// ... Not this one either.
win.connect_grab_notify(|_, grabbed| {
if !grabbed {
println!("Window ungrabbed!");
}
});
{
let btn = gtk::Button::new();
// This `connect` event works just fine.
btn.connect_clicked(|_| {
println!("Button clicked!");
exit(0);
});
{
let lbl = gtk::Label::new(Some("Click to exit"));
btn.add(&lbl);
}
win.add(&btn);
}
win.show_all();
});
app.run(&env::args().collect::<Vec<_>>());
}
Ah, no worries. Might rewrite my example in C and then take a look at the source. I’ll need to make a gitlab account, but if I find a bug, I’ll submit a PR
Well, I tried just using add_events and it didn’t error, but it didn’t get the thing working how I want it. At this point, I think I’m just gonna use some X11 crate to check the global mouse state, for the sake of simplicity.
So, I started reading the topic from your first question, and I finally realised what you want to achieve.
If you want to create a menu, you should use Gtk::Menu. If you want to re-implement a menu from first principles, then you get to re-implement all that GtkMenu does, which includes holding a windowing system grab. Under a grab, all events are delivered to a specific window, even when they happen outside of the window’s surface; once you get a button press event outside the bounds of your menu, you release the grab, and close the menu.
You won’t be able to emulate this functionality with focus-out events or similar, because you’re using what’s called an override-redirect window, and those are not under the remit of the typical window manager behaviour.