1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{DeriveInput, parse_macro_input};
4
5use super::get_simple_attribute_field;
6
7pub fn derive_visual_context(input: TokenStream) -> TokenStream {
8 let ast = parse_macro_input!(input as DeriveInput);
9
10 let Some(window_variable) = get_simple_attribute_field(&ast, "window") else {
11 return quote! {
12 compile_error!("Derive must have a #[window] attribute to detect the &mut Window field");
13 }
14 .into();
15 };
16
17 let Some(app_variable) = get_simple_attribute_field(&ast, "app") else {
18 return quote! {
19 compile_error!("Derive must have a #[app] attribute to detect the &mut App field");
20 }
21 .into();
22 };
23
24 let type_name = &ast.ident;
25 let (impl_generics, type_generics, where_clause) = ast.generics.split_for_impl();
26
27 let r#gen = quote! {
28 impl #impl_generics gpui::VisualContext for #type_name #type_generics
29 #where_clause
30 {
31 fn window_handle(&self) -> gpui::AnyWindowHandle {
32 self.#window_variable.window_handle()
33 }
34
35 fn update_window_entity<T: 'static, R>(
36 &mut self,
37 entity: &gpui::Entity<T>,
38 update: impl FnOnce(&mut T, &mut gpui::Window, &mut gpui::Context<T>) -> R,
39 ) -> Self::Result<R> {
40 gpui::AppContext::update_entity(self.#app_variable, entity, |entity, cx| update(entity, self.#window_variable, cx))
41 }
42
43 fn new_window_entity<T: 'static>(
44 &mut self,
45 build_entity: impl FnOnce(&mut gpui::Window, &mut gpui::Context<'_, T>) -> T,
46 ) -> Self::Result<gpui::Entity<T>> {
47 gpui::AppContext::new(self.#app_variable, |cx| build_entity(self.#window_variable, cx))
48 }
49
50 fn replace_root_view<V>(
51 &mut self,
52 build_view: impl FnOnce(&mut gpui::Window, &mut gpui::Context<V>) -> V,
53 ) -> Self::Result<gpui::Entity<V>>
54 where
55 V: 'static + gpui::Render,
56 {
57 self.#window_variable.replace_root(self.#app_variable, build_view)
58 }
59
60 fn focus<V>(&mut self, entity: &gpui::Entity<V>) -> Self::Result<()>
61 where
62 V: gpui::Focusable,
63 {
64 let focus_handle = gpui::Focusable::focus_handle(entity, self.#app_variable);
65 self.#window_variable.focus(&focus_handle)
66 }
67 }
68 };
69
70 r#gen.into()
71}