Reputation: 4360
... or, How can I subclass a gtk::Widget?
I have the following dependencies in my Cargo.toml
:
[dependencies]
num = "*"
gtk = "*"
cairo-rs = "*"
gdk = "*"
time = "*"
I want to create my own type of widget (for rendering fractals). I have:
extern crate cairo;
extern crate gtk;
use cairo::Context;
use gtk::signal::Inhibit;
use gtk::signal::WidgetSignals;
use gtk::traits::ContainerTrait;
use gtk::traits::WidgetTrait;
struct MyWidget {
widget: gtk::DrawingArea,
foo: u32,
}
impl MyWidget {
fn new() -> MyWidget {
let result = MyWidget {
widget: gtk::DrawingArea::new().unwrap(),
foo: 17
};
result.widget.connect_draw(move |_w, c| {
// Cannot do: result.redraw(c)
Inhibit(true)
});
result
}
fn modify(&mut self, x: u32) {
self.foo += x;
self.widget.queue_draw();
}
fn redraw(&self, _ : Context) -> Inhibit {
println!("Should redraw for {}", self.foo);
Inhibit(true)
}
}
fn main() {
gtk::init().ok();
let window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
let area = MyWidget::new();
window.add(&area.widget);
window.connect_delete_event(|_, _| {
gtk::main_quit();
Inhibit(true)
});
window.connect_key_release_event(move |_w, _event| {
// Cannot do: area.modify(3);
Inhibit(true)
});
window.connect_button_release_event(move |_w, _event| {
// Cannot do: area.modify(17);
Inhibit(true)
});
window.show_all();
gtk::main();
}
But when redraw
gets called, w is of course the gtk::DrawingArea
and not my FractalWidget
. I have laborated with calling connect_draw
with a closure, but not managed to use result
in it (I have tried Box
ing the result and move
ing it into the lambda, but I'm new to this, so there is probably some way I haven't tried).
So, my actual question: Is there a way to either send more data into a rust-gnome redraw method (and other similar callbacks), or is there a way to extend a widget struct to contain my own data?
Upvotes: 4
Views: 969
Reputation: 4360
Ok, here's some code that actually works, mainly by using Arc<Mutex<MyWidget>>
insead of plain MyWidget
. This still feels rather clumsy, as I need an explicit clone before moving into a closure and locking the mutex inside it, but this is probably good stuff to do (even if there is only one thread for gtk events). Maybe the verbosity can be fixed by a macro ...
extern crate cairo;
extern crate gtk;
use cairo::Context;
use gtk::signal::Inhibit;
use gtk::signal::WidgetSignals;
use gtk::traits::ContainerTrait;
use gtk::traits::WidgetTrait;
use std::sync::{Arc,Mutex};
struct MyWidget {
widget: gtk::DrawingArea,
foo: u32,
}
impl MyWidget {
fn new() -> Arc<Mutex<MyWidget>> {
let result = Arc::new(Mutex::new(MyWidget {
widget: gtk::DrawingArea::new().unwrap(),
foo: 17
}));
let r2 = result.clone();
result.lock().unwrap().widget.connect_draw(move |_w, c| {
r2.lock().unwrap().redraw(c)
});
result
}
fn modify(&mut self, x: u32) {
self.foo += x;
self.widget.queue_draw();
}
fn redraw(&self, _ : Context) -> Inhibit {
println!("Should redraw for {}", self.foo);
Inhibit(true)
}
}
fn main() {
gtk::init().ok();
let window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
let area = MyWidget::new();
window.add(&area.lock().unwrap().widget);
window.connect_delete_event(|_, _| {
gtk::main_quit();
Inhibit(true)
});
let a1 = area.clone();
window.connect_key_release_event(move |_w, _event| {
a1.lock().unwrap().modify(3);
Inhibit(true)
});
let a2 = area.clone();
window.connect_button_release_event(move |_w, _event| {
a2.lock().unwrap().modify(17);
Inhibit(true)
});
window.show_all();
gtk::main();
}
I'll wait at least a few days with marking this answer as correct, in case anyone has a better one.
Upvotes: 3