duat_core/context/mod.rs
1//! Access to widgets and other other parts of the state of Duat
2//!
3//! This module lets you access and mutate some things:
4//!
5//! # Buffers
6use std::any::TypeId;
7
8pub use self::{cache::*, global::*, handles::*, log::*};
9use crate::{
10 buffer::Buffer,
11 data::{Pass, RwData},
12 ui::{Area, Node, Widget},
13};
14
15mod cache;
16mod handles;
17mod log;
18
19mod global {
20 use std::{
21 path::PathBuf,
22 sync::{
23 LazyLock, Mutex, OnceLock,
24 atomic::{AtomicBool, Ordering},
25 mpsc,
26 },
27 };
28
29 use super::{CurWidgetNode, DynBuffer};
30 use crate::{
31 context::Handle,
32 data::{DataMap, Pass, RwData},
33 session::DuatEvent,
34 text::Text,
35 ui::{Widget, Window, Windows},
36 };
37
38 static WINDOWS: OnceLock<&Windows> = OnceLock::new();
39 static MODE_NAME: LazyLock<RwData<&str>> = LazyLock::new(RwData::default);
40
41 static WILL_RELOAD_OR_QUIT: AtomicBool = AtomicBool::new(false);
42 static CUR_DIR: OnceLock<Mutex<PathBuf>> = OnceLock::new();
43 static SENDER: OnceLock<mpsc::Sender<DuatEvent>> = OnceLock::new();
44
45 /// Queues a function to be done on the main thread with a
46 /// [`Pass`]
47 ///
48 /// You can use this whenever you don't have access to a `Pass`,
49 /// in order to execute an action on the main thread, gaining
50 /// access to Duat's global state within that function.
51 pub fn queue(f: impl FnOnce(&mut Pass) + Send + 'static) {
52 sender()
53 .send(DuatEvent::QueuedFunction(Box::new(f)))
54 .unwrap();
55 }
56
57 ////////// Internal setters meant to be called once
58
59 /// Attempts to set the current [`Handle`]
60 ///
61 /// Fails if said [`Handle`] was already deleted.
62 pub(crate) fn set_current_node(pa: &mut Pass, node: crate::ui::Node) {
63 if let Err(err) = windows().set_current_node(pa, node) {
64 super::warn!("{err}");
65 }
66 }
67
68 /// Sets the [`Window`]s for Duat
69 pub(crate) fn set_windows(windows: Windows) {
70 if WINDOWS.set(Box::leak(Box::new(windows))).is_err() {
71 panic!("Setup ran twice");
72 }
73 }
74
75 /// Orders to quit Duat
76 pub(crate) fn order_reload_or_quit() {
77 WILL_RELOAD_OR_QUIT.store(true, Ordering::Relaxed);
78 }
79
80 /// Sets the sender for [`DuatEvent`]s
81 ///
82 /// ONLY MEANT TO BE USED BY THE DUAT EXECUTABLE
83 #[doc(hidden)]
84 pub fn set_sender(sender: mpsc::Sender<DuatEvent>) {
85 SENDER.set(sender).expect("setup ran twice");
86 }
87
88 ////////// Widget Handle getters
89
90 /// Returns a "fixed" [`Handle`] for the currently active
91 /// [`Buffer`]
92 ///
93 /// This `Handle` will always point to the same `Buffer`,
94 /// even when it is not active. If you want a `Handle` that
95 /// always points to the current Buffer, see dyn_buffer
96 ///
97 /// [`Buffer`]: crate::buffer::Buffer
98 pub fn current_buffer(pa: &Pass) -> &Handle {
99 windows().current_buffer(pa).read(pa)
100 }
101
102 /// Returns a "dynamic" [`Handle`] for the active [`Buffer`]
103 ///
104 /// This `Handle` will change to point to the current `Buffer`,
105 /// whenever the user swicthes which `Buffer` is active. If you
106 /// want a `Handle` that will stay on the current `Buffer`, see
107 /// [`current_buffer`].
108 ///
109 /// [`Buffer`]: crate::buffer::Buffer
110 pub fn dynamic_buffer(pa: &Pass) -> DynBuffer {
111 let dyn_buffer = windows().current_buffer(pa).clone();
112 let cur_buffer = RwData::new(dyn_buffer.read(pa).clone());
113 DynBuffer {
114 cur_buffer: dyn_buffer,
115 saved_buffer: cur_buffer,
116 }
117 }
118
119 /// Returns a [`Handle`] for a [`Buffer`] with the given name
120 ///
121 /// [`Buffer`]: crate::buffer::Buffer
122 pub fn get_buffer(pa: &Pass, name: impl ToString) -> Result<Handle, Text> {
123 let (.., handle) = windows().named_buffer_entry(pa, &name.to_string())?;
124
125 Ok(handle)
126 }
127
128 /// Returns the current active [`Handle`]
129 ///
130 /// Unlike [`current_buffer`], this function will return a
131 /// [`Handle<dyn Widget>`], which means it could be any
132 /// [`Widget`], not just a [`Buffer`].
133 ///
134 /// [`Buffer`]: crate::buffer::Buffer
135 pub fn current_widget(pa: &Pass) -> &Handle<dyn Widget> {
136 windows().current_widget(pa).read(pa).handle()
137 }
138
139 /// The [`CurWidgetNode`]
140 pub(crate) fn current_widget_node(pa: &Pass) -> CurWidgetNode {
141 CurWidgetNode(windows().current_widget(pa).clone())
142 }
143
144 ////////// Other getters
145
146 /// The [`Window`]s of Duat
147 ///
148 /// This struct gives you reading access to every [`Window`] in
149 /// Duat with [`Windows::get`], you also get access to every
150 /// [`Handle<dyn Widget>`], including every [`Handle<Buffer>`],
151 /// through the [`Windows::handles`], [`Window::handles`] and
152 /// [`Window::buffers`] function
153 pub fn windows() -> &'static Windows {
154 WINDOWS.get().unwrap()
155 }
156
157 /// The current [`Window`]
158 ///
159 /// You can iterate through all [`Handle<Buffer>`]s and
160 /// [`Handle<dyn Widget>`] with [`Window::buffers`] and
161 /// [`Window::handles`] respectively.
162 ///
163 /// If you wish to access other [`Window`]s, you can use
164 /// `context::windows().get(pa, n)` to get the `n`th `Window`.
165 /// The current window number can be found with
166 /// [`context::current_win_index`]
167 ///
168 /// [`context::current_win_index`]: current_win_index
169 pub fn current_window(pa: &Pass) -> &Window {
170 let win = current_win_index(pa);
171 WINDOWS.get().unwrap().get(pa, win).unwrap()
172 }
173
174 /// The index of the currently active window
175 pub fn current_win_index(pa: &Pass) -> usize {
176 windows().current_window(pa)
177 }
178
179 /// The name of the current [`Mode`]
180 ///
181 /// This uses a [`DataMap`] in order to prevent mutation of said
182 /// name.
183 ///
184 /// [`Mode`]: crate::mode::Mode
185 pub fn mode_name() -> DataMap<&'static str, &'static str> {
186 MODE_NAME.map(|name| *name)
187 }
188
189 // pub(crate) in order to keep just the DataMap one public
190 pub(crate) fn raw_mode_name() -> RwData<&'static str> {
191 MODE_NAME.clone()
192 }
193
194 /// The current directory
195 pub fn current_dir() -> PathBuf {
196 CUR_DIR
197 .get_or_init(|| Mutex::new(std::env::current_dir().unwrap()))
198 .lock()
199 .unwrap()
200 .clone()
201 }
202
203 /// A [`mpsc::Sender`] for [`DuatEvent`]s in the main loop
204 pub(crate) fn sender() -> mpsc::Sender<DuatEvent> {
205 SENDER.get().unwrap().clone()
206 }
207
208 ////////// Functions for synchronization
209
210 /// Returns `true` if Duat is about to reload
211 pub fn will_reload_or_quit() -> bool {
212 WILL_RELOAD_OR_QUIT.load(Ordering::Relaxed)
213 }
214}
215
216/// A "dynamic" [`Handle`] wrapper for [`Buffer`]s
217///
218/// This `Handle` wrapper will always point to the presently active
219/// `Buffer`. It can also detect when that `Buffer` has been changed
220/// or when another `Buffer` becomes the active `Buffer`.
221pub struct DynBuffer {
222 cur_buffer: RwData<Handle>,
223 saved_buffer: RwData<Handle>,
224}
225
226impl DynBuffer {
227 /// Wether the [`Buffer`] pointed to has changed or swapped with
228 /// another
229 pub fn has_changed(&self, pa: &Pass) -> bool {
230 if self.cur_buffer.has_changed() {
231 true
232 } else {
233 self.saved_buffer.read(pa).has_changed(pa)
234 }
235 }
236
237 /// Swaps the [`DynBuffer`] to the currently active [`Buffer`]
238 pub fn swap_to_current(&mut self) {
239 // SAFETY: Since this struct uses deep Cloning, no mutable
240 // references to the RwData exist.
241 let pa = unsafe { &mut Pass::new() };
242 if self.cur_buffer.has_changed() {
243 *self.saved_buffer.write(pa) = self.cur_buffer.read(pa).clone();
244 }
245 }
246
247 /// Reads the presently active [`Buffer`]
248 pub fn read<'a>(&'a mut self, pa: &'a Pass) -> &'a Buffer {
249 self.saved_buffer.read(pa).read(pa)
250 }
251
252 /// The [`Handle<Buffer>`] currently being pointed to
253 pub fn handle(&self) -> &Handle {
254 // SAFETY: Since this struct uses deep Cloning, no mutable
255 // references to the RwData exist.
256 static INTERNAL_PASS: &Pass = unsafe { &Pass::new() };
257 self.saved_buffer.read(INTERNAL_PASS)
258 }
259
260 /// Simulates a [`read`] without actually reading
261 ///
262 /// This is useful if you want to tell Duat that you don't want
263 /// [`has_changed`] to return `true`, but you don't have a
264 /// [`Pass`] available to [`read`] the value.
265 ///
266 /// This assumes that you don't care about the active [`Buffer`]
267 /// possibly being swapped.
268 ///
269 /// [`read`]: Self::read
270 /// [`has_changed`]: Self::has_changed
271 pub fn declare_as_read(&self) {
272 // SAFETY: Since this struct uses deep Cloning, no mutable
273 // references to the RwData exist.
274 static INTERNAL_PASS: &Pass = unsafe { &Pass::new() };
275 self.cur_buffer.declare_as_read();
276 self.saved_buffer.read(INTERNAL_PASS).declare_as_read();
277 }
278
279 ////////// Writing functions
280
281 /// Reads the presently active [`Buffer`]
282 pub fn write<'a>(&'a self, pa: &'a mut Pass) -> &'a mut Buffer {
283 // SAFETY: Because I already got a &mut Pass, the RwData can't be
284 // accessed anyways.
285 static INTERNAL_PASS: &Pass = unsafe { &Pass::new() };
286
287 self.saved_buffer.read(INTERNAL_PASS).write(pa)
288 }
289
290 /// Writes to the [`Buffer`] and [`Area`], making use of a
291 /// [`Pass`]
292 ///
293 /// [`Area`]: crate::ui::Area
294 pub fn write_with_area<'a>(&'a self, pa: &'a mut Pass) -> (&'a mut Buffer, &'a mut Area) {
295 // SAFETY: Because I already got a &mut Pass, the RwData can't be
296 // accessed anyways.
297 static INTERNAL_PASS: &Pass = unsafe { &Pass::new() };
298
299 self.saved_buffer.read(INTERNAL_PASS).write_with_area(pa)
300 }
301
302 /// Simulates a [`write`] without actually writing
303 ///
304 /// This is useful if you want to tell Duat that you want
305 /// [`has_changed`] to return `true`, but you don't have a
306 /// [`Pass`] available to `write` the value with.
307 ///
308 /// [`write`]: Self::write
309 /// [`has_changed`]: Self::has_changed
310 pub fn declare_written(&self) {
311 // SAFETY: Since this struct uses deep Cloning, no other references to
312 // the RwData exist.
313 static INTERNAL_PASS: &Pass = unsafe { &Pass::new() };
314 self.cur_buffer.read(INTERNAL_PASS).declare_written();
315 }
316}
317
318impl Clone for DynBuffer {
319 /// Returns a _deep cloned_ duplicate of the value
320 ///
321 /// In this case, what this means is that the clone and `self`
322 /// will have different internal pointers for the current
323 /// [`Buffer`]. So if, for example, you call
324 /// [`DynBuffer::swap_to_current`] on `self`, that will switch
325 /// `self` to point to the current `Buffer`, but the same will not
326 /// be done in the clone.
327 fn clone(&self) -> Self {
328 // SAFETY: Because I already got a &mut Pass, the RwData can't be
329 // accessed anyways.
330 static INTERNAL_PASS: &Pass = unsafe { &Pass::new() };
331
332 Self {
333 cur_buffer: RwData::new(self.cur_buffer.read(INTERNAL_PASS).clone()),
334 saved_buffer: self.saved_buffer.clone(),
335 }
336 }
337}
338
339/// The current [`Widget`]
340pub(crate) struct CurWidgetNode(RwData<Node>);
341
342impl CurWidgetNode {
343 /// The [`Widget`]'s [`TypeId`]
344 pub fn type_id(&self, pa: &Pass) -> TypeId {
345 self.0.read(pa).widget().type_id()
346 }
347
348 /// Reads the [`Widget`] and its [`Area`]
349 pub fn _read<R>(&self, pa: &Pass, f: impl FnOnce(&dyn Widget, &Area) -> R) -> R {
350 let node = self.0.read(pa);
351 f(node.handle().read(pa), node.area().read(pa))
352 }
353
354 /// Reads the [`Widget`] as `W` and its
355 /// [`Area`]
356 pub fn _read_as<W: Widget, R>(&self, pa: &Pass, f: impl FnOnce(&W, &Area) -> R) -> Option<R> {
357 let node = self.0.read(pa);
358 Some(f(node.read_as(pa)?, node.area().read(pa)))
359 }
360
361 /// Mutates the [`RwData<dyn Widget>`], its
362 /// [`Area`], and related [`Widget`]s
363 pub(crate) fn mutate_data<R>(&self, pa: &Pass, f: impl FnOnce(&Handle<dyn Widget>) -> R) -> R {
364 f(self.0.read(pa).handle())
365 }
366
367 /// Mutates the [`RwData<dyn Widget>`] as `W`, its
368 /// [`Area`], and related [`Widget`]s
369 pub(crate) fn mutate_data_as<W: Widget, R>(
370 &self,
371 pa: &Pass,
372 f: impl FnOnce(&Handle<W>) -> R,
373 ) -> Option<R> {
374 let inner = self.0.read(pa);
375 Some(f(&inner.try_downcast()?))
376 }
377
378 /// The inner [`Node`]
379 pub(crate) fn node(&self, pa: &Pass) -> Node {
380 self.0.read(pa).clone()
381 }
382}