Widgets

Widgets are the components that make up a GTK application. GTK offers many-preexisting ones and if those do not fit, you can even create custom ones. There are display widgets, buttons, containers and windows. One kind of widget might be able to contain other widgets, it might present information and it might react to interaction.

The Widget Gallery is useful to find out which widget fits your needs. Let us say we want to add a button to our app. We have quite a bit of choice here, but let us take the simplest one — a Button.

GTK is an object-oriented framework, so all widgets are part of an inheritance tree with GObject at the top. The inheritance tree of a Button looks like this:

GObject
╰── Widget
    ╰── Button

The GTK documentation also tells us that Button implements the interfaces GtkAccessible, GtkActionable, GtkBuildable, GtkConstraintTarget.

Now let us compare that with the corresponding Button struct in gtk-rs. The gtk-rs documentation tells us which methods and traits it implements. We find that these traits either have a corresponding base class or interface in the GTK docs. In the "Hello World" app we wanted to react to a button click. This behavior is specific to a button, so we expect to find a suitable method in the ButtonExt trait. And indeed, ButtonExt includes the method connect_clicked.

Filename: listings/widgets/1/main.rs

use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, Button};

fn main() {
    // Create a new application
    let app = Application::new(Some("org.gtk.example"), Default::default());
    app.connect_activate(build_ui);

    // Run the application
    app.run();
}

fn build_ui(application: &Application) {
    // Create a window
    let window = ApplicationWindow::new(application);

    // Set the window title
    window.set_title(Some("My GTK App"));

    // Create a button
    let button = Button::with_label("Press me!");

    // Set the button margins
    button.set_margin_top(18);
    button.set_margin_bottom(18);
    button.set_margin_start(18);
    button.set_margin_end(18);

    // Connect callback
    button.connect_clicked(move |button| {
        // Set the label to "Hello World!" after the button has been clicked on
        button.set_label("Hello World!");
    });
    // Add button
    window.set_child(Some(&button));
    window.present();
}

Please note that Rust requires bringing traits into scope, before using one of its methods. In our example we did that by adding the following line:

Filename: listings/widgets/1/main.rs

use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, Button};

fn main() {
    // Create a new application
    let app = Application::new(Some("org.gtk.example"), Default::default());
    app.connect_activate(build_ui);

    // Run the application
    app.run();
}

fn build_ui(application: &Application) {
    // Create a window
    let window = ApplicationWindow::new(application);

    // Set the window title
    window.set_title(Some("My GTK App"));

    // Create a button
    let button = Button::with_label("Press me!");

    // Set the button margins
    button.set_margin_top(18);
    button.set_margin_bottom(18);
    button.set_margin_start(18);
    button.set_margin_end(18);

    // Connect callback
    button.connect_clicked(move |button| {
        // Set the label to "Hello World!" after the button has been clicked on
        button.set_label("Hello World!");
    });
    // Add button
    window.set_child(Some(&button));
    window.present();
}

With it, we import all necessary traits for dealing with widgets. You probably want to bring the prelude into scope in most of your source files.

This is also a good moment to mention that all gtk-rs widgets support the builder pattern. This is especially nice for creating widgets where multiple widget characteristics are already known during its creation. We can then make the construction of our button neater, by creating a ButtonBuilder with the Button::builder method.

Filename: listings/widgets/2/main.rs

use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, Button};

fn main() {
    // Create a new application
    let app = Application::new(Some("org.gtk.example"), Default::default());
    app.connect_activate(build_ui);

    // Run the application
    app.run();
}

fn build_ui(application: &Application) {
    // Create a window
    let window = ApplicationWindow::builder()
        .application(application)
        .title("My GTK App")
        .build();

    // Create a button
    let button = Button::builder()
        .label("Press me!")
        .margin_top(12)
        .margin_bottom(12)
        .margin_start(12)
        .margin_end(12)
        .build();

    // Connect callback
    button.connect_clicked(move |button| {
        // Set the label to "Hello World!" after the button has been clicked on
        button.set_label("Hello World!");
    });

    // Add button
    window.set_child(Some(&button));
    window.present();
}