Skip to content

Commit

Permalink
gtk: Add composite template callback macro
Browse files Browse the repository at this point in the history
  • Loading branch information
jf2048 committed Nov 22, 2021
1 parent 1b37f42 commit 39c97f2
Show file tree
Hide file tree
Showing 6 changed files with 440 additions and 3 deletions.
2 changes: 1 addition & 1 deletion gtk4-macros/src/attribute_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ fn find_attribute_meta(attrs: &[Attribute], attr_name: &str) -> Result<Option<Me
}

// parse a single meta like: ident = "value"
fn parse_attribute(meta: &NestedMeta) -> Result<(String, String)> {
pub fn parse_attribute(meta: &NestedMeta) -> Result<(String, String)> {
let meta = match &meta {
NestedMeta::Meta(m) => m,
_ => bail!("wrong meta type"),
Expand Down
92 changes: 92 additions & 0 deletions gtk4-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
mod attribute_parser;
mod composite_template_derive;
mod template_callbacks_attribute;
mod util;

use proc_macro::TokenStream;
Expand All @@ -17,6 +18,9 @@ use syn::{parse_macro_input, DeriveInput};
/// The `template` attribute specifies where the template should be loaded
/// from; it can be a `file`, a `resource`, or a `string`.
///
/// The `callbacks` attribute specifies this template has bound callbacks.
/// See `template_callbacks` for a description of how to use them.
///
/// The `template_child` attribute is used to mark all internal widgets
/// we need to have programmatic access to.
///
Expand Down Expand Up @@ -90,3 +94,91 @@ pub fn composite_template_derive(input: TokenStream) -> TokenStream {
let gen = composite_template_derive::impl_composite_template(&input);
gen.into()
}

/// Attribute macro for creating template callbacks from Rust methods.
///
/// The `template_callback` attribute is used to mark methods that will be
/// exposed to the template scope. It can take the following options:
/// - `name` which defaults to the function name if not defined
///
/// These methods can then be bound using the `<signal>` or `<closure>` tags
/// in the template file. Note that the arguments and return type will only be
/// checked at run time when the method is invoked.
///
/// Methods can optionally take `&self` as a first parameter. In this case, the
/// attribute `swapped="true"` will usually have to be set on the `<signal>` or
/// `<closure>` tag in order to invoke the function correctly.
///
/// # Example
///
///
/// ```no_run
/// # fn main() {}
/// use gtk::prelude::*;
/// use gtk::glib;
/// use gtk::CompositeTemplate;
/// use gtk::subclass::prelude::*;
///
/// mod imp {
/// use super::*;
///
/// #[derive(Debug, Default, CompositeTemplate)]
/// #[template(file = "test/template_callbacks.ui", callbacks)]
/// pub struct MyWidget {
/// #[template_child]
/// pub label: TemplateChild<gtk::Label>,
/// #[template_child(id = "my_button_id")]
/// pub button: TemplateChild<gtk::Button>,
/// }
///
/// #[glib::object_subclass]
/// impl ObjectSubclass for MyWidget {
/// const NAME: &'static str = "MyWidget";
/// type Type = super::MyWidget;
/// type ParentType = gtk::Box;
///
/// fn class_init(klass: &mut Self::Class) {
/// Self::bind_template(klass);
/// }
///
/// fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
/// obj.init_template();
/// }
/// }
///
/// #[gtk::template_callbacks]
/// impl MyWidget {
/// #[template_callback]
/// fn button_clicked(&self, button: gtk::Button) {
/// button.set_label("I was clicked!");
/// self.label.set_label("The button was clicked!");
/// }
/// }
///
/// impl ObjectImpl for MyWidget {}
/// impl WidgetImpl for MyWidget {}
/// impl BoxImpl for MyWidget {}
/// }
///
/// glib::wrapper! {
/// pub struct MyWidget(ObjectSubclass<imp::MyWidget>) @extends gtk::Widget, gtk::Box;
/// }
///
/// impl MyWidget {
/// pub fn new() -> Self {
/// glib::Object::new(&[]).expect("Failed to create an instance of MyWidget")
/// }
/// }
/// ```
#[proc_macro_attribute]
#[proc_macro_error]
pub fn template_callbacks(attr: TokenStream, item: TokenStream) -> TokenStream {
use proc_macro_error::abort_call_site;
let args = parse_macro_input!(attr as template_callbacks_attribute::Args);
match syn::parse::<syn::ItemImpl>(item) {
Ok(input) => {
template_callbacks_attribute::impl_template_callbacks(input, args).into()
}
Err(_) => abort_call_site!(template_callbacks_attribute::WRONG_PLACE_MSG),
}
}
Loading

0 comments on commit 39c97f2

Please sign in to comment.