Can't seem to detect when I click off of a popup window

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.

1 Like

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 :slight_smile:

3 Likes

This is the smallest example I could come up with, sorry I didn’t include one to begin with. :smile:

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<_>>());
}
1 Like

Is there a chance that this is a bug with GTK rather than an issue with my code? @sdroege

I don’t know, sorry. This needs someone who actually knows GTK to look into it :slight_smile:

There’s also nothing Rust-specific about this, you should get exactly the same behaviour with the equivalent C code.

1 Like

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 :+1:

See GtkWidget focus-out-event:

To receive this signal, the GdkWindow associated to the widget needs to enable the GDK_FOCUS_CHANGE_MASK mask.

I added this to try and appease the mask requirement, with no luck.
image
Gonna try just subscribing to the mouse gdk::Device next…

Why are you trying to add device events?

If you want to get a focus change event on a GtkWidget, you will need to call gtk_widget_add_events().

1 Like

Must not be implemented in gtk-rs for some reason, I don’t see it listed here. I was just trying the closest thing I could find.

It’s listed on WidgetExtManual.

I have some code using it here if you’re looking for an example.

If you enter add_events in the search box of the docs you’ll find it.

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.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.