Upcast and downcast

Since there is an inheritance system in Gtk, it’s only logical to have one as well in Gtk-rs. Normally, most people won’t need this, but it’s always nice to understand how things work.

Upcasting

This is actually quite simple:

let button = gtk::Button::new_with_label("Click me!");
let widget = button.upcast::<gtk::Widget>();

Since the Button struct implements IsA<Widget>, we can upcast into a Widget. It’s important to note that the IsA trait is implemented on every widget for every of its parents, which, in here, allows to upcast the Button into a Widget.

Let’s see now a more global usage.

Checking if a widget is another widget

Let’s say you want to write a generic function and want to check if a Widget is actually a Box. First, let’s see a bit of code:

fn is_a_box<W: IsA<gtk::Object> + IsA<gtk::Widget> + Clone>(widget: &W) -> bool {
    widget.clone().upcast::<gtk::Widget>().is::<gtk::Box>()
}

Ok, so what’s happening in there? First, you’ll note the usage of the IsA trait. The received widget needs to implement both IsA<Widget> and IsA<Object>.

We need the Object to be able to use the Cast trait which contains both upcast and downcast methods (take a look to it for other methods as well).

We don’t really need our widget to be a Widget, we just put it here to make things easier (so we can upcast into a Widget without troubles).

So the point of this function is to upcast the widget to the highest widget type and then try downcasting it into the wanted object. We could make even more generic like this:

fn is_a<W: IsA<gtk::Object> + IsA<gtk::Widget> + Clone,
        T: IsA<gtk::Object> + IsA<gtk::Widget>>(widget: &W) -> bool {
    widget.clone().upcast::<gtk::Widget>().downcast::<T>().is_ok()
}

Then let’s test it:

let button = gtk::Button::new_with_label("Click me!");

assert_eq!(is_a::<_, gtk::Container>(&button), true);
assert_eq!(is_a::<_, gtk::Label>(&button), false);