vgtk 0.3.0

A declarative UI framework for GTK
Documentation
use gobject_sys;
use gtk_sys;

use glib::translate::{from_glib, from_glib_none, mut_override, ToGlib, ToGlibPtr};
use glib::{glib_bool_error, BoolError, ParamFlags, ParamSpec, ToValue};
use gtk::{Container, Widget};

use std::os::raw::c_char;

extern "C" {
    fn gtk_container_class_find_child_property(
        klass: *mut gtk_sys::GtkContainerClass,
        prop: *const c_char,
    ) -> *mut gobject_sys::GParamSpec;
}

pub fn find_child_property<'a, P: Into<&'a str>>(parent: &Container, prop: P) -> Option<ParamSpec> {
    let prop = prop.into();
    unsafe {
        let obj: *const gtk_sys::GtkContainer = parent.to_glib_none().0;
        let klass = (*(obj as *const gobject_sys::GObject))
            .g_type_instance
            .g_class as *mut gobject_sys::GObjectClass;

        from_glib_none(gtk_container_class_find_child_property(
            klass as *mut gtk_sys::GtkContainerClass,
            prop.to_glib_none().0,
        ))
    }
}

pub fn set_child_property<'a, P: Into<&'a str>>(
    parent: &Container,
    child: &Widget,
    prop: P,
    value: &dyn ToValue,
) -> Result<(), BoolError> {
    let prop = prop.into();
    let value = value.to_value();

    let pspec = match find_child_property(parent, prop) {
        Some(pspec) => pspec,
        None => return Err(glib_bool_error!("property not found")),
    };

    if !pspec.get_flags().contains(ParamFlags::WRITABLE)
        || pspec.get_flags().contains(ParamFlags::CONSTRUCT_ONLY)
    {
        return Err(glib_bool_error!("property is not writable"));
    }

    unsafe {
        let valid_type: bool = from_glib(gobject_sys::g_type_check_value_holds(
            value.to_glib_none().0,
            pspec.get_value_type().to_glib(),
        ));
        if !valid_type {
            return Err(glib_bool_error!(
                "property can't be set from the given type"
            ));
        }

        let changed: bool = from_glib(gobject_sys::g_param_value_validate(
            pspec.to_glib_none().0,
            mut_override(value.to_glib_none().0),
        ));
        let change_allowed = pspec.get_flags().contains(ParamFlags::LAX_VALIDATION);
        if changed && !change_allowed {
            return Err(glib_bool_error!(
                "property can't be set from given value, it is invalid or out of range",
            ));
        }

        gtk_sys::gtk_container_child_set_property(
            parent.to_glib_none().0,
            child.to_glib_none().0,
            prop.to_glib_none().0,
            value.to_glib_none().0,
        )
    }

    Ok(())
}