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::{
11    HasHooks, Hook, UseCallbackHook, UseEffectHook, UseMemoHook, UseRefHook, UseStateHook,
12};
13
14/// HookedElement trait - 管理组件的hooks
15/// 使用内部可变性模式,使得hooks可以在&self上调用
16///
17/// 自动实现了 UseStateHook, UseEffectHook, UseMemoHook
18pub trait HookedElement:
19    UseStateHook + UseEffectHook + UseMemoHook + UseRefHook + UseCallbackHook
20{
21    /// Get access to the hooks RefCell
22    fn _hooks_ref(&self) -> &RefCell<Vec<Box<dyn Hook>>>;
23
24    /// Get the current hook index
25    fn _hook_index(&self) -> usize;
26
27    /// Set the hook index
28    fn _set_hook_index(&self, index: usize);
29
30    /// Get the previous hook count
31    fn _prev(&self) -> usize;
32
33    /// Set the previous hook count
34    fn _set_prev(&self, prev: usize);
35
36    /// Increment hook index and return the old value
37    fn _next_hook_index(&self) -> usize {
38        let idx = self._hook_index();
39        self._set_hook_index(idx + 1);
40        idx
41    }
42
43    /// Reset hook state at the start of render
44    fn _reset(&self) {
45        let current = self._hook_index();
46        let prev = self._prev();
47
48        // Check if hook count changed from previous render
49        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
61// Blanket implementation of HasHooks for all HookedElement types
62// This enables automatic implementation of UseStateHook, UseEffectHook, UseMemoHook
63impl<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
73/// HookedRender trait - 在GPUI的Render后执行钩子代码
74pub trait HookedRender: Sized + HookedElement {
75    /// render之前执行的代码(默认执行所有hooks)
76    fn pre_render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) {
77        self._reset();
78    }
79
80    /// render的主要逻辑
81    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement;
82}
83
84/// 执行HookedRender的完整流程,在Render实现中调用
85pub 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// Blanket implementation for Drop to clean up effects
98// Note: Users need to manually call cleanup_effects in their Drop impl
99// since we can't provide a blanket Drop impl without specialization