Skip to main content

gpui_cn/theme/
scope.rs

1//! A subtree drawn with another token set.
2
3use gpui_kit::{
4    AnyElement, App, Bounds, Element, ElementId, GlobalElementId, InspectorElementId, IntoElement,
5    LayoutId, Pixels, SharedString, Window,
6};
7
8use super::Theme;
9
10/// Draws its child with the tokens of a scope registered by
11/// [`Theme::set_scope`], so every gpui-cn component inside reads that
12/// scope through `cx.theme()`. A terminal uses one to draw its tab strip in
13/// the terminal's colors.
14///
15/// The scope holds while the child renders, lays out, and paints. A
16/// listener that runs later, on an event, reads the window's tokens. An
17/// unknown key draws with the window's tokens. Entering a scope changes no
18/// global, so it wakes no observer of the theme and costs no frame.
19pub struct ThemeScope {
20    key: SharedString,
21    child: AnyElement,
22}
23
24impl ThemeScope {
25    /// Draws `child` with the tokens of the scope `key`. The scope takes
26    /// the child's layout, so size the child.
27    pub fn new(key: impl Into<SharedString>, child: impl IntoElement) -> Self {
28        Self {
29            key: key.into(),
30            child: child.into_any_element(),
31        }
32    }
33}
34
35/// Runs `f` with the scope `key` active, then restores the scope around
36/// it, so scopes nest.
37fn within<R>(key: &str, cx: &mut App, f: impl FnOnce(&mut App) -> R) -> R {
38    let theme = Theme::global(cx);
39    let index = theme.scope_index(key);
40    let outer = theme
41        .active_scope
42        .replace(index.or(theme.active_scope.get()));
43    let result = f(cx);
44    Theme::global(cx).active_scope.set(outer);
45    result
46}
47
48impl IntoElement for ThemeScope {
49    type Element = Self;
50
51    fn into_element(self) -> Self::Element {
52        self
53    }
54}
55
56impl Element for ThemeScope {
57    type RequestLayoutState = ();
58    type PrepaintState = ();
59
60    fn id(&self) -> Option<ElementId> {
61        None
62    }
63
64    fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
65        None
66    }
67
68    fn request_layout(
69        &mut self,
70        _id: Option<&GlobalElementId>,
71        _inspector_id: Option<&InspectorElementId>,
72        window: &mut Window,
73        cx: &mut App,
74    ) -> (LayoutId, ()) {
75        let child = &mut self.child;
76        let layout = within(&self.key, cx, |cx| child.request_layout(window, cx));
77        (layout, ())
78    }
79
80    fn prepaint(
81        &mut self,
82        _id: Option<&GlobalElementId>,
83        _inspector_id: Option<&InspectorElementId>,
84        _bounds: Bounds<Pixels>,
85        _request_layout: &mut (),
86        window: &mut Window,
87        cx: &mut App,
88    ) {
89        let child = &mut self.child;
90        within(&self.key, cx, |cx| child.prepaint(window, cx));
91    }
92
93    fn paint(
94        &mut self,
95        _id: Option<&GlobalElementId>,
96        _inspector_id: Option<&InspectorElementId>,
97        _bounds: Bounds<Pixels>,
98        _request_layout: &mut (),
99        _prepaint: &mut (),
100        window: &mut Window,
101        cx: &mut App,
102    ) {
103        let child = &mut self.child;
104        within(&self.key, cx, |cx| child.paint(window, cx));
105    }
106}