1use std::cell::RefCell;
6
7use gpui::{Context, IntoElement, Window};
8pub use gpui_hooks_macros::hook_element;
9pub mod hooks;
10use hooks::{HasHooks, Hook, UseEffectHook, UseMemoHook, UseStateHook};
11
12pub trait HookedElement: UseStateHook + UseEffectHook + UseMemoHook {
17 fn _hooks_ref(&self) -> &RefCell<Vec<Box<dyn Hook>>>;
19
20 fn _hook_index(&self) -> usize;
22
23 fn _set_hook_index(&self, index: usize);
25
26 fn _prev(&self) -> usize;
28
29 fn _set_prev(&self, prev: usize);
31
32 fn _next_hook_index(&self) -> usize {
34 let idx = self._hook_index();
35 self._set_hook_index(idx + 1);
36 idx
37 }
38
39 fn _reset(&self) {
41 let current = self._hook_index();
42 let prev = self._prev();
43
44 if prev != 0 && current != prev {
46 panic!(
47 "Hook count changed from {} to {}. Hooks must be called in the same order every render.",
48 prev, current
49 );
50 }
51
52 self._set_prev(current);
53 self._set_hook_index(0);
54 }
55}
56
57impl<T: HookedElement> HasHooks for T {
60 fn _hooks_storage(&self) -> &RefCell<Vec<Box<dyn Hook>>> {
61 HookedElement::_hooks_ref(self)
62 }
63
64 fn _next_index(&self) -> usize {
65 HookedElement::_next_hook_index(self)
66 }
67}
68
69pub trait HookedRender: Sized + HookedElement {
71 fn pre_render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) {
73 self._reset();
74 }
75
76 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement;
78}
79
80pub fn execute_hooked_render<T>(
82 this: &mut T,
83 window: &mut Window,
84 cx: &mut Context<T>,
85) -> impl IntoElement
86where
87 T: HookedRender,
88{
89 this.pre_render(window, cx);
90 this.render(window, cx)
91}
92
93