We'll continue by adding event handlers to some of the buttons.
First of all, we'll need new use statements:
use gtk::{ ToolButtonExt, WidgetExt, }; use App;
We import ToolButtonExt, which provides methods to be called on ToolButton and App from the main module, because we'll add a new method to this type:
impl App { pub fn connect_toolbar_events(&self) { let window = self.window.clone(); self.toolbar.quit_button.connect_clicked(move |_| { window.destroy(); }); } }
In Rust, it's perfectly valid to declare a method in a module different to where the type was created. Here, we say that clicking the quit button will destroy the window, which will effectively exit the application.
Let's add another event that will toggle the play button image with the pause image:
let play_button = self.toolbar.play_button.clone(); self.toolbar.play_button.connect_clicked(move |_| { if play_button.get_stock_id() == Some(PLAY_STOCK.to_string()) { play_button.set_stock_id(PAUSE_STOCK); } else { play_button.set_stock_id(PLAY_STOCK); } });
This code requires a new constant to be added next to PLAY_STOCK:
const PAUSE_STOCK: &str = "gtk-media-pause";
Let's first look at the body of the closure before looking at the peculiarities of this code. Here, we use a condition to check whether the button is showing the play imageāif it is, we switch to the pause stock item. Otherwise, we switch back to the play icon.
But why do we need to clone the button and use this move keyword before the closure? Let's try the normal way, that is, how you would do that in most programming languages:
self.toolbar.play_button.connect_clicked(|_| { if self.toolbar.play_button.get_stock_id() == Some(PLAY_STOCK.to_string()) { self.toolbar.play_button.set_stock_id(PAUSE_STOCK); } else { self.toolbar.play_button.set_stock_id(PLAY_STOCK); } });
If we do that, we get the following compilation error:
error[E0477]: the type `[closure@src/toolbar.rs:79:50: 85:10 self:&&App]` does not fulfill the required lifetime --> src/toolbar.rs:79:34 | 79 | self.toolbar.play_button.connect_clicked(|_| { | ^^^^^^^^^^^^^^^ | = note: type must satisfy the static lifetime error[E0495]: cannot infer an appropriate lifetime for capture of `self` by closure due to conflicting requirements --> src/toolbar.rs:79:50 | 79 | self.toolbar.play_button.connect_clicked(|_| { | __________________________________________________^ 80 | | if self.toolbar.play_button.get_stock_id() == Some(PLAY_STOCK.to_string()) { 81 | | self.toolbar.play_button.set_stock_id(PAUSE_STOCK); 82 | | } else { 83 | | self.toolbar.play_button.set_stock_id(PLAY_STOCK); 84 | | } 85 | | }); | |_________^
And it continues even further to explain why the lifetime cannot be inferred.
Let's look at the signature of the connect_clicked() method to understand what's going on:
fn connect_clicked<F: Fn(&Self) + 'static>(&self, f: F) -> u64
The Fn(&Self) part means the function requires something that looks like a function that takes a parameter that is a reference to Self (ToolButton in this case). The 'static part is a lifetime annotation.