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