gpui_base/
global_state.rs1use std::rc::{Rc, Weak};
2
3use gpui::{App, Entity, Global, OwnedMenu};
4
5use crate::text::TextViewState;
6
7pub struct DeferredPopover(#[allow(dead_code)] Rc<()>);
13
14pub struct GlobalState {
16 app_menus: Vec<OwnedMenu>,
17 deferred_popovers: Vec<Weak<()>>,
18 suppress_text_selection: bool,
19 pub(crate) text_view_state_stack: Vec<Entity<TextViewState>>,
20 selection_document_order: u64,
21}
22
23impl Global for GlobalState {}
24
25impl GlobalState {
26 fn new() -> Self {
27 Self {
28 app_menus: Vec::new(),
29 deferred_popovers: Vec::new(),
30 suppress_text_selection: false,
31 text_view_state_stack: Vec::new(),
32 selection_document_order: 1,
33 }
34 }
35
36 #[doc(hidden)]
38 pub fn init(cx: &mut App) {
39 if !cx.has_global::<Self>() {
40 cx.set_global(Self::new());
41 }
42 }
43
44 pub fn suppress_text_selection(cx: &mut App) {
49 Self::global_mut(cx).suppress_text_selection = true;
50 }
51
52 #[doc(hidden)]
54 pub fn reset_text_selection_suppression(cx: &mut App) {
55 Self::global_mut(cx).suppress_text_selection = false;
56 }
57
58 #[doc(hidden)]
60 pub fn is_text_selection_suppressed(cx: &App) -> bool {
61 Self::global(cx).suppress_text_selection
62 }
63
64 pub fn global(cx: &App) -> &Self {
65 cx.global::<Self>()
66 }
67
68 pub fn global_mut(cx: &mut App) -> &mut Self {
69 cx.global_mut::<Self>()
70 }
71
72 pub(crate) fn text_view_state(&self) -> Option<&Entity<TextViewState>> {
73 self.text_view_state_stack.last()
74 }
75
76 #[doc(hidden)]
77 pub fn begin_selection_frame(&mut self) {
78 self.selection_document_order = 1;
79 }
80
81 pub(crate) fn next_selection_document_order(&mut self) -> u64 {
82 let order = self.selection_document_order;
83 self.selection_document_order = self.selection_document_order.wrapping_add(1);
84 order
85 }
86
87 pub fn app_menus(&self) -> &[OwnedMenu] {
89 &self.app_menus
90 }
91
92 pub fn set_app_menus(&mut self, menus: Vec<OwnedMenu>) {
94 self.app_menus = menus;
95 }
96
97 pub fn is_in_deferred_context(cx: &App) -> bool {
100 Self::global(cx)
101 .deferred_popovers
102 .iter()
103 .any(|popover| popover.strong_count() > 0)
104 }
105
106 pub fn register_deferred_popover(cx: &mut App) -> DeferredPopover {
118 let token = Rc::new(());
119 let state = Self::global_mut(cx);
120 state
121 .deferred_popovers
122 .retain(|popover| popover.strong_count() > 0);
123 state.deferred_popovers.push(Rc::downgrade(&token));
124 DeferredPopover(token)
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[gpui::test]
133 fn initialization_is_idempotent_and_suppression_can_be_reset(cx: &mut gpui::TestAppContext) {
134 cx.update(|cx| {
135 GlobalState::init(cx);
136 GlobalState::suppress_text_selection(cx);
137 GlobalState::init(cx);
138 assert!(GlobalState::is_text_selection_suppressed(cx));
139
140 GlobalState::reset_text_selection_suppression(cx);
141 assert!(!GlobalState::is_text_selection_suppressed(cx));
142
143 assert!(!GlobalState::is_in_deferred_context(cx));
144 let popover = GlobalState::register_deferred_popover(cx);
145 assert!(GlobalState::is_in_deferred_context(cx));
146 drop(popover);
147 assert!(!GlobalState::is_in_deferred_context(cx));
148 });
149 }
150
151 #[gpui::test]
155 fn a_dropped_registration_closes_the_deferred_context(cx: &mut gpui::TestAppContext) {
156 cx.update(|cx| {
157 GlobalState::init(cx);
158
159 let outer = GlobalState::register_deferred_popover(cx);
160 {
161 let _inner = GlobalState::register_deferred_popover(cx);
162 assert!(GlobalState::is_in_deferred_context(cx));
163 }
164 assert!(GlobalState::is_in_deferred_context(cx));
165
166 drop(outer);
167 assert!(!GlobalState::is_in_deferred_context(cx));
168
169 let popover = GlobalState::register_deferred_popover(cx);
171 assert_eq!(GlobalState::global(cx).deferred_popovers.len(), 1);
172 drop(popover);
173 assert!(!GlobalState::is_in_deferred_context(cx));
174 });
175 }
176}