Creating a GTK3 Window

It's finally time to gain some experience with GUI programming using GTK from the gtk-rs wrapper project.

Setting the Use Statements

Like the command-line front-end, we are going to need to import article::Article and homepage for getting the homepage and collecting articles. However, now we are going to also import gtk for creating GUI windows and widgets, gtk::traits::* to allow widgets to use all their traits, gdk::ffi::GdkRGBA for creating colors to color our widgets and finally pango for manipulating font styles.

use article::Article;
use homepage;
use gtk;
use gtk::traits::*;
use gdk::ffi::GdkRGBA;
use pango;

Initializing GTK

The absolute first step to take when setting up a GTK GUI is to first initialize GTK. The following function will attempt to initialize GTK, and if it fails, will panic with an error that it failed to initialize.

gtk::init().unwrap_or_else(|_| panic!("Failed to initialize GTK."));

After this step, you will then set up your GUI, but at the very end you must end your main() function with gtk::main(), which will actually start your GUI application.

pub fn launch() {
    gtk::init().unwrap_or_else(|_| panic!("Failed to initialize GTK."));

    gtk::main();
}

Creating the Main Window

Between the init() and main() functions, we will begin setting up all of our GTK widgets and window. We will start by creating a Toplevel Window. The syntax for creating widgets is universally the same. Each widget provides a new() method, only varying in the input variables that it accepts. the gtk::Window type takes a WindowType enum as input to tell the wrapper what type of window to create.

You can think of a Toplevel window as a main window to a program.

let window = gtk::Window::new(gtk::WindowType::Toplevel).unwrap();

Now we need to make some configurations to our newly-created gtk::Window widget. The information that we want is the default_size(), set_title() and connect_delete_event(). The first, default_size(), will set the dimensions of the window upon launch. Then, set_title() will set the title of the window. Finally, connect_delete_event() will define the action to take when the window is closed.

window.set_title("Phoronix Reader");
let (width, height) = (600, 500);
window.default_size(width, height);

The hard part is understanding this next part: connect_delete_event(). All of the connect events take a function or closure as input, which is where the confusion lies in. This is how this event is generally configured:

window.connect_delete_event(|_,_| {
    gtk::main_quit();
    gtk::signal::Inhibit(true)
});

This signifies that it takes no variables as input into the closure, and simply executes gtk::main_quit() and gtk::signal::Inhibit(true), which tells GTK not to interrupt this action.

We can combine all of this together into a new function specific for configuring the window. We will pass the window to the function by reference as that's simply the best practice when working with GTK widgets.

fn configure_window(window: &gtk::Window) {
    window.set_title("Phoronix Reader");
    let (width, height) = (600, 500);
    window.set_default_size(width, height);
    window.connect_delete_event(|_,_| {
        gtk::main_quit();
        gtk::signal::Inhibit(true)
    });
}

Then we can call it in our launch function as such:

configure_window(&window);

To actually show the window after the program starts, you need to use the show_all() method.

window.show_all();

phoronix_gui.rs Review

use article::Article;
use homepage;
use gtk;
use gtk::traits::*;
use gdk::ffi::GdkRGBA;
use pango;

fn configure_window(window: &gtk::Window) {
    window.set_title("Phoronix Reader");
    let (width, height) = (600, 500);
    window.set_default_size(width, height);
    window.connect_delete_event(|_,_| {
        gtk::main_quit();
        gtk::signal::Inhibit(true)
    });
}

pub fn launch() {
    gtk::init().unwrap_or_else(|_| panic!("Failed to initialize GTK."));

    let window = gtk::Window::new(gtk::WindowType::Toplevel).unwrap();
    configure_window(&window);

    window.show_all();

    gtk::main();
}