I am porting a working gtk-rs application from gtk3 to gtk4. The following code is a reduced self-contained example to demonstrate the Problem. The code is for gtk3 with the changes required for gtk4 indicated by comments.
use gtk::prelude::*;
use gtk::{ApplicationWindow, DrawingArea};
use std::cell::RefCell;
use std::rc::Rc;
struct Ctx {
text: String,
_drawing_area: gtk::DrawingArea,
}
impl Drop for Ctx {
fn drop(&mut self) {
println!("DROP");
}
}
fn on_activate(app: >k::Application) {
let drawing_area = DrawingArea::builder().build();
let win = ApplicationWindow::builder()
.application(app)
.child(&drawing_area)
.build();
// gtk3:
win.show_all();
// gkt4:
//win.show();
let ctx = Ctx {
text: "Hallo".into(),
_drawing_area: drawing_area.clone(),
};
let shared_context = Rc::new(RefCell::new(ctx));
{
let shared_context = shared_context.clone();
// gtk3:
drawing_area.connect_draw(move |_area, _cairo| {
let ctx = shared_context.borrow();
println!("draw text= \"{}\"", ctx.text);
gtk::Inhibit(false)
});
// gtk4:
//drawing_area.set_draw_func(move |_area, _cairo, _width, _height| {
// let ctx = shared_context.borrow();
// println!("draw text= \"{}\"", ctx.text);
//});
}
}
fn main() {
let app = gtk::Application::builder().build();
app.connect_activate(on_activate);
app.run();
println!("main loop finished");
}
With gtk3, the code works as intended: The Ctx structure is dropped when the window is closed:
Running `target/debug/droptest`
draw text= "Hallo"
DROP
main loop finished
With gtk4, however, the Ctx structure is not dropped:
Running `target/debug/droptest`
draw text= "Hallo"
main loop finished
I see, that there is a circular reference ctx -> drawing_area -> draw_func -> closure -> shared_context -> ctx. I assume that is the reason, that ctx is not dropped with gtk4.
However, I'd expected gtk3 and gtk4 (-rs) to behave identically. Where does the difference come from? Is it documented?
Any suggestions to solve the problem with gtk4? The closure could get a weak reference, but then ctx would be dropped when on_active()
is finished. Is there an elegant way to reference ctx from the ApplicationWindows once? In reality, of course, Ctx contains many widgets and other data and there are multiple callbacks.