Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions glib-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,9 +525,12 @@ pub fn closure_local(item: TokenStream) -> TokenStream {
/// }
/// ```
///
/// When using the [`Properties`] macro with enums that derive [`Enum`], the default value must be
/// explicitly set via the `builder` parameter of the `#[property]` attribute. See
/// [here](Properties#supported-types) for details.
/// When using the [`Properties`] macro with enums that derive [`Enum`], the
/// default value can be explicitly set via the `builder` parameter of the
/// `#[property]` attribute. If the enum implements or derives
/// `Default`, you can specify that should be the default value
/// via the `default` parameter. See [here](Properties#supported-types) for
/// details.
///
/// An enum can be registered as a dynamic type by setting the derive macro
/// helper attribute `enum_dynamic`:
Expand Down Expand Up @@ -1333,7 +1336,8 @@ pub fn cstr_bytes(item: TokenStream) -> TokenStream {
/// | `construct` | Specify that the property is construct property. Ensures that the property is always set during construction (if not explicitly then the default value is used). The use of a custom internal setter is supported. | | `#[property(get, construct)]` or `#[property(get, set = set_prop, construct)]` |
/// | `construct_only` | Specify that the property is construct only. This will not generate a public setter and only allow the property to be set during object construction. The use of a custom internal setter is supported. | | `#[property(get, construct_only)]` or `#[property(get, set = set_prop, construct_only)]` |
/// | `builder(<required-params>)[.ident]*` | Used to input required params or add optional Param Spec builder fields | | `#[property(builder(SomeEnum::default()))]`, `#[builder().default_value(1).minimum(0).maximum(5)]`, etc. |
/// | `default` | Sets the `default_value` field of the Param Spec builder | | `#[property(default = 1)]` |
/// | `default` | Sets the param spec builder field to the default value | | `#[property(default)]` |
/// | `default = expr` | Sets the `default_value` field of the Param Spec builder | | `#[property(default = 1)]` |
/// | `<optional-pspec-builder-fields> = expr` | Used to add optional Param Spec builder fields | | `#[property(minimum = 0)` , `#[property(minimum = 0, maximum = 1)]`, etc. |
/// | `<optional-pspec-builder-fields>` | Used to add optional Param Spec builder fields | | `#[property(explicit_notify)]` , `#[property(construct_only)]`, etc. |
///
Expand Down Expand Up @@ -1443,6 +1447,8 @@ pub fn cstr_bytes(item: TokenStream) -> TokenStream {
/// smart_pointer: Rc<RefCell<String>>,
/// #[property(get, set, builder(MyEnum::Val))]
/// my_enum: Cell<MyEnum>,
/// #[property(get, set, default)]
/// my_enum_with_default: Cell<MyEnum>,
/// /// # Getter
/// ///
/// /// Get the value of the property `extra_comments`
Expand Down
32 changes: 29 additions & 3 deletions glib-macros/src/properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ enum PropAttr {

// ident = "literal"
Name(syn::LitStr),

// ident
Default,
}

impl Parse for PropAttr {
Expand Down Expand Up @@ -194,6 +197,7 @@ impl Parse for PropAttr {
),
))
}
"default" => PropAttr::Default,
_ => PropAttr::BuilderField((name, None)),
}
};
Expand All @@ -213,6 +217,7 @@ struct ReceivedAttrs {
name: Option<syn::LitStr>,
builder: Option<(Punctuated<syn::Expr, Token![,]>, TokenStream2)>,
builder_fields: HashMap<syn::Ident, Option<syn::Expr>>,
use_default: bool,
}

impl Parse for ReceivedAttrs {
Expand Down Expand Up @@ -244,6 +249,9 @@ impl ReceivedAttrs {
PropAttr::BuilderField((ident, expr)) => {
self.builder_fields.insert(ident, expr);
}
PropAttr::Default => {
self.use_default = true;
}
}
}
}
Expand All @@ -265,6 +273,7 @@ struct PropDesc {
builder: Option<(Punctuated<syn::Expr, Token![,]>, TokenStream2)>,
builder_fields: HashMap<syn::Ident, Option<syn::Expr>>,
is_construct_only: bool,
use_default: bool,
}

impl PropDesc {
Expand All @@ -286,6 +295,7 @@ impl PropDesc {
name,
builder,
builder_fields,
use_default,
} = attrs;

let is_construct_only = builder_fields.iter().any(|(k, _)| *k == "construct_only");
Expand Down Expand Up @@ -333,6 +343,7 @@ impl PropDesc {
builder,
builder_fields,
is_construct_only,
use_default,
})
}
fn is_overriding(&self) -> bool {
Expand All @@ -343,7 +354,11 @@ impl PropDesc {
fn expand_param_spec(prop: &PropDesc) -> TokenStream2 {
let crate_ident = crate_ident_new();
let PropDesc {
ty, name, builder, ..
ty,
name,
builder,
use_default,
..
} = prop;
let stripped_name = strip_raw_prefix_from_name(name);

Expand Down Expand Up @@ -385,9 +400,20 @@ fn expand_param_spec(prop: &PropDesc) -> TokenStream2 {
let builder_fields = prop.builder_fields.iter().map(|(k, v)| quote!(.#k(#v)));

let span = prop.attrs_span;

// Figure out if we should use the default version or the one that explicitly sets the `Default` value.
let (trait_name, fn_name) = if *use_default {
(
quote!(HasParamSpecDefaulted),
quote!(param_spec_builder_defaulted),
)
} else {
(quote!(HasParamSpec), quote!(param_spec_builder))
};

quote_spanned! {span=>
<<#ty as #crate_ident::property::Property>::Value as #crate_ident::prelude::HasParamSpec>
::param_spec_builder() #builder_call
<<#ty as #crate_ident::property::Property>::Value as #crate_ident::#trait_name>
::#fn_name() #builder_call
#rw_flags
#(#builder_fields)*
.build()
Expand Down
28 changes: 27 additions & 1 deletion glib-macros/tests/properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ mod foo {
pub enum SimpleEnum {
#[default]
One,
Two,
}

#[derive(Default, Clone)]
Expand Down Expand Up @@ -129,10 +130,14 @@ mod foo {
builder_fields_without_builder: RefCell<u32>,
#[property(get, set, builder('c'))]
builder_with_required_param: RefCell<char>,
#[property(get, set, default)]
char_default: RefCell<char>,
#[property(get, set)]
boxed: RefCell<SimpleBoxedString>,
#[property(get, set, builder(SimpleEnum::One))]
#[property(get, set, builder(SimpleEnum::Two))]
fenum: RefCell<SimpleEnum>,
#[property(get, set, default)]
fenum_default: RefCell<SimpleEnum>,
#[property(get, set, nullable)]
object: RefCell<Option<glib::Object>>,
#[property(get, set, nullable)]
Expand Down Expand Up @@ -206,6 +211,8 @@ mod foo {

#[test]
fn props() {
use crate::foo::SimpleEnum;

let myfoo: foo::Foo = glib::object::Object::new();

// Read values
Expand Down Expand Up @@ -274,6 +281,25 @@ fn props() {
"hello".to_string()
);

assert_eq!(
myfoo
.find_property("fenum")
.unwrap()
.default_value()
.get::<SimpleEnum>()
.unwrap(),
SimpleEnum::Two
);
assert_eq!(
myfoo
.find_property("fenum_default")
.unwrap()
.default_value()
.get::<SimpleEnum>()
.unwrap(),
SimpleEnum::One
);

// numeric builder
assert_eq!(
myfoo
Expand Down
29 changes: 29 additions & 0 deletions glib/src/param_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2174,6 +2174,35 @@ pub trait HasParamSpec {
fn param_spec_builder() -> Self::BuilderFn;
}

// unless a custom `default` attribute is specified, the macro will use this trait.
pub trait HasParamSpecDefaulted: HasParamSpec + Default {
type BuilderFnDefaulted;
fn param_spec_builder_defaulted() -> Self::BuilderFnDefaulted;
}

// Manually implement the trait for every Enum
impl<
T: HasParamSpec<ParamSpec = ParamSpecEnum>
+ StaticType
+ FromGlib<i32>
+ IntoGlib<GlibType = i32>
+ Default,
> HasParamSpecDefaulted for T
{
type BuilderFnDefaulted = fn(name: &str) -> ParamSpecEnumBuilder<T>;
fn param_spec_builder_defaulted() -> Self::BuilderFnDefaulted {
|name| Self::ParamSpec::builder(name)
}
}

// Manually implement the trait for chars
impl HasParamSpecDefaulted for char {
type BuilderFnDefaulted = fn(name: &str) -> ParamSpecUnicharBuilder;
fn param_spec_builder_defaulted() -> Self::BuilderFnDefaulted {
|name| Self::ParamSpec::builder(name, Default::default())
}
}

impl<T: crate::value::ToValueOptional + HasParamSpec> HasParamSpec for Option<T> {
type ParamSpec = T::ParamSpec;
type SetValue = T::SetValue;
Expand Down
Loading