Skip to main content

gpui_hooks/
lib.rs

1//! GPUI Hooks - 为GPUI组件添加hook系统的库
2//!
3//! 这个库提供了 `#[hook_element]` 属性宏,可以为结构体自动添加hooks字段和相关方法。
4
5use 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
12/// HookedElement trait - 管理组件的hooks
13/// 使用内部可变性模式,使得hooks可以在&self上调用
14///
15/// 自动实现了 UseStateHook, UseEffectHook, UseMemoHook
16pub trait HookedElement: UseStateHook + UseEffectHook + UseMemoHook {
17    /// Get access to the hooks RefCell
18    fn _hooks_ref(&self) -> &RefCell<Vec<Box<dyn Hook>>>;
19
20    /// Get the current hook index
21    fn _hook_index(&self) -> usize;
22
23    /// Set the hook index
24    fn _set_hook_index(&self, index: usize);
25
26    /// Get the previous hook count
27    fn _prev(&self) -> usize;
28
29    /// Set the previous hook count
30    fn _set_prev(&self, prev: usize);
31
32    /// Increment hook index and return the old value
33    fn _next_hook_index(&self) -> usize {
34        let idx = self._hook_index();
35        self._set_hook_index(idx + 1);
36        idx
37    }
38
39    /// Reset hook state at the start of render
40    fn _reset(&self) {
41        let current = self._hook_index();
42        let prev = self._prev();
43
44        // Check if hook count changed from previous render
45        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
57// Blanket implementation of HasHooks for all HookedElement types
58// This enables automatic implementation of UseStateHook, UseEffectHook, UseMemoHook
59impl<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
69/// HookedRender trait - 在GPUI的Render后执行钩子代码
70pub trait HookedRender: Sized + HookedElement {
71    /// render之前执行的代码(默认执行所有hooks)
72    fn pre_render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) {
73        self._reset();
74    }
75
76    /// render的主要逻辑
77    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement;
78}
79
80/// 执行HookedRender的完整流程,在Render实现中调用
81pub 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// Blanket implementation for Drop to clean up effects
94// Note: Users need to manually call cleanup_effects in their Drop impl
95// since we can't provide a blanket Drop impl without specialization