rustgtkgtk-rsgtk4

Making a clickable box in GTK4


How can I make a gtk4::Box clickable in gtk_rs?

In GTK3 it seems that using an EventBox was the way to achieve this, however in GTK4:

Stop using GtkEventBox

GtkEventBox is no longer needed and has been removed.

All widgets receive all events.

https://docs.gtk.org/gtk4/migrating-3to4.html#stop-using-gtkeventbox

So it seems that click handlers should now be attached to widgets directly. However I can't find any clear documentation or examples on how to do this.

If someone could give me an example of attaching a click listener to a gtk4::Box it would be much appreciated.


Solution

  • You can do this using gtk::GestureClick like shown in this example.

    Here is a full example (click somewhere inside the window as the box is not actually visible):

    use gtk::prelude::*;
    use gtk::{Application, ApplicationWindow};
    
    fn main() {
        // Create a new application
        let app = Application::builder()
            .application_id("org.gtk-rs.example")
            .build();
    
        // Connect to "activate" signal of `app`
        app.connect_activate(|app| {
            build_ui(app);
        });
    
        // Run the application
        app.run();
    }
    
    fn build_ui(app: &Application) {
        // Create a window and set the title
        let window = ApplicationWindow::builder()
            .application(app)
            .title("My GTK App")
            .build();
    
        // Create a box
        let gtk_box = gtk::builders::BoxBuilder::new()
            .height_request(200)
            .width_request(300)
            .build();
    
        // Assign a click listener
        let gesture = gtk::GestureClick::new();
        gesture.connect_released(|gesture, _, _, _| {
            gesture.set_state(gtk::EventSequenceState::Claimed);
            println!("Box pressed!");
        });
        gtk_box.add_controller(&gesture);
    
        // Add the box
        window.set_child(Some(&gtk_box));
    
        // Present window to the user
        window.present();
    }
    

    A more involved example

    Say you wanted to actually also see the box and maybe provide some user feedback on mouse hover. Then you will need to use CSS rules to achieve that.

    Change your main.rs to:

    use gtk::gdk::Display;
    use gtk::{CssProvider, StyleContext, prelude::*};
    use gtk::{Application, ApplicationWindow};
    
    fn main() {
        // Create a new application
        let app = Application::builder()
            .application_id("org.gtk-rs.example")
            .build();
    
        // Connect to "activate" signal of `app`
        app.connect_activate(|app| {
            // Load a CSS stylesheet from the included bytes.
            let provider = CssProvider::new();
            provider.load_from_data(include_bytes!("style.css"));
    
            // Give the CssProvider to the default screen so the CSS rules are
            // applied to the window.
            StyleContext::add_provider_for_display(
                &Display::default().expect("Error initializing gtk css provider."),
                &provider,
                gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
            );
    
            build_ui(app);
        });
    
        // Run the application
        app.run();
    }
    
    fn build_ui(app: &Application) {
        // Create a window and set the title
        let window = ApplicationWindow::builder()
            .application(app)
            .title("My GTK App")
            .build();
    
        // Create a box
        let gtk_box = gtk::builders::BoxBuilder::new()
            .height_request(200)
            .width_request(300)
            .margin_bottom(20)
            .margin_end(20)
            .margin_start(20)
            .margin_top(20)
            .css_classes(vec![String::from("hover-box")])
            .build();
    
        // Assign a click listener
        let gesture = gtk::GestureClick::new();
        gesture.connect_released(|gesture, _, _, _| {
            gesture.set_state(gtk::EventSequenceState::Claimed);
            println!("Box pressed!");
        });
        gtk_box.add_controller(&gesture);
    
        // Add the box
        window.set_child(Some(&gtk_box));
    
        // Present window to the user
        window.present();
    }
    

    and create a style.css file in the src/ directory (the same directory where the main.rs is located) with this content:

    .hover-box {
        background-color: blue;
        border-radius: 5px;
    }
    
    .hover-box:hover {
        background-color: rgb(11, 133, 240);
        border-radius: 5px;
    }
    

    Now you get a gtk4::Box with a 5px border radius and blue background color that changes when you hover the mouse above the box.