Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get my own data to a GTK callback when using rust-gnome?

... 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 Boxing the result and moveing 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?

like image 431
Rasmus Kaj Avatar asked Oct 19 '25 05:10

Rasmus Kaj


1 Answers

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.

like image 194
Rasmus Kaj Avatar answered Oct 22 '25 05:10

Rasmus Kaj



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!