use proc_macro::TokenStream;
use quote::quote;
use syn::{
parse::{Parse, ParseStream},
parse_macro_input,
token::Comma,
Attribute, Fields, Ident, Path, Result,
};
struct ComponentArgs {
props: Ident,
state: Ident,
}
impl Parse for ComponentArgs {
fn parse(input: ParseStream) -> Result<Self> {
let props: Ident = input.parse()?;
input.parse::<Comma>()?;
let state: Ident = input.parse()?;
Ok(ComponentArgs { state, props })
}
}
#[proc_macro_attribute]
pub fn component(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as ComponentArgs);
let input = parse_macro_input!(input as syn::ItemStruct);
let vis = &input.vis;
let ident = &input.ident;
let fields = &input.fields;
let fields_list = match fields {
Fields::Named(fields) => fields.named.clone(),
Fields::Unnamed(_) => Default::default(),
Fields::Unit => Default::default(),
};
let props = args.props;
let state = args.state;
let gen = quote! {
#vis struct #ident {
root: grx::Component,
props: #props,
annotations: grx::annotations::Annotations,
state: std::cell::RefCell<#state>,
__state_update_handlers: std::rc::Rc<std::cell::RefCell<Vec< (Box<dyn Fn(&#state, &#state) -> bool>, Box<dyn Fn(&#state)>) >>>,
__store_selectors: std::cell::RefCell<Vec<std::rc::Rc<dyn Fn()>>>,
#fields_list
}
impl #ident {
pub fn set_state(&self, setter: impl Fn(&mut #state) + 'static) {
let old_state = self.state.borrow().clone();
setter(&mut self.state.borrow_mut());
self.__state_update_handlers.borrow().iter().for_each(move |(selector, handler)| {
let new_state = &*self.state.borrow();
if selector(&old_state, new_state) {
handler(new_state);
}
});
}
pub fn on_state(&self, handler: impl Fn(&#state) + 'static) {
let handlers = &mut *self.__state_update_handlers.borrow_mut();
handler(&self.state.borrow());
handlers.push((Box::new(|_,_| true), Box::new(handler)));
}
pub fn select_state(&self, selector: impl Fn(&#state, &#state) -> bool + 'static, handler: impl Fn(&#state) + 'static) {
let handlers = &mut *self.__state_update_handlers.borrow_mut();
handler(&self.state.borrow());
handlers.push((Box::new(selector), Box::new(handler)));
}
pub fn later_drop(self: &std::rc::Rc<Self>, fun: Result<std::rc::Rc<dyn Fn()>, ()>) {
if let Ok(fun) = fun {
self.__store_selectors.borrow_mut().push(fun);
}
}
pub fn drop_selectors_from_store(&self) {
for dropper in self.__store_selectors.borrow().iter() {
dropper();
}
}
}
impl #ident {
fn new(props: #props, component: grx::Component) -> std::rc::Rc<Self> {
let c = std::rc::Rc::new(Self {
root: component,
props,
annotations: Default::default(),
state: Default::default(),
__state_update_handlers: Default::default(),
__store_selectors: Default::default(),
});
grx::props::apply(c.clone());
c
}
}
impl grx::ComponentExt for #ident {
fn visible(self: &Self) -> bool {
self.root.visible()
}
fn height(self: &Self) -> i32 {
self.root.height()
}
fn width(self: &Self) -> i32 {
self.root.width()
}
fn set_visible(self: &Self, visible: bool) {
self.root.set_visible(visible)
}
fn add_class(self: &Self, class: &str) {
self.root.add_class(class)
}
fn classes<'a>(self: &Self) -> Vec<String> {
self.root.classes()
}
fn remove_class(self: &Self, class: &str) {
self.root.remove_class(class)
}
fn set_styles(self: &Self, styles: Vec<grx::Style>) {
self.root.set_styles(styles)
}
fn inner(self: &Self) -> std::rc::Rc<dyn std::any::Any> {
self.root.inner()
}
fn props(self: &Self) -> &dyn props::ExtendingProps {
self.root.props()
}
fn annotations<'a>(self: &'a Self) -> std::cell::Ref<'a, grx::annotations::Annotations> {
self.root.annotations()
}
fn annotations_mut<'a>(self: &'a Self) -> std::cell::RefMut<'a, grx::annotations::Annotations> {
self.root.annotations_mut()
}
fn try_annotations_mut<'a>(self: &'a Self) -> Result<std::cell::RefMut<'a, grx::annotations::Annotations>, std::cell::BorrowMutError> {
self.root.try_annotations_mut()
}
fn children(self: &Self) -> std::cell::Ref<Vec<crate::grx::Component>> {
self.root.children()
}
fn children_mut(self: &Self) -> std::cell::RefMut<Vec<crate::grx::Component>> {
self.root.children_mut()
}
fn into_any(self: std::rc::Rc<Self>) -> std::rc::Rc<dyn std::any::Any> {
self
}
}
};
TokenStream::from(gen)
}
#[proc_macro_attribute]
pub fn props(_args: TokenStream, input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as syn::ItemStruct);
let vis = &input.vis;
let ident = &input.ident;
let attrs: Vec<Attribute> = input
.attrs
.into_iter()
.filter(|a| {
if let Some(ident) = a.path().get_ident() {
ident.to_string() != "props"
} else {
true
}
})
.collect();
let fields = &input.fields;
let fields_list = match fields {
Fields::Named(fields) => fields.named.clone(),
Fields::Unnamed(_) => Default::default(),
Fields::Unit => Default::default(),
};
let gen = quote! {
#(#attrs)*
#vis struct #ident {
pub id: String,
pub classes: String,
pub styles: Vec<crate::grx::Style>,
pub children: Vec<crate::grx::Component>,
#fields_list
}
impl crate::grx::props::ExtendingProps for #ident {
fn id(&self) -> Option<&str> {
if self.id.len() == 0 {
None
} else {
Some(&self.id)
}
}
fn styles(&self) -> Option<&Vec<crate::grx::Style>> {
if self.styles.len() == 0 {
None
} else {
Some(&self.styles)
}
}
fn classes(&self) -> Option<&str> {
if self.classes.len() == 0 {
None
} else {
Some(&self.classes)
}
}
}
impl crate::grx::props::ExtendingProps for &#ident {
fn id(&self) -> Option<&str> {
if self.id.len() == 0 {
None
} else {
Some(&self.id)
}
}
fn styles(&self) -> Option<&Vec<crate::grx::Style>> {
if self.styles.len() == 0 {
None
} else {
Some(&self.styles)
}
}
fn classes(&self) -> Option<&str> {
if self.classes.len() == 0 {
None
} else {
Some(&self.classes)
}
}
}
};
TokenStream::from(gen)
}
struct GtkComponentArgs {
path: Path,
}
impl Parse for GtkComponentArgs {
fn parse(input: ParseStream) -> Result<Self> {
let path: Path = input.parse()?;
Ok(GtkComponentArgs { path })
}
}
#[proc_macro_attribute]
pub fn gtk_component(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as GtkComponentArgs);
let input = parse_macro_input!(input as syn::ItemStruct);
let vis = &input.vis;
let ident = &input.ident;
let attrs: Vec<Attribute> = input
.attrs
.into_iter()
.filter(|a| {
if let Some(ident) = a.path().get_ident() {
ident.to_string() != "props"
} else {
true
}
})
.collect();
let fields = &input.fields;
let fields_list = match fields {
Fields::Named(fields) => fields.named.clone(),
Fields::Unnamed(_) => Default::default(),
Fields::Unit => Default::default(),
};
let wid = args.path;
let gen = quote! {
#(#attrs)*
#vis struct #ident {
widget: #wid,
pub props: Props,
children: std::cell::RefCell<Vec<crate::grx::Component>>,
annotations: std::cell::RefCell<crate::annotations::Annotations>,
#fields_list
}
impl crate::components::ComponentExt for #ident {
fn visible(self: &Self) -> bool {
gtk::prelude::WidgetExt::is_visible(&self.widget)
}
fn height(self: &Self) -> i32 {
gtk::prelude::WidgetExt::allocation(&self.widget).height()
}
fn width(self: &Self) -> i32 {
gtk::prelude::WidgetExt::allocation(&self.widget).width()
}
fn set_visible(self: &Self, visible: bool) {
gtk::prelude::WidgetExt::set_visible(&self.widget, visible);
}
fn add_class(self: &Self, class: &str) {
gtk::prelude::WidgetExt::add_css_class(&self.widget, class);
}
fn classes(self: &Self) -> Vec<String> {
gtk::prelude::WidgetExt::css_classes(&self.widget)
.into_iter()
.map(|s| s.to_string())
.collect()
}
fn set_styles(self: &Self, styles: Vec<crate::styles::Style>) {
for style in styles.iter() {
crate::styles::Stylable::apply(style, self.widget.upcast_ref());
}
}
fn remove_class(self: &Self, class: &str) {
gtk::prelude::WidgetExt::remove_css_class(&self.widget, class);
}
fn inner(self: &Self) -> std::rc::Rc<dyn std::any::Any> {
let w: >k::Widget = self.widget.upcast_ref();
Rc::new(w.clone())
}
fn props(&self) -> &dyn crate::props::ExtendingProps {
&self.props
}
fn annotations<'a>(&'a self) -> std::cell::Ref<'a, crate::annotations::Annotations> {
self.annotations.borrow()
}
fn annotations_mut<'a>(&'a self) -> std::cell::RefMut<'a, crate::annotations::Annotations> {
use std::borrow::BorrowMut;
self.annotations.borrow_mut()
}
fn try_annotations_mut<'a>(&'a self) -> Result<std::cell::RefMut<'a, crate::annotations::Annotations>, std::cell::BorrowMutError> {
use std::borrow::BorrowMut;
self.annotations.try_borrow_mut()
}
fn children(self: &Self) -> std::cell::Ref<Vec<crate::grx::Component>> {
self.children.borrow()
}
fn children_mut(self: &Self) -> std::cell::RefMut<Vec<crate::grx::Component>> {
self.children.borrow_mut()
}
fn into_any(self: std::rc::Rc<Self>) -> std::rc::Rc<dyn std::any::Any> {
self
}
}
};
TokenStream::from(gen)
}