vendor egui_inspect

This commit is contained in:
nora 2023-04-18 12:44:46 +02:00
parent 48386a6cf2
commit 16be9061d9
11 changed files with 3364 additions and 3 deletions

View file

@ -0,0 +1,106 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Attribute, Data, DeriveInput, Member};
enum FieldInspectKind {
/// Auto-inspected (it is assumed that the field implements Inspect)
Auto,
/// A function named by the token stream is called to inspect the field.
/// The function takes (thing: &mut T, ui: &mut Ui, id_source: u64)
WithFn(syn::Ident),
/// Not visited, left alone.
/// Useful when you want to skip a field that doesn't implement Inspect.
Opaque,
}
fn inspect_kind(attrs: &[Attribute]) -> FieldInspectKind {
for attr in attrs {
if attr.path().is_ident("opaque") {
return FieldInspectKind::Opaque;
} else if attr.path().is_ident("inspect_with") {
let fun: syn::Ident = attr.parse_args().expect("Failed to parse ident");
return FieldInspectKind::WithFn(fun);
}
}
FieldInspectKind::Auto
}
#[proc_macro_derive(Inspect, attributes(opaque, inspect_with))]
pub fn derive_inspect(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let ty_ident = input.ident;
let ts = match input.data {
Data::Struct(s) => {
let mut exprs = Vec::new();
for (i, f) in s.fields.iter().enumerate() {
let memb = match &f.ident {
Some(ident) => Member::from(ident.clone()),
None => Member::from(i),
};
match inspect_kind(&f.attrs) {
FieldInspectKind::Auto => {
exprs.push(quote! {
ui.horizontal(|ui| {
if ui.add(::egui::Label::new(stringify!(#f)).sense(::egui::Sense::click())).clicked() {
ui.output_mut(|o| o.copied_text = format!("{:?}", self.#memb));
}
::egui_inspect::Inspect::inspect_mut(&mut self.#memb, ui, #i as u64)
});
});
}
FieldInspectKind::Opaque => {
exprs.push(quote! {
ui.horizontal(|ui| {
ui.label(concat!(stringify!(#memb), " <opaque>"));
});
});
}
FieldInspectKind::WithFn(fun) => {
exprs.push(quote! {
ui.horizontal(|ui| {
ui.label(stringify!(#memb));
#fun(&mut self.#memb, ui, #i as u64)
});
});
}
}
}
quote! {
::egui::CollapsingHeader::new(stringify!(#ty_ident)).id_source(id_source).show(ui, |ui| {
#(#exprs)*
});
}
}
Data::Enum(e) => {
let mut sel_name_match_exprs = Vec::new();
let mut selectable_value_exprs = Vec::new();
for var in &e.variants {
let name = &var.ident;
sel_name_match_exprs.push(quote! {Self::#name => stringify!(#name)});
selectable_value_exprs
.push(quote! {ui.selectable_value(self, Self::#name, stringify!(#name))});
}
quote! {
let sel_text = match self {
#(#sel_name_match_exprs,)*
};
::egui::ComboBox::new(id_source, stringify!(#ty_ident)).selected_text(sel_text).show_ui(ui, |ui| {
#(#selectable_value_exprs;)*
});
}
}
Data::Union(_) => panic!("Unions are not supported"),
};
let (intro_generics, forward_generics, where_clauses) = input.generics.split_for_impl();
let expanded = quote! {
impl #intro_generics ::egui_inspect::Inspect for #ty_ident #forward_generics #where_clauses {
fn inspect(&self, ui: &mut ::egui::Ui, id_source: u64) {
}
fn inspect_mut(&mut self, ui: &mut ::egui::Ui, id_source: u64) {
#ts
}
}
};
proc_macro::TokenStream::from(expanded)
}