rustgtk-rs

How to store Rust object without Default in GObject


I am basically following the gtk-rs book to create a simple app. I have the following rust struct, which I want to display in a ListView. This struct is basically read-only and should not be edited, so I do not want to expose it's members as properties and I also do not want to create it from GTK itself (it's created inside the rust-only business logic). I would like to expose the entire struct as one property.

pub struct Video {
    pub number: u32,
    pub encoding: Encoding,
    pub chapters: Vec<Chapter>,
}

pub struct Chapter {
    pub number: u32,
    pub filename: String,
    pub thumbnail_filename: Option<String>,
    pub preview_video_filename: Option<String>,
}

How can I wrap this inside a GObject? I'm trying to create the following wrapper:

mod imp {
    #[derive(Default)]
    pub struct VideoObject {
        pub data: Rc<RefCell<Video>>,
    }

    #[glib::object_subclass]
    impl ObjectSubclass for VideoObject {
        const NAME: &'static str = "VideoObject";
        type Type = super::VideoObject;
    }
    impl ObjectImpl for VideoObject {}

}

pub const VIDEO_PROPERTY: &str = "video";

glib::wrapper! {
    pub struct VideoObject(ObjectSubclass<imp::VideoObject>);
}

impl VideoObject {
    pub fn new(video: Video) -> Self {
        Object::new(&[]).expect("Could not create VideoObject")
    }
}

This fails because Video does not implement Default. I thought about wrapping it inside an Option like data: Rc<RefCell<Option<Video>>>, which compiles. Unfortunately, then I'm stuck on how to set it as a property.

I guess one way would be to use a ParamSpecPointer, box and leak Video and then pass it before, but this strucks me as unsafe and ugly...

Is there a better way to do it?


Solution

  • You can directly access the "imp" object and set fields manually. No need to transform everything into GObject properties:

    impl VideoObject {
        pub fn new(video: Video) -> Self {
            let object = Object::new(&[]).unwrap();
            let imp = imp::VideoObject::from_instance(&object);
            imp.data.replace(Some(video));
            object
        }
    }