1use std::{any::TypeId, sync::Arc};
7
8use parking_lot::Mutex;
9
10pub use self::global::*;
11use crate::{
12 data::{ReadDataGuard, RwData, WriteDataGuard},
13 mode::Cursors,
14 text::Text,
15 ui::{Area, Ui},
16 widgets::{File, Node, Related, Widget},
17};
18
19mod global {
20 use std::{
21 any::Any,
22 path::PathBuf,
23 sync::{
24 LazyLock, OnceLock,
25 atomic::{AtomicBool, AtomicUsize, Ordering},
26 },
27 };
28
29 use parking_lot::{Mutex, RwLock};
30
31 use super::{CurFile, CurWidget, DynamicFile, FileParts, FixedFile, Notifications};
32 use crate::{
33 data::RwData,
34 duat_name,
35 mode::Regular,
36 text::{Text, err},
37 ui::{Ui, Window},
38 widgets::Node,
39 };
40
41 static MODE_NAME: LazyLock<RwData<&str>> =
42 LazyLock::new(|| RwData::new(duat_name::<Regular>()));
43 static CUR_FILE: OnceLock<&(dyn Any + Send + Sync)> = OnceLock::new();
44 static CUR_WIDGET: OnceLock<&(dyn Any + Send + Sync)> = OnceLock::new();
45 static CUR_WINDOW: AtomicUsize = AtomicUsize::new(0);
46 static WINDOWS: OnceLock<&(dyn Any + Send + Sync)> = OnceLock::new();
47 static NOTIFICATIONS: LazyLock<RwData<Vec<Text>>> = LazyLock::new(RwData::default);
48 static WILL_RELOAD_OR_QUIT: AtomicBool = AtomicBool::new(false);
49 static CUR_DIR: OnceLock<Mutex<PathBuf>> = OnceLock::new();
50
51 pub(crate) fn mode_name() -> &'static RwData<&'static str> {
53 &MODE_NAME
54 }
55
56 pub fn file_named<U: Ui>(name: impl ToString) -> Result<FixedFile<U>, Text> {
57 let windows = windows::<U>().read();
58 let name = name.to_string();
59 let (.., node) = crate::file_entry(&windows, &name)?;
60
61 let (widget, area, related) = node.parts();
62 let file = widget.try_downcast().unwrap();
63
64 Ok(FixedFile((
65 file,
66 area.clone(),
67 related.as_ref().unwrap().clone(),
68 )))
69 }
70
71 pub fn fixed_file<U: Ui>() -> Result<FixedFile<U>, Text> {
72 Ok(cur_file()?.fixed_file())
73 }
74
75 pub fn dyn_file<U: Ui>() -> Result<DynamicFile<U>, Text> {
76 Ok(cur_file()?.dyn_file())
77 }
78
79 pub fn cur_window() -> usize {
80 CUR_WINDOW.load(Ordering::Relaxed)
81 }
82
83 pub fn cur_dir() -> PathBuf {
84 CUR_DIR
85 .get_or_init(|| Mutex::new(std::env::current_dir().unwrap()))
86 .lock()
87 .clone()
88 }
89
90 pub fn notifications() -> Notifications {
91 Notifications(NOTIFICATIONS.clone())
92 }
93
94 pub fn notify(msg: Text) {
95 NOTIFICATIONS.write().push(msg)
96 }
97
98 pub fn will_reload_or_quit() -> bool {
100 WILL_RELOAD_OR_QUIT.load(Ordering::Relaxed)
101 }
102
103 pub(crate) fn set_cur<U: Ui>(
104 parts: Option<FileParts<U>>,
105 node: Node<U>,
106 ) -> Option<(FileParts<U>, Node<U>)> {
107 let prev = parts.and_then(|p| inner_cur_file().0.write().replace(p));
108
109 prev.zip(inner_cur_widget().0.write().replace(node))
110 }
111
112 pub(crate) fn cur_widget<U: Ui>() -> Result<&'static CurWidget<U>, Text> {
113 let cur_widget = inner_cur_widget();
114 cur_widget.0.read().as_ref().ok_or(err!("No widget yet"))?;
115 Ok(cur_widget)
116 }
117
118 pub(crate) fn set_windows<U: Ui>(wins: Vec<Window<U>>) -> &'static AtomicUsize {
119 *windows().write() = wins;
120 &CUR_WINDOW
121 }
122
123 pub(crate) fn windows<U: Ui>() -> &'static RwLock<Vec<Window<U>>> {
124 WINDOWS.get().unwrap().downcast_ref().expect("1 Ui only")
125 }
126
127 pub(crate) fn inner_cur_file<U: Ui>() -> &'static CurFile<U> {
128 CUR_FILE.get().unwrap().downcast_ref().expect("1 Ui only")
129 }
130
131 pub(crate) fn order_reload_or_quit() {
133 WILL_RELOAD_OR_QUIT.store(true, Ordering::Relaxed);
134 while crate::thread::still_running() {
135 std::thread::sleep(std::time::Duration::from_micros(500));
136 }
137 }
138
139 fn cur_file<U: Ui>() -> Result<&'static CurFile<U>, Text> {
140 let cur_file = inner_cur_file();
141 cur_file.0.read().as_ref().ok_or(err!("No file yet"))?;
142 Ok(cur_file)
143 }
144
145 fn inner_cur_widget<U: Ui>() -> &'static CurWidget<U> {
146 CUR_WIDGET.get().unwrap().downcast_ref().expect("1 Ui only")
147 }
148
149 #[doc(hidden)]
150 pub fn setup_non_statics<U: Ui>(
151 cur_file: &'static CurFile<U>,
152 cur_widget: &'static CurWidget<U>,
153 cur_window: usize,
154 windows: &'static RwLock<Vec<Window<U>>>,
155 ) {
156 CUR_FILE.set(cur_file).expect("setup ran twice");
157 CUR_WIDGET.set(cur_widget).expect("setup ran twice");
158 CUR_WINDOW.store(cur_window, Ordering::Relaxed);
159 WINDOWS.set(windows).expect("setup ran twice");
160 }
161}
162
163pub struct CurFile<U: Ui>(RwData<Option<FileParts<U>>>);
164
165impl<U: Ui> CurFile<U> {
166 pub fn new() -> Self {
167 Self(RwData::new(None))
168 }
169
170 pub fn fixed_file(&self) -> FixedFile<U> {
171 let parts = self.0.raw_read();
172 let (file, area, related) = parts.clone().unwrap();
173
174 FixedFile((file, area, related))
175 }
176
177 pub fn dyn_file(&self) -> DynamicFile<U> {
178 let dyn_parts = self.0.clone();
179 let (file, area, related) = {
182 let parts = dyn_parts.read();
183 parts.clone().unwrap()
184 };
185 let checker = file.checker();
186
187 DynamicFile {
188 parts: (file, area, related),
189 dyn_parts,
190 checker: Arc::new(Mutex::new(Box::new(checker))),
191 }
192 }
193
194 pub(crate) fn mutate_related_widget<W: Widget<U>, R>(
195 &self,
196 f: impl FnOnce(&mut W, &U::Area) -> R,
197 ) -> Option<R> {
198 let f = move |widget: &mut W, area| {
199 let cfg = widget.print_cfg();
200 widget.text_mut().remove_cursors(area, cfg);
201
202 let ret = f(widget, area);
203
204 let cfg = widget.print_cfg();
205
206 if let Some(main) = widget.cursors().and_then(Cursors::get_main) {
207 area.scroll_around_point(widget.text(), main.caret(), widget.print_cfg());
208 }
209 widget.text_mut().add_cursors(area, cfg);
210 widget.update(area);
211 widget.print(area);
212
213 ret
214 };
215
216 let data = self.0.raw_read();
217 let (file, area, rel) = data.as_ref().unwrap();
218
219 let rel = rel.read();
220 if file.data_is::<W>() {
221 file.write_as().map(|mut w| f(&mut w, area))
222 } else {
223 rel.iter()
224 .find(|node| node.data_is::<W>())
225 .and_then(|node| {
226 let (widget, area, _) = node.parts();
227 widget.write_as().map(|mut w| f(&mut w, area))
228 })
229 }
230 }
231}
232
233impl<U: Ui> Default for CurFile<U> {
234 fn default() -> Self {
235 Self::new()
236 }
237}
238
239#[derive(Clone)]
240pub struct FixedFile<U: Ui>(FileParts<U>);
241
242impl<U: Ui> FixedFile<U> {
243 pub fn read(&mut self) -> (ReadDataGuard<File>, &U::Area) {
244 let (file, area, _) = &self.0;
245 (file.read(), area)
246 }
247
248 pub fn write(&mut self) -> (WriteFileGuard<U>, &U::Area) {
249 let (file, area, _) = &self.0;
250 let mut file = file.write();
251 let cfg = file.print_cfg();
252 file.text_mut().remove_cursors(area, cfg);
253
254 let guard = WriteFileGuard { file, area };
255 (guard, area)
256 }
257
258 pub fn has_changed(&self) -> bool {
259 self.0.0.has_changed()
260 }
261
262 pub fn checker(&self) -> impl Fn() -> bool + Send + Sync + 'static + use<U> {
263 self.0.0.checker()
264 }
265
266 pub fn file_ptr_eq(&self, other: &Node<U>) -> bool {
269 other.ptr_eq(&self.0.0)
270 }
271
272 pub(crate) fn get_related_widget<W>(&mut self) -> Option<(RwData<W>, U::Area)> {
273 let (.., related) = &self.0;
274 let related = related.read();
275 related.iter().find_map(|node| {
276 let (widget, area, _) = node.parts();
277 widget.try_downcast().map(|data| (data, area.clone()))
278 })
279 }
280
281 pub(crate) fn inspect_related<W: 'static, R>(
282 &mut self,
283 f: impl FnOnce(&W, &U::Area) -> R,
284 ) -> Option<R> {
285 let (file, area, related) = &self.0;
286
287 if file.data_is::<W>() {
288 file.read_as().map(|w| f(&w, area))
289 } else {
290 let related = related.read();
291 related
292 .iter()
293 .find(|node| node.data_is::<W>())
294 .and_then(|node| node.widget().read_as().map(|w| f(&w, node.area())))
295 }
296 }
297
298 pub(crate) fn related_widgets(&self) -> &RwData<Vec<Node<U>>> {
299 &self.0.2
300 }
301}
302
303pub struct DynamicFile<U: Ui> {
304 parts: FileParts<U>,
305 dyn_parts: RwData<Option<FileParts<U>>>,
306 checker: Arc<Mutex<Box<dyn Fn() -> bool + Send + Sync + 'static>>>,
307}
308
309impl<U: Ui> DynamicFile<U> {
310 pub fn read(&mut self) -> (ReadDataGuard<File>, &U::Area) {
311 if self.dyn_parts.has_changed() {
312 let (file, area, related) = self.dyn_parts.read().clone().unwrap();
313 *self.checker.lock() = Box::new(file.checker());
314 self.parts = (file, area, related);
315 }
316
317 let (file, area, _) = &self.parts;
318 (file.read(), area)
319 }
320
321 pub fn write(&mut self) -> (WriteFileGuard<U>, &U::Area) {
322 if self.dyn_parts.has_changed() {
323 let (file, area, related) = self.dyn_parts.read().clone().unwrap();
324 *self.checker.lock() = Box::new(file.checker());
325 self.parts = (file, area, related);
326 }
327
328 let (file, area, _) = &self.parts;
329 let mut file = file.write();
330 let cfg = file.print_cfg();
331 file.text_mut().remove_cursors(area, cfg);
332
333 let guard = WriteFileGuard { file, area };
334 (guard, area)
335 }
336
337 pub fn has_swapped(&self) -> bool {
339 self.dyn_parts.has_changed()
340 }
341
342 pub fn has_changed(&self) -> bool {
343 let has_swapped = self.dyn_parts.has_changed();
344 let (file, ..) = &self.parts;
345
346 file.has_changed() || has_swapped
347 }
348
349 pub fn checker(&self) -> impl Fn() -> bool + Send + Sync + 'static + use<U> {
350 let dyn_checker = self.dyn_parts.checker();
351 let checker = self.checker.clone();
352 move || checker.lock()() || { dyn_checker() }
353 }
354
355 pub fn file_ptr_eq(&self, other: &Node<U>) -> bool {
358 other.ptr_eq(&self.dyn_parts.raw_read().as_ref().unwrap().0)
359 }
360
361 pub(crate) fn inspect_related<W: 'static, R>(
362 &mut self,
363 f: impl FnOnce(&W, &U::Area) -> R,
364 ) -> Option<R> {
365 if self.dyn_parts.has_changed() {
366 let (file, area, related) = self.dyn_parts.read().clone().unwrap();
367 *self.checker.lock() = Box::new(file.checker());
368 self.parts = (file, area, related);
369 }
370 let (file, area, related) = &self.parts;
371
372 if file.data_is::<W>() {
373 file.read_as().map(|w| f(&w, area))
374 } else {
375 let related = related.read();
376 related
377 .iter()
378 .find(|node| node.data_is::<W>())
379 .and_then(|node| node.widget().read_as().map(|w| f(&w, node.area())))
380 }
381 }
382}
383
384impl<U: Ui> Clone for DynamicFile<U> {
385 fn clone(&self) -> Self {
386 let (file, area, related) = self.parts.clone();
387 let checker = file.checker();
388 Self {
389 parts: (file, area, related),
390 dyn_parts: self.dyn_parts.clone(),
391 checker: Arc::new(Mutex::new(Box::new(checker))),
392 }
393 }
394}
395
396pub struct WriteFileGuard<'a, U: Ui> {
397 file: WriteDataGuard<'a, File>,
398 area: &'a U::Area,
399}
400
401impl<U: Ui> std::ops::Deref for WriteFileGuard<'_, U> {
402 type Target = File;
403
404 fn deref(&self) -> &Self::Target {
405 &self.file
406 }
407}
408
409impl<U: Ui> std::ops::DerefMut for WriteFileGuard<'_, U> {
410 fn deref_mut(&mut self) -> &mut Self::Target {
411 &mut self.file
412 }
413}
414
415impl<U: Ui> Drop for WriteFileGuard<'_, U> {
417 fn drop(&mut self) {
418 let cfg = self.file.print_cfg();
419
420 if let Some(main) = self.file.cursors().get_main() {
421 self.area
422 .scroll_around_point(self.file.text(), main.caret(), cfg);
423 }
424 self.file.text_mut().add_cursors(self.area, cfg);
425
426 <File as Widget<U>>::update(&mut self.file, self.area);
427 <File as Widget<U>>::print(&mut self.file, self.area);
428 }
429}
430
431#[derive(Clone)]
439pub struct Notifications(RwData<Vec<Text>>);
440
441impl Notifications {
442 pub fn read(&mut self) -> ReadDataGuard<Vec<Text>> {
444 self.0.read()
445 }
446
447 pub fn has_changed(&self) -> bool {
449 self.0.has_changed()
450 }
451
452 pub fn checker(&self) -> impl Fn() -> bool + Send + Sync + use<> {
454 self.0.checker()
455 }
456
457 pub fn push(&mut self, text: Text) {
461 self.0.write().push(text)
462 }
463}
464
465#[doc(hidden)]
466pub struct CurWidget<U: Ui>(RwData<Option<Node<U>>>);
467
468impl<U: Ui> CurWidget<U> {
469 pub fn new() -> Self {
470 Self(RwData::new(None))
471 }
472
473 pub fn type_id(&self) -> TypeId {
474 self.0.type_id
475 }
476
477 pub fn inspect<R>(&self, f: impl FnOnce(&dyn Widget<U>, &U::Area) -> R) -> R {
478 let data = self.0.raw_read();
479 let (widget, area, _) = data.as_ref().unwrap().parts();
480 let widget = widget.read();
481
482 f(&*widget, area)
483 }
484
485 pub fn inspect_as<W: Widget<U>, R>(&self, f: impl FnOnce(&W, &U::Area) -> R) -> Option<R> {
486 let data = self.0.raw_read();
487 let (widget, area, _) = data.as_ref().unwrap().parts();
488
489 widget.read_as().map(|widget| f(&widget, area))
490 }
491
492 pub(crate) fn mutate_data<R>(
493 &self,
494 f: impl FnOnce(&RwData<dyn Widget<U>>, &U::Area, &Related<U>) -> R,
495 ) -> R {
496 let data = self.0.read();
497 let (widget, area, related) = data.as_ref().unwrap().parts();
498
499 f(widget, area, related)
500 }
501
502 pub(crate) fn mutate_data_as<W: Widget<U>, R>(
503 &self,
504 f: impl FnOnce(&RwData<W>, &U::Area, &Related<U>) -> R,
505 ) -> Option<R> {
506 let data = self.0.read();
507 let (widget, area, related) = data.as_ref().unwrap().parts();
508
509 Some(f(&widget.try_downcast::<W>()?, area, related))
510 }
511
512 pub(crate) fn node(&self) -> Node<U> {
513 self.0.read().as_ref().unwrap().clone()
514 }
515}
516
517impl<U: Ui> Default for CurWidget<U> {
518 fn default() -> Self {
519 Self::new()
520 }
521}
522
523pub(crate) type FileParts<U> = (RwData<File>, <U as Ui>::Area, RwData<Vec<Node<U>>>);