iced_layershell_macros/
lib.rs

1use darling::{
2    FromDeriveInput, FromMeta,
3    ast::{Data, NestedMeta},
4    util::{Flag, Ignored},
5};
6use proc_macro2::TokenStream as TokenStream2;
7use syn::{DeriveInput, Generics, Ident, Variant, Visibility};
8
9use quote::quote;
10
11/// to_layer_message is to convert a normal enum to the enum usable in iced_layershell
12/// It impl the try_into trait for the enum and make it can be convert to the actions in
13/// layershell.
14///
15/// It will automatic add the fields which match the actions in iced_layershell
16#[manyhow::manyhow]
17#[proc_macro_attribute]
18pub fn to_layer_message(attr: TokenStream2, input: TokenStream2) -> manyhow::Result<TokenStream2> {
19    let meta = NestedMeta::parse_meta_list(attr)?;
20
21    let ToLayerMessageAttr { multi } = ToLayerMessageAttr::from_list(&meta)?;
22
23    let is_multi = multi.is_present();
24
25    let derive_input = syn::parse2::<DeriveInput>(input)?;
26    let attrs = &derive_input.attrs;
27    let MessageEnum {
28        vis,
29        ident,
30        generics,
31        data,
32    } = MessageEnum::from_derive_input(&derive_input)?;
33
34    let (impl_gen, ty_gen, where_gen) = generics.split_for_impl();
35    let variants = data.take_enum().unwrap();
36
37    let (additional_variants, try_into_impl) = match is_multi {
38        true => {
39            let additional_variants = quote! {
40                AnchorChange{id: iced::window::Id, anchor: iced_layershell::reexport::Anchor},
41                SetInputRegion{ id: iced::window::Id, callback: iced_layershell::actions::ActionCallback },
42                AnchorSizeChange{id: iced::window::Id, anchor:iced_layershell::reexport::Anchor, size: (u32, u32)},
43                LayerChange{id: iced::window::Id, layer:iced_layershell::reexport::Layer},
44                MarginChange{id: iced::window::Id, margin: (i32, i32, i32, i32)},
45                SizeChange{id: iced::window::Id, size: (u32, u32)},
46                VirtualKeyboardPressed {
47                    time: u32,
48                    key: u32,
49                },
50                NewLayerShell { settings: iced_layershell::reexport::NewLayerShellSettings, id: iced::window::Id },
51                NewPopUp { settings: iced_layershell::actions::IcedNewPopupSettings, id: iced::window::Id },
52                NewMenu { settings: iced_layershell::actions::IcedNewMenuSettings, id: iced::window::Id },
53                RemoveWindow(iced::window::Id),
54                ForgetLastOutput,
55            };
56            let try_into_impl = quote! {
57                impl #impl_gen TryInto<iced_layershell::actions::LayershellCustomActionsWithId> for #ident #ty_gen #where_gen {
58                    type Error = Self;
59
60                    fn try_into(self) -> Result<iced_layershell::actions::LayershellCustomActionsWithId, Self::Error> {
61                        use iced_layershell::actions::LayershellCustomActions;
62                        use iced_layershell::actions::LayershellCustomActionsWithId;
63
64                        match self {
65                            Self::SetInputRegion{ id, callback } => Ok(LayershellCustomActionsWithId::new(Some(id), LayershellCustomActions::SetInputRegion(callback))),
66                            Self::AnchorChange { id, anchor } => Ok(LayershellCustomActionsWithId::new(Some(id), LayershellCustomActions::AnchorChange(anchor))),
67                            Self::AnchorSizeChange { id, anchor, size } => Ok(LayershellCustomActionsWithId::new(Some(id), LayershellCustomActions::AnchorSizeChange(anchor, size))),
68                            Self::LayerChange { id, layer } => Ok(LayershellCustomActionsWithId::new(Some(id), LayershellCustomActions::LayerChange(layer))),
69                            Self::MarginChange { id, margin } => Ok(LayershellCustomActionsWithId::new(Some(id), LayershellCustomActions::MarginChange(margin))),
70                            Self::SizeChange { id, size } => Ok(LayershellCustomActionsWithId::new(Some(id), LayershellCustomActions::SizeChange(size))),
71                            Self::VirtualKeyboardPressed { time, key } => Ok(LayershellCustomActionsWithId::new(
72                                None,
73                                LayershellCustomActions::VirtualKeyboardPressed { time, key })
74                            ),
75                            Self::NewLayerShell {settings, id } => Ok(LayershellCustomActionsWithId::new(None, LayershellCustomActions::NewLayerShell { settings, id })),
76                            Self::NewPopUp { settings, id } => Ok(LayershellCustomActionsWithId::new(None, LayershellCustomActions::NewPopUp { settings, id })),
77                            Self::NewMenu { settings, id } =>  Ok(LayershellCustomActionsWithId::new(None, LayershellCustomActions::NewMenu {settings, id })),
78                            Self::RemoveWindow(id) => Ok(LayershellCustomActionsWithId::new(None, LayershellCustomActions::RemoveWindow(id))),
79                            Self::ForgetLastOutput => Ok(LayershellCustomActionsWithId::new(None, LayershellCustomActions::ForgetLastOutput)),
80                            _ => Err(self)
81                        }
82                    }
83                }
84                impl #ident #ty_gen #where_gen {
85                    fn layershell_open(settings: iced_layershell::reexport::NewLayerShellSettings) -> (iced::window::Id, Self) {
86                        let id = iced::window::Id::unique();
87                        (
88                            id,
89                            Self::NewLayerShell { settings, id }
90                        )
91
92                    }
93                    fn popup_open(settings: iced_layershell::actions::IcedNewPopupSettings) -> (iced::window::Id, Self) {
94                        let id = iced::window::Id::unique();
95                        (
96                            id,
97                            Self::NewPopUp { settings, id }
98                        )
99
100                    }
101                    fn menu_open(settings: iced_layershell::actions::IcedNewMenuSettings) -> (iced::window::Id, Self) {
102                        let id = iced::window::Id::unique();
103                        (
104                            id,
105                            Self::NewMenu { settings, id }
106                        )
107
108                    }
109                }
110            };
111            (additional_variants, try_into_impl)
112        }
113        false => {
114            let additional_variants = quote! {
115                AnchorChange(iced_layershell::reexport::Anchor),
116                SetInputRegion(iced_layershell::actions::ActionCallback),
117                AnchorSizeChange(iced_layershell::reexport::Anchor, (u32, u32)),
118                LayerChange(iced_layershell::reexport::Layer),
119                MarginChange((i32, i32, i32, i32)),
120                SizeChange((u32, u32)),
121                VirtualKeyboardPressed {
122                    time: u32,
123                    key: u32,
124                },
125            };
126            let try_into_impl = quote! {
127                impl #impl_gen TryInto<iced_layershell::actions::LayershellCustomActions> for #ident #ty_gen #where_gen {
128                    type Error = Self;
129
130                    fn try_into(self) -> Result<iced_layershell::actions::LayershellCustomActions, Self::Error> {
131                        use iced_layershell::actions::LayershellCustomActions;
132
133                        match self {
134                            Self::SetInputRegion(callback) => Ok(LayershellCustomActions::SetInputRegion(callback)),
135                            Self::AnchorChange(anchor) => Ok(LayershellCustomActions::AnchorChange(anchor)),
136                            Self::AnchorSizeChange(anchor, size) => Ok(LayershellCustomActions::AnchorSizeChange(anchor, size)),
137                            Self::LayerChange(layer) => Ok(LayershellCustomActions::LayerChange(layer)),
138
139                            Self::MarginChange(margin) => Ok(LayershellCustomActions::MarginChange(margin)),
140                            Self::SizeChange(size) => Ok(LayershellCustomActions::SizeChange(size)),
141                            Self::VirtualKeyboardPressed { time, key } => Ok(LayershellCustomActions::VirtualKeyboardPressed {
142                                time,
143                                key
144                            }),
145                            _ => Err(self)
146                        }
147                    }
148                }
149            };
150
151            (additional_variants, try_into_impl)
152        }
153    };
154
155    Ok(quote! {
156        #(#attrs)*
157        #vis enum #ident #ty_gen #where_gen {
158            #(#variants,)*
159            #additional_variants
160        }
161
162        #try_into_impl
163    })
164}
165
166#[derive(FromMeta)]
167struct ToLayerMessageAttr {
168    multi: Flag,
169}
170
171#[derive(FromDeriveInput)]
172#[darling(supports(enum_any))]
173struct MessageEnum {
174    vis: Visibility,
175    ident: Ident,
176    generics: Generics,
177    data: Data<Variant, Ignored>,
178}