Skip to main content

gpui_hooks/hooks/
use_state.rs

1use super::Hook;
2use std::any::Any;
3use std::cell::{Ref, RefCell, RefMut};
4
5/// Result type for use_state hook - (getter function, setter function)
6type UseStateResult<T> = (Box<dyn Fn() -> T>, Box<dyn Fn(T)>);
7
8/// UseState hook - manages a mutable state value
9pub struct UseState<T: 'static> {
10    value: RefCell<T>,
11    version: RefCell<usize>,
12}
13
14impl<T: 'static> UseState<T> {
15    pub fn new(initial: T) -> Self {
16        Self {
17            value: RefCell::new(initial),
18            version: RefCell::new(0),
19        }
20    }
21
22    /// Get an immutable reference to the current value
23    pub fn get(&self) -> Ref<'_, T> {
24        self.value.borrow()
25    }
26
27    /// Get a mutable reference to the current value (doesn't trigger re-render)
28    pub fn get_mut(&self) -> RefMut<'_, T> {
29        self.value.borrow_mut()
30    }
31
32    /// Set a new value and increment version (triggers re-render)
33    pub fn set(&self, new_value: T) {
34        *self.value.borrow_mut() = new_value;
35        *self.version.borrow_mut() += 1;
36    }
37
38    /// Get the current version number
39    pub fn version(&self) -> usize {
40        *self.version.borrow()
41    }
42}
43
44impl<T: 'static> Hook for UseState<T> {
45    fn as_any(&self) -> &dyn Any {
46        self
47    }
48    fn as_any_mut(&mut self) -> &mut dyn Any {
49        self
50    }
51}
52
53/// Trait for using state hooks
54///
55/// This trait is automatically implemented for any type that implements `HasHooks`
56/// (which includes all types using `#[hook_element]`).
57pub trait UseStateHook {
58    /// Get access to the hooks storage (internal use)
59    fn _hooks_ref(&self) -> &RefCell<Vec<Box<dyn Hook>>>;
60
61    /// Get and increment the hook index (internal use)
62    fn _next_hook_index(&self) -> usize;
63
64    /// Use a state hook
65    /// Returns (getter function, setter function)
66    fn use_state<T, F>(&self, initial: F) -> UseStateResult<T>
67    where
68        T: Clone + 'static,
69        F: FnOnce() -> T,
70    {
71        let idx = self._next_hook_index();
72        let hooks_ref = self._hooks_ref();
73
74        // Create hook if it doesn't exist
75        let hooks_len = hooks_ref.borrow().len();
76        if idx >= hooks_len {
77            let state = UseState::new(initial());
78            hooks_ref.borrow_mut().push(Box::new(state));
79        }
80
81        // Get the raw pointer to the hook - it will be stable after creation
82        let hook_ptr: *const dyn Hook = {
83            let hooks = hooks_ref.borrow();
84            let hook = hooks.get(idx).expect("Hook index out of bounds");
85            hook.as_ref() as *const dyn Hook
86        };
87
88        // Create getter closure using the stable raw pointer
89        let getter: Box<dyn Fn() -> T> = {
90            let state_ptr = hook_ptr as *const UseState<T>;
91            Box::new(move || unsafe { (*state_ptr).get().clone() })
92        };
93
94        // Create setter closure using the stable raw pointer
95        let setter: Box<dyn Fn(T)> = {
96            let state_ptr = hook_ptr as *const UseState<T>;
97            Box::new(move |new_value: T| unsafe {
98                (*state_ptr).set(new_value);
99            })
100        };
101
102        (getter, setter)
103    }
104}