Skip to main content

gpui/
global.rs

1use crate::{App, BorrowAppContext};
2
3/// Marks a type whose value is stored once per app.
4///
5/// Implement it for a type, then store one value of that type with [`App::set_global`]. Read it with
6/// [`App::global`], and change it with [`UpdateGlobal::update_global`]. Use a global for state that
7/// exists once for the whole app, such as settings or a theme. Use an entity for state that can have
8/// several instances, or that several parts of the app change independently.
9///
10/// The trait is a marker: it adds no methods of its own. Helper traits with blanket implementations,
11/// such as [`ReadGlobal`] and [`UpdateGlobal`], add methods to every `Global` type.
12///
13/// # Restricting access to globals
14///
15/// In some situations you may need to store some global state, but want to
16/// restrict access to reading it or writing to it.
17///
18/// In these cases, Rust's visibility system can be used to restrict access to
19/// a global value. For example, you can create a private struct that implements
20/// [`Global`] and holds the global state. Then create a newtype struct that wraps
21/// the global type and create custom accessor methods to expose the desired subset
22/// of operations.
23///
24/// # Examples
25///
26/// ```
27/// # use gpui::{Global, TestApp};
28/// struct Theme {
29///     dark: bool,
30/// }
31///
32/// impl Global for Theme {}
33///
34/// # let mut app = TestApp::new();
35/// app.set_global(Theme { dark: false });
36/// assert!(!app.read(|cx| cx.global::<Theme>().dark));
37/// ```
38pub trait Global: 'static {
39    // This trait is intentionally left empty, by virtue of being a marker trait.
40    //
41    // Use additional traits with blanket implementations to attach functionality
42    // to types that implement `Global`.
43}
44
45/// Reads the global of the implementing type from an [`App`], as in `Theme::global(cx)`.
46///
47/// Every [`Global`] type implements this trait. Use it to name a global by type instead of calling
48/// [`App::global`] with a turbofish.
49///
50/// # Examples
51///
52/// ```
53/// # use gpui::{Global, ReadGlobal, TestApp};
54/// struct Theme {
55///     dark: bool,
56/// }
57///
58/// impl Global for Theme {}
59///
60/// # let mut app = TestApp::new();
61/// app.set_global(Theme { dark: true });
62/// assert!(app.read(|cx| Theme::global(cx).dark));
63/// ```
64pub trait ReadGlobal {
65    /// Returns the global of this type.
66    ///
67    /// # Panics
68    ///
69    /// Panics if no global of this type has been set.
70    fn global(cx: &App) -> &Self;
71}
72
73impl<T: Global> ReadGlobal for T {
74    fn global(cx: &App) -> &Self {
75        cx.global::<T>()
76    }
77}
78
79/// Changes or sets the global of the implementing type through a context.
80///
81/// Every [`Global`] type implements this trait. `Theme::update_global(cx, ...)` is the same as
82/// `cx.update_global::<Theme, _>(...)`, and `Theme::set_global(cx, ...)` is the same as
83/// `cx.set_global(...)`. Both accept any context that implements `BorrowAppContext`, such as
84/// `&mut App`.
85///
86/// # Examples
87///
88/// ```
89/// # use gpui::{Global, TestApp, UpdateGlobal};
90/// struct Theme {
91///     dark: bool,
92/// }
93///
94/// impl Global for Theme {}
95///
96/// # let mut app = TestApp::new();
97/// app.update(|cx| {
98///     Theme::set_global(cx, Theme { dark: false });
99///     Theme::update_global(cx, |theme, _cx| theme.dark = true);
100/// });
101/// assert!(app.read(|cx| cx.global::<Theme>().dark));
102/// ```
103pub trait UpdateGlobal {
104    /// Runs `update` with mutable access to the global and context.
105    ///
106    /// The global is taken out of the app while `update` runs, so `update` can't read it through the
107    /// context. Observers of the global are notified when the update finishes.
108    ///
109    /// # Panics
110    ///
111    /// Panics if no global of this type has been set, or if this global is already being updated.
112    fn update_global<C, F, R>(cx: &mut C, update: F) -> R
113    where
114        C: BorrowAppContext,
115        F: FnOnce(&mut Self, &mut C) -> R;
116
117    /// Sets the global of this type to `global`, replacing any previous value.
118    ///
119    /// Observers of the global are notified when the update finishes.
120    fn set_global<C>(cx: &mut C, global: Self)
121    where
122        C: BorrowAppContext;
123}
124
125impl<T: Global> UpdateGlobal for T {
126    #[track_caller]
127    fn update_global<C, F, R>(cx: &mut C, update: F) -> R
128    where
129        C: BorrowAppContext,
130        F: FnOnce(&mut Self, &mut C) -> R,
131    {
132        cx.update_global(update)
133    }
134
135    fn set_global<C>(cx: &mut C, global: Self)
136    where
137        C: BorrowAppContext,
138    {
139        cx.set_global(global)
140    }
141}