gtk-rs-core
gtk-rs-core copied to clipboard
Add derive macro for structs representing GObject properties
Kind of related to https://github.com/gtk-rs/glib/issues/637 and I would do them together in a few days.
I'll write out the whole plan here once I'm at a computer again, but the basic idea would be a derive macro and corresponding infrastructure that
- registers a GObject property per struct field, incl default, min, max values etc
- provides a set/get property function that directly does the translation
- provides a notify function for notifying changes
The struct itself would then be stored inside the instance data in a refcell or mutex.
This should allow to bring down the boilerplate for GObject subclasses with properties considerably.
I've had too many ideas about this, so I guess I might as well comment some here. Don't know what ideas anyone else has for how this should work.
Vala is probably a good model for glib property syntax.
The struct itself would then be stored inside the instance data in a refcell or mutex.
It probably doesn't matter that much, but Cell
also makes sense for small Copy
types (like a lot of properties).
provides a set/get property function that directly does the translation
This complicates things somewhat since these should be methods of the wrapper type. It looks like impl Self::Type
won't work.
Anyway, something like this could be good:
#[derive(glib::ObjectProperties)]
struct Something {
#[prop]
foo: RefCell<String>,
// I guess unlike Vala, something like this isn't really sensible,
// since it will add an unused member to the struct?
// I guess it could use `PhantomData` or something magically removed by the macro.
#[prop(ro, get="self.get_bar")]
bar: Cell<i32>,
}
The default value would be as defined by Default::default
unless something else is specified. min
and max
could likewise default to the minimum and maximum of the type.
Having to manually implement ObjectImpl::{properties, set_property, get_property}
to use this could be somewhat verbose. Maybe an attribute macro for the ObjectImpl
block to add those?
Not sure it's a good idea, but I thought of using an enum, and I recall someone else mentioning that on IRC. So here's something like that:
#[glib::object_properties]
enum FooProperty {
#[prop(rw)]
Bar(i32)
}
impl ObjectImpl for ... {
type Property = FooProperty;
fn set_property(self, obj: &Self::Type, prop: FooProperty) {
match prop {
FooProperty::Bar(val) => ...
}
}
fn get_propety(self, obj: &Self::Type, prop: &mut FooProperty {
match prop {
FooProperty::Bar(ref mut val) => ...
}
}
}
the glib::Value
related traits are refactored now so that you can always get back what you're asking for. In addition there's now a ValueType
trait that is implemented by all 'static
types that can be used in glib::Values
(i.e. everything you could use for storing a GObject property value!).
Another thing that would be missing for automatically generating code for property things now is something to get from a type to its corresponding glib::ParamSpec
and creating an instance of it in a generic way.
One proposal in a PR of mine was
/// A type for which a `ParamSpec` exists and that can be used for object properties.
pub trait UsableAsParam: ValueType {
/// Create a new default `ParamSpec` for this type.
///
/// For types that allow specifiying a default value or valid range, these will be initialized
/// with the equivalent of `None` or `0` and the whole valid range.
fn param_spec(name: &str, nick: &str, blurb: &str, flags: ParamFlags) -> ParamSpec;
}
/// A type for which a `ParamSpec` exists that allows specifying a default value.
pub trait UsableAsParamWithDefault: UsableAsParam {
/// Create a new `ParamSpec` for this type with a default value.
///
/// For types that allow specifying a valid range, these will be initialized with the whole
/// valid range.
fn param_spec_with_default(
name: &str,
nick: &str,
blurb: &str,
// FIXME: Should probably be `Self`
default: &Self,
flags: ParamFlags,
) -> ParamSpec;
}
/// A type for which a `ParamSpec` exists that allows specifying a default value and valid range.
pub trait UsableAsParamWithMinMax: UsableAsParamWithDefault {
/// Create a new `ParamSpec` for this type.
fn param_spec_with_min_max(
name: &str,
nick: &str,
blurb: &str,
min: &Self,
max: &Self,
default: &Self,
flags: ParamFlags,
) -> ParamSpec;
}
This however does not cover a couple of special cases: g_param_spec_gtype()
, g_param_spec_value_array()
, g_param_spec_variant()
which all have a special parameter.
Also it doesn't fit with g_param_spec_flags()
, g_param_spec_enum()
but that's less of a problem as the trait would be directly implemented on the concrete enum/flags type anyway.
I suppose this can be closed now?