Skip to main content

gtk3_macros/
composite_template_derive.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use proc_macro2::TokenStream;
4use proc_macro_error::{abort, abort_call_site};
5use quote::quote;
6use syn::Data;
7
8use std::string::ToString;
9
10use crate::attribute_parser::*;
11use crate::util::*;
12
13fn gen_set_template(source: TemplateSource) -> TokenStream {
14    match source {
15        TemplateSource::File(file) => quote! {
16            let t = include_bytes!(#file);
17            klass.set_template(t);
18        },
19        TemplateSource::Resource(resource) => quote! {
20            klass.set_template_from_resource(&#resource);
21        },
22        TemplateSource::String(template) => quote! {
23            klass.set_template(&#template);
24        },
25    }
26}
27
28fn gen_template_child_bindings(fields: &syn::Fields) -> TokenStream {
29    let crate_ident = crate_ident_new();
30    let attributed_fields = match parse_fields(fields) {
31        Ok(fields) => fields,
32        Err(err) => abort!(err.span(), err.to_string()),
33    };
34
35    let recurse = attributed_fields.iter().map(|field| match field.attr.ty {
36        FieldAttributeType::TemplateChild => {
37            let mut value_id = &field.ident.to_string();
38            let ident = &field.ident;
39            field.attr.args.iter().for_each(|arg| match arg {
40                FieldAttributeArg::Id(value) => {
41                    value_id = value;
42                }
43            });
44
45            quote! {
46                klass.bind_template_child_with_offset(
47                    &#value_id,
48                    #crate_ident::offset_of!(Self => #ident),
49                );
50            }
51        }
52    });
53
54    quote! {
55        #(#recurse)*
56    }
57}
58
59pub fn impl_composite_template(input: &syn::DeriveInput) -> TokenStream {
60    let name = &input.ident;
61    let crate_ident = crate_ident_new();
62
63    let source = match parse_template_source(input) {
64        Ok(v) => v,
65        Err(e) => abort_call_site!(
66            "{}: derive(CompositeTemplate) requires #[template(...)] to specify 'file', 'resource', or 'string'",
67            e
68        ),
69    };
70
71    let set_template = gen_set_template(source);
72
73    let fields = match input.data {
74        Data::Struct(ref s) => &s.fields,
75        _ => abort_call_site!("derive(CompositeTemplate) only supports structs"),
76    };
77
78    let template_children = gen_template_child_bindings(fields);
79
80    quote! {
81        impl #crate_ident::subclass::widget::CompositeTemplate for #name {
82            fn bind_template(klass: &mut Self::Class) {
83                #set_template
84
85                unsafe {
86                    #template_children
87                }
88            }
89        }
90    }
91}