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