Skip to main content

duat_core/ui/
window.rs

1//! The [`Window`]s of Duat
2//!
3//! In Duat, there is a [`Windows`] struct (accessible via
4//! [`context::windows`]), which holds [`Window`]s. Each `Window`
5//! represents a set of [`Widget`]s that should be displayed at the
6//! same time, like a browser tab.
7//!
8//! These [`Widget`]s will be contained in [`Node`]s, which are an
9//! anonymized version of [`Handle`]s, which the end user interacts
10//! with through the [`Pass`] struct, allowing for massively
11//! desynchronized global state accessibility.
12use std::{
13    iter::Chain,
14    path::Path,
15    sync::{Arc, Mutex},
16};
17
18use super::{Node, Widget, layout::Layout};
19use crate::{
20    buffer::{Buffer, BufferOpts, PathKind},
21    context::{self, Handle},
22    data::{Pass, RwData},
23    hook::{self, BufferClosed, BufferSwitched, BufferUnloaded, WidgetOpened, WindowOpened},
24    mode,
25    session::UiMouseEvent,
26    text::{Text, txt},
27    ui::{Coord, DynSpawnSpecs, PushSpecs, RwArea, SpawnId, StaticSpawnSpecs, Ui},
28};
29
30/// A list of all [`Window`]s in Duat
31pub struct Windows {
32    inner: RwData<InnerWindows>,
33    spawns_to_remove: Mutex<Vec<SpawnId>>,
34    ui: Ui,
35}
36
37/// Inner holder of [`Window`]s
38struct InnerWindows {
39    layout: Box<Mutex<dyn Layout>>,
40    list: Vec<Window>,
41    new_additions: Arc<Mutex<Option<Vec<(usize, Node)>>>>,
42    cur_buffer: RwData<Handle>,
43    cur_widget: RwData<Node>,
44    cur_win: usize,
45    buffer_history: BufferHistory,
46}
47
48impl Windows {
49    /// Initializes the `Windows`, returning a [`Node`] for the first
50    /// [`Buffer`]
51    pub(crate) fn initialize(
52        pa: &mut Pass,
53        buffer: Buffer,
54        layout: Box<Mutex<dyn Layout>>,
55        ui: Ui,
56    ) {
57        let new_additions = Arc::new(Mutex::default());
58        let (window, node) = Window::new(0, pa, ui, buffer, new_additions.clone());
59
60        context::set_windows(Self {
61            inner: RwData::new(InnerWindows {
62                layout,
63                list: vec![window.clone()],
64                new_additions,
65                cur_buffer: RwData::new(node.try_downcast().unwrap()),
66                cur_widget: RwData::new(node.clone()),
67                cur_win: 0,
68                buffer_history: BufferHistory::default(),
69            }),
70            spawns_to_remove: Mutex::new(Vec::new()),
71            ui,
72        });
73
74        hook::trigger(
75            pa,
76            WidgetOpened(node.handle().try_downcast::<Buffer>().unwrap()),
77        );
78        hook::trigger(pa, WindowOpened(window));
79    }
80
81    ////////// Functions for new Widgets
82
83    /// Creates a new list of [`Window`]s, with a main one
84    /// initialiazed
85    pub(crate) fn new_window(&self, pa: &mut Pass, buffer: Buffer) -> Node {
86        let win = self.inner.read(pa).list.len();
87        let new_additions = self.inner.read(pa).new_additions.clone();
88        let (window, node) = Window::new(win, pa, self.ui, buffer, new_additions);
89
90        let inner = self.inner.write(pa);
91        inner.list.push(window);
92
93        hook::trigger(pa, WindowOpened(self.inner.read(pa).list[win].clone()));
94        hook::trigger(
95            pa,
96            WidgetOpened(node.handle().try_downcast::<Buffer>().unwrap()),
97        );
98
99        node
100    }
101
102    /// Push a [`Widget`] to [`Handle`]
103    pub(crate) fn push_widget<W: Widget>(
104        &self,
105        pa: &mut Pass,
106        (target, on_buffers, specs): (&RwArea, Option<bool>, PushSpecs),
107        widget: W,
108        master: Option<&RwArea>,
109    ) -> Option<Handle<W>> {
110        self.push(pa, (target, on_buffers, specs), widget, master)?
111            .handle()
112            .try_downcast()
113    }
114
115    /// Spawn a [`Widget`] on a [`Handle`]
116    ///
117    /// Can fail if the `Handle` in question was already removed.
118    pub(crate) fn spawn_on_widget<W: Widget>(
119        &self,
120        pa: &mut Pass,
121        (target, specs): (&RwArea, DynSpawnSpecs),
122        widget: W,
123    ) -> Option<Handle<W>> {
124        let (win, cluster_master, master) =
125            self.inner
126                .read(pa)
127                .list
128                .iter()
129                .enumerate()
130                .find_map(|(win, window)| {
131                    let inner = window.0.read(pa);
132                    let master = window.nodes(pa).find_map(|node| {
133                        node.area()
134                            .area_is_eq(pa, target)
135                            .then(|| node.handle().clone())
136                    });
137
138                    if inner.master_area.is_master_of(pa, target) {
139                        Some((win, None, master))
140                    } else if let Some((_, node)) = inner
141                        .spawned
142                        .iter()
143                        .find(|(_, node)| node.area().is_master_of(pa, target))
144                    {
145                        Some((win, node.area().get_cluster_master(pa), master))
146                    } else {
147                        None
148                    }
149                })?;
150
151        let widget = RwData::new(widget);
152        let id = SpawnId::new();
153
154        let path = widget
155            .read_as::<Buffer>(pa)
156            .and_then(|buffer| buffer.path_set());
157        let spawned = cluster_master.as_ref().unwrap_or(target).spawn(
158            pa,
159            path.as_ref().map(|p| p.as_ref()),
160            id,
161            specs,
162        )?;
163
164        let node = Node::new(widget, spawned, master);
165
166        let window = self.inner.write(pa).list.remove(win);
167        window.add(pa, node.clone(), None, Location::Spawned(id));
168        self.inner.write(pa).list.insert(win, window);
169
170        hook::trigger(pa, WidgetOpened(node.handle().try_downcast::<W>().unwrap()));
171
172        node.handle().try_downcast()
173    }
174
175    /// Spawns a [`Widget`] on [`Text`]
176    pub(crate) fn spawn_on_text<W: Widget>(
177        &self,
178        pa: &mut Pass,
179        (id, specs): (SpawnId, DynSpawnSpecs),
180        widget: W,
181        win: usize,
182        master: Handle<dyn Widget>,
183    ) -> Handle<W> {
184        let widget = RwData::new(widget);
185        let path = widget
186            .read_as::<Buffer>(pa)
187            .and_then(|buffer| buffer.path_set());
188        let spawned = self
189            .ui
190            .new_dyn_spawned(path.as_ref().map(|p| p.as_ref()), id, specs, win);
191
192        let node = Node::new(widget, spawned, Some(master));
193
194        let window = self.inner.write(pa).list.remove(win);
195        window.add(pa, node.clone(), None, Location::Spawned(id));
196        self.inner.write(pa).list.insert(win, window);
197
198        hook::trigger(pa, WidgetOpened(node.handle().try_downcast::<W>().unwrap()));
199
200        node.handle().try_downcast().unwrap()
201    }
202
203    fn spawn_static<W: Widget>(
204        &self,
205        pa: &mut Pass,
206        (specs, win): (StaticSpawnSpecs, usize),
207        widget: W,
208    ) -> Option<Handle<W>> {
209        let id = SpawnId::new();
210        let widget = RwData::new(widget);
211        let path = widget
212            .read_as::<Buffer>(pa)
213            .and_then(|buffer| buffer.path_set());
214        let spawned = self
215            .ui
216            .new_static_spawned(path.as_ref().map(|p| p.as_ref()), id, specs, win);
217
218        let node = Node::new(widget, spawned, None);
219
220        let window = self.inner.write(pa).list.remove(win);
221        window.add(pa, node.clone(), None, Location::Spawned(id));
222        self.inner.write(pa).list.insert(win, window);
223
224        hook::trigger(pa, WidgetOpened(node.handle().try_downcast::<W>().unwrap()));
225
226        node.handle().try_downcast()
227    }
228
229    /// Pushes a [`Buffer`] to the buffer's parent
230    ///
231    /// This function will push to the edge of `self.buffers_parent`
232    /// This is an area, usually in the center, that contains all
233    /// [`Buffer`]s, and their associated [`Widget`]s,
234    /// with others being at the perifery of this area.
235    pub(crate) fn new_buffer(&self, pa: &mut Pass, buffer: Buffer) -> Node {
236        let inner = self.inner.read(pa);
237        let (handle, specs) = inner.layout.lock().unwrap().new_buffer(pa, &inner.list);
238
239        let specs = PushSpecs { cluster: false, ..specs };
240
241        if let Some(master) = handle.area().get_cluster_master(pa) {
242            self.push(pa, (&master, Some(true), specs), buffer, None)
243                .unwrap()
244        } else {
245            self.push(pa, (&handle.area, Some(true), specs), buffer, None)
246                .unwrap()
247        }
248    }
249
250    /// Pushes a [`Widget`] to the [`Window`]s
251    ///
252    /// May return [`None`] if the [`Area`] was already deleted.
253    fn push<W: Widget>(
254        &self,
255        pa: &mut Pass,
256        (target, on_buffers, mut specs): (&RwArea, Option<bool>, PushSpecs),
257        widget: W,
258        master: Option<&RwArea>,
259    ) -> Option<Node> {
260        let inner = self.inner.read(pa);
261        let win = inner
262            .list
263            .iter()
264            .position(|window| {
265                window.0.read(pa).master_area.is_master_of(pa, target)
266                    || window
267                        .nodes(pa)
268                        .any(|node| node.area().is_master_of(pa, target))
269            })
270            .unwrap();
271
272        let inner_window = inner.list[win].0.read(pa);
273        let target_is_on_buffers = inner_window.buffers_area.is_master_of(pa, target);
274
275        let on_buffers = on_buffers.unwrap_or(target_is_on_buffers) && target_is_on_buffers;
276
277        if target_is_on_buffers && !on_buffers {
278            specs.cluster = false;
279        }
280
281        let location = if on_buffers {
282            Location::OnBuffers
283        } else if let Some((id, _)) = inner_window
284            .spawned
285            .iter()
286            .find(|(_, node)| node.area().area_is_eq(pa, target))
287        {
288            Location::Spawned(*id)
289        } else {
290            Location::Regular
291        };
292
293        let widget = RwData::new(widget);
294        let path = widget
295            .read_as::<Buffer>(pa)
296            .and_then(|buffer| buffer.path_set());
297        let (pushed, parent) =
298            target.push(pa, path.as_ref().map(|p| p.as_ref()), specs, on_buffers)?;
299
300        let master = master.and_then(|area| {
301            self.entries(pa).find_map(|(.., node)| {
302                node.area()
303                    .area_is_eq(pa, area)
304                    .then(|| node.handle().clone())
305            })
306        });
307
308        let node = Node::new(widget, pushed, master);
309
310        let window = self.inner.write(pa).list.remove(win);
311        window.add(pa, node.clone(), parent, location);
312        self.inner.write(pa).list.insert(win, window);
313
314        hook::trigger(pa, WidgetOpened(node.handle().try_downcast::<W>().unwrap()));
315
316        Some(node)
317    }
318
319    ////////// Existing Widget manipulation
320
321    /// Closes a [`Handle`], removing it from the ui
322    pub(crate) fn close<W: Widget + ?Sized>(
323        &self,
324        pa: &mut Pass,
325        handle: &Handle<W>,
326    ) -> Result<(), Text> {
327        let win = self.handle_window(pa, handle)?;
328
329        // If it's a Buffer, swap all buffers ahead, so this one becomes the
330        // last.
331        if let Some(buf_handle) = handle.try_downcast::<Buffer>() {
332            hook::trigger(pa, BufferUnloaded(buf_handle.clone()));
333            hook::trigger(pa, BufferClosed(buf_handle.clone()));
334
335            let buffers_ahead: Vec<Node> = self.inner.read(pa).list[win]
336                .nodes(pa)
337                .filter(|node| {
338                    node.handle().read_as::<Buffer>(pa).is_some_and(|buffer| {
339                        buffer.layout_order > buf_handle.read(pa).layout_order
340                    })
341                })
342                .cloned()
343                .collect();
344
345            for buffer_ahead in buffers_ahead {
346                self.swap(pa, handle, buffer_ahead.handle())?;
347            }
348        }
349
350        // Actually removing the Handle.
351        let mut list = std::mem::take(&mut self.inner.write(pa).list);
352
353        if list[win].close(pa, handle) {
354            list.remove(win);
355            self.ui.remove_window(win);
356            let cur_win = context::current_win_index(pa);
357            if cur_win > win {
358                self.inner.write(pa).cur_win -= 1;
359            }
360        }
361
362        let inner = self.inner.write(pa);
363        inner.list = list;
364        inner.new_additions.lock().unwrap().get_or_insert_default();
365
366        // If this is the active Handle, pick another one to make active.
367        let inner = self.inner.read(pa);
368        if handle == inner.cur_widget.read(pa).handle() || handle == inner.cur_buffer.read(pa) {
369            if let Some(handle) = handle.try_downcast::<Buffer>() {
370                self.inner.write(pa).buffer_history.remove(&handle);
371
372                let entry = self
373                    .inner
374                    .write(pa)
375                    .buffer_history
376                    .jump_by(handle.clone(), -1)
377                    .or_else(|| self.buffers(pa).first().cloned())
378                    .and_then(|handle| {
379                        self.entries(pa).find_map(|(win, node)| {
380                            (*node.handle() == handle).then(|| (win, node.clone()))
381                        })
382                    });
383
384                if let Some((_, node)) = entry {
385                    crate::mode::reset_to(pa, node.handle());
386                } else {
387                    // If there is no previous Buffer, just quit.
388                    context::sender().send(crate::session::DuatEvent::Quit);
389                    return Ok(());
390                }
391            } else {
392                crate::mode::reset_to(pa, &inner.cur_buffer.read(pa).clone());
393            }
394        }
395
396        Ok(())
397    }
398
399    /// Swaps two [`Handle`]'s positions
400    pub(crate) fn swap<W1: Widget + ?Sized, W2: Widget + ?Sized>(
401        &self,
402        pa: &mut Pass,
403        lhs: &Handle<W1>,
404        rhs: &Handle<W2>,
405    ) -> Result<(), Text> {
406        let lhs_win = self.handle_window(pa, lhs)?;
407        let rhs_win = self.handle_window(pa, rhs)?;
408
409        let [lhs_buffer, rhs_buffer] = [lhs.try_downcast::<Buffer>(), rhs.try_downcast()];
410
411        if let [Some(lhs), Some(rhs)] = [lhs_buffer, rhs_buffer] {
412            let lhs_lo = lhs.read(pa).layout_order;
413            let rhs_lo = std::mem::replace(&mut rhs.write(pa).layout_order, lhs_lo);
414            lhs.write(pa).layout_order = rhs_lo
415        }
416
417        let windows = std::mem::take(&mut self.inner.write(pa).list);
418
419        let lhs_nodes = windows[lhs_win].take_with_related_nodes(pa, lhs);
420        windows[rhs_win].insert_nodes(pa, lhs_nodes);
421
422        let rhs_nodes = windows[rhs_win].take_with_related_nodes(pa, rhs);
423        windows[lhs_win].insert_nodes(pa, rhs_nodes);
424
425        let wins = self.inner.write(pa);
426        wins.list = windows;
427        wins.new_additions.lock().unwrap().get_or_insert_default();
428
429        lhs.area().swap(pa, rhs.area());
430
431        let cur_buffer = context::current_buffer(pa);
432        if lhs_win != rhs_win {
433            if *lhs == cur_buffer {
434                self.inner.write(pa).cur_win = lhs_win;
435                self.ui.switch_window(lhs_win);
436            } else if *rhs == cur_buffer {
437                self.inner.write(pa).cur_win = rhs_win;
438                self.ui.switch_window(rhs_win);
439            }
440        }
441
442        Ok(())
443    }
444
445    /// Opens a new [`Buffer`] on a new [`Window`], or moves it there,
446    /// if it is already open
447    pub(crate) fn open_or_move_to_new_window(
448        &self,
449        pa: &mut Pass,
450        pk: PathKind,
451        default_buffer_opts: BufferOpts,
452    ) -> Node {
453        let node = match self.buffer_entry(pa, pk.clone()) {
454            Ok((win, handle)) if self.get(pa, win).unwrap().buffers(pa).len() > 1 => {
455                // Take the nodes in the original Window
456                handle.write(pa).layout_order = 0;
457
458                let nodes = {
459                    let old_window = self.inner.write(pa).list.remove(win);
460                    let nodes = old_window.take_with_related_nodes(pa, &handle.to_dyn());
461                    self.inner.write(pa).list.insert(win, old_window);
462
463                    nodes
464                };
465
466                // Create a new Window Swapping the new root with buffers_area
467                let path = handle.read(pa).path_set();
468                let new_root = self.ui.new_root(path.as_ref().map(|p| p.as_ref()));
469                handle.area().swap(pa, &new_root);
470                let window = Window::new_from_raw(
471                    pa,
472                    win,
473                    handle.area.clone(),
474                    nodes,
475                    self.inner.read(pa).new_additions.clone(),
476                );
477
478                self.inner.write(pa).list.push(window.clone());
479
480                hook::trigger(pa, WindowOpened(window));
481
482                // Swap the Buffers ahead of the swapped new_root
483                let lo = handle.read(pa).layout_order;
484
485                for handle in &self.inner.read(pa).list[win].buffers(pa)[lo..] {
486                    new_root.swap(pa, handle.area());
487                }
488
489                // Delete the new_root, which should be the last "Buffer" in the
490                // list of the original Window.
491                new_root.delete(pa);
492
493                self.inner
494                    .write(pa)
495                    .new_additions
496                    .lock()
497                    .unwrap()
498                    .get_or_insert_default();
499
500                Node::from_handle(handle)
501            }
502            // The Handle in question is already in its own window, so no need
503            // to move it to another one.
504            Ok((.., handle)) => Node::from_handle(handle),
505            Err(_) => self.new_window(pa, Buffer::new(pk.as_path(), default_buffer_opts)),
506        };
507
508        if context::current_buffer(pa).read(pa).path_kind() != pk {
509            mode::reset_to(pa, node.handle());
510        }
511
512        node
513    }
514
515    /// Sets the current active [`Handle`]
516    #[track_caller]
517    pub(crate) fn set_current_node(&self, pa: &mut Pass, node: Node) -> Result<(), Text> {
518        // SAFETY: This Pass is only used when I'm already reborrowing a &mut
519        // Pass, and it is known that it only writes to other types.
520        let internal_pass = &mut unsafe { Pass::new() };
521
522        let win = self.handle_window(pa, node.handle())?;
523        let inner = self.inner.write(pa);
524
525        if let Some(current) = node.try_downcast::<Buffer>() {
526            let former = std::mem::replace(inner.cur_buffer.write(internal_pass), current.clone());
527            inner.buffer_history.insert(former.clone(), current.clone());
528
529            hook::trigger(pa, BufferSwitched((former, current)));
530        }
531
532        let inner = self.inner.write(pa);
533        *inner.cur_widget.write(internal_pass) = node.clone();
534        inner.cur_win = win;
535        self.ui.switch_window(win);
536
537        Ok(())
538    }
539
540    ////////// Spawned Widget cleanup
541
542    /// Adds a [`SpawnId`] to be removed when a [`Pass`] is available
543    pub(crate) fn queue_close_spawned(&self, id: SpawnId) {
544        let mut spawns_to_remove = self.spawns_to_remove.lock().unwrap();
545        if !spawns_to_remove.contains(&id) {
546            spawns_to_remove.push(id)
547        }
548    }
549
550    /// Removes all [`SpawnId`]'s [`Widget`]s which were queued for
551    /// closure
552    pub(crate) fn cleanup_despawned(&self, pa: &mut Pass) {
553        let spawns_to_remove = std::mem::take(&mut *self.spawns_to_remove.lock().unwrap());
554        for id in spawns_to_remove {
555            if let Some((_, node)) = self
556                .iter(pa)
557                .flat_map(|window| &window.0.read(pa).spawned)
558                .find(|(other, _)| *other == id)
559            {
560                self.close(pa, &node.handle().clone()).unwrap();
561            }
562        }
563    }
564
565    ////////// Entry lookup
566
567    /// An entry for a [`Handle`]
568    pub(crate) fn handle_window<W: Widget + ?Sized>(
569        &self,
570        pa: &Pass,
571        handle: &Handle<W>,
572    ) -> Result<usize, Text> {
573        self.entries(pa)
574            .find_map(|(win, node)| (node.handle() == handle).then_some(win))
575            .ok_or_else(|| txt!("The Handle was already closed"))
576    }
577
578    /// An entry for a buffer with the given name
579    pub(crate) fn buffer_entry(&self, pa: &Pass, pk: PathKind) -> Result<(usize, Handle), Text> {
580        self.entries(pa)
581            .find_map(|(win, node)| {
582                (node.read_as(pa).filter(|f: &&Buffer| f.path_kind() == pk))
583                    .and_then(|_| node.try_downcast().map(|handle| (win, handle)))
584            })
585            .ok_or_else(|| txt!("Buffer {pk} not found"))
586    }
587
588    /// An entry for a buffer with the given name
589    pub(crate) fn named_buffer_entry(
590        &self,
591        pa: &Pass,
592        name: &str,
593    ) -> Result<(usize, Handle), Text> {
594        self.entries(pa)
595            .find_map(|(win, node)| {
596                (node.read_as(pa).filter(|f: &&Buffer| f.name() == name))
597                    .and_then(|_| node.try_downcast().map(|handle| (win, handle)))
598            })
599            .ok_or_else(|| txt!("Buffer [buffer]{name}[] not found"))
600    }
601
602    /// An entry for a buffer with the given name
603    pub(crate) fn path_buffer_entry(
604        &self,
605        pa: &Pass,
606        path: &Path,
607    ) -> Result<(usize, Handle), Text> {
608        self.entries(pa)
609            .find_map(|(win, node)| {
610                (node
611                    .read_as(pa)
612                    .filter(|f: &&Buffer| f.path_kind().as_path().is_some_and(|p| p == path)))
613                .and_then(|_| node.try_downcast().map(|handle| (win, handle)))
614            })
615            .ok_or_else(|| txt!("Buffer [buffer]{path}[] not found"))
616    }
617
618    /// An entry for a widget of a specific type
619    ///
620    /// Returns the index of the window, the index of the [`Widget`],
621    /// and the [`Widget`]'s [`Node`]
622    pub(crate) fn node_of<'a, W: Widget>(&'a self, pa: &'a Pass) -> Result<&'a Node, Text> {
623        let handle = context::current_buffer(pa);
624
625        if let Some((handle, _)) = handle.get_related::<W>(pa).next() {
626            self.entries(pa)
627                .find_map(|(.., node)| node.ptr_eq(handle.widget()).then_some(node))
628        } else {
629            let cur_win = self.inner.read(pa).cur_win;
630            let list = &self.inner.read(pa).list;
631            list[cur_win]
632                .nodes(pa)
633                .chain(list[cur_win + 1..].iter().flat_map(|win| win.nodes(pa)))
634                .chain(list[..cur_win].iter().flat_map(|win| win.nodes(pa)))
635                .find(|node| node.data_is::<W>())
636        }
637        .ok_or(txt!(
638            "No widget of type [a]{}[] found",
639            std::any::type_name::<W>()
640        ))
641    }
642
643    ////////// Entry iterators
644
645    /// Iterates over all widget entries, with window indices
646    pub(crate) fn entries<'a>(&'a self, pa: &'a Pass) -> impl Iterator<Item = (usize, &'a Node)> {
647        self.inner
648            .read(pa)
649            .list
650            .iter()
651            .enumerate()
652            .flat_map(|(win, window)| {
653                let inner = window.0.read(pa);
654                inner
655                    .nodes
656                    .iter()
657                    .chain(inner.spawned.iter().map(|(_, node)| node))
658                    .map(move |node| (win, node))
659            })
660    }
661
662    /// Iterates around a specific widget, going forwards
663    pub(crate) fn iter_around<'a>(
664        &'a self,
665        pa: &'a Pass,
666        win: usize,
667        wid: usize,
668    ) -> impl Iterator<Item = (usize, &'a Node)> + 'a {
669        let window_entries =
670            |(w, window): (usize, &'a Window)| window.nodes(pa).map(move |node| (w, node));
671
672        let windows = &self.inner.read(pa).list;
673
674        let prev_len: usize = windows
675            .iter()
676            .take(win)
677            .map(|win| win.len_widgets(pa))
678            .sum();
679
680        windows
681            .iter()
682            .enumerate()
683            .skip(win)
684            .flat_map(window_entries)
685            .skip(wid + 1)
686            .chain(
687                windows
688                    .iter()
689                    .enumerate()
690                    .take(win + 1)
691                    .flat_map(window_entries)
692                    .take(prev_len + wid),
693            )
694    }
695
696    /// Iterates around a specific widget, going backwards
697    pub(crate) fn iter_around_rev<'a>(
698        &'a self,
699        pa: &'a Pass,
700        win: usize,
701        wid: usize,
702    ) -> impl Iterator<Item = (usize, &'a Node)> + 'a {
703        let entries =
704            |(w, window): (usize, &'a Window)| window.nodes(pa).map(move |node| (w, node));
705
706        let windows = &self.inner.read(pa).list;
707
708        let next_len: usize = windows
709            .iter()
710            .skip(win)
711            .map(|win| win.len_widgets(pa))
712            .sum();
713
714        windows
715            .iter()
716            .enumerate()
717            .rev()
718            .skip(windows.len() - (win + 1))
719            .flat_map(move |(w, win)| entries((w, win)).rev().skip(win.len_widgets(pa) - wid))
720            .chain(
721                windows
722                    .iter()
723                    .enumerate()
724                    .rev()
725                    .take(windows.len() - win)
726                    .flat_map(move |(i, win)| entries((i, win)).rev())
727                    .take(next_len - (wid + 1)),
728            )
729    }
730
731    ////////// Buffer switching
732
733    /// Jumps around in the buffer history
734    ///
735    /// This will jump forwards if `number` is positive, backwards
736    /// otherwise.
737    pub fn jump_buffers_by(&self, pa: &mut Pass, jumps: i32) {
738        let current = self.inner.read(pa).cur_buffer.read(pa).clone();
739        if let Some(handle) = self.inner.write(pa).buffer_history.jump_by(current, jumps) {
740            mode::reset_to(pa, &handle);
741        } else {
742            context::warn!("No buffer [a]{jumps}[] jumps away from the current one");
743        }
744    }
745
746    /// Jumps to the last buffer
747    ///
748    /// Calling this repeatedly
749    pub fn last_switched_buffer(&self, pa: &mut Pass) -> Result<Handle, Text> {
750        let current = self.inner.read(pa).cur_buffer.read(pa).clone();
751        if let Some(handle) = self.inner.write(pa).buffer_history.last(current) {
752            mode::reset_to(pa, &handle);
753            Ok(handle)
754        } else {
755            Err(txt!("No last buffer"))
756        }
757    }
758
759    ////////// Querying functions
760
761    /// The bottom right [`Coord`] on the screen
762    ///
763    /// Since the top left coord is `Coord { x: 0.0, y: 0.0 }`, this
764    /// is also the size of the window.
765    pub fn size(&self) -> Coord {
766        self.ui.size()
767    }
768
769    /// The number of open [`Window`]s
770    ///
771    /// Should never be 0, as that is not a valid state of affairs.
772    pub fn len(&self, pa: &Pass) -> usize {
773        self.inner.read(pa).list.len()
774    }
775
776    /// get's the `win`th [`Window`]
777    pub fn get<'a>(&'a self, pa: &'a Pass, win: usize) -> Option<&'a Window> {
778        self.inner.read(pa).list.get(win)
779    }
780
781    /// Iterates through every [`Window`]
782    pub fn iter<'a>(&'a self, pa: &'a Pass) -> std::slice::Iter<'a, Window> {
783        self.inner.read(pa).list.iter()
784    }
785
786    /// Returns an [`Iterator`] over the [`Handle`]s of Duat
787    pub fn handles<'a>(&'a self, pa: &'a Pass) -> impl Iterator<Item = Handle<dyn Widget>> + 'a {
788        self.inner
789            .read(pa)
790            .list
791            .iter()
792            .flat_map(|w| w.nodes(pa).map(|n| n.handle().clone()))
793    }
794
795    /// Returns an [`Iterator`] over the [`Handle`]s of Duat
796    pub fn handles_of<W: Widget>(&self, pa: &Pass) -> Vec<Handle<W>> {
797        self.inner
798            .read(pa)
799            .list
800            .iter()
801            .flat_map(|w| w.nodes(pa).filter_map(|n| n.handle().try_downcast()))
802            .collect()
803    }
804
805    /// Iterates over all [`Handle<Buffer>`]s in Duat
806    pub fn buffers(&self, pa: &Pass) -> Vec<Handle> {
807        self.inner
808            .read(pa)
809            .list
810            .iter()
811            .flat_map(|w| w.buffers(pa))
812            .collect()
813    }
814
815    /// The index of the currently active [`Window`]
816    pub fn current_window(&self, pa: &Pass) -> usize {
817        self.inner.read(pa).cur_win
818    }
819
820    /// The [`RwData`] that points to the currently active [`Buffer`]
821    pub(crate) fn current_buffer<'a>(&'a self, pa: &'a Pass) -> &'a RwData<Handle> {
822        &self.inner.read(pa).cur_buffer
823    }
824
825    /// The [`RwData`] that points to the currently active [`Widget`]
826    pub(crate) fn current_widget<'a>(&'a self, pa: &'a Pass) -> &'a RwData<Node> {
827        &self.inner.read(pa).cur_widget
828    }
829
830    /// Gets the new additions to the [`Windows`]
831    pub(crate) fn get_additions(&self, pa: &mut Pass) -> Option<Vec<(usize, Node)>> {
832        self.inner.write(pa).new_additions.lock().unwrap().take()
833    }
834}
835
836/// A region containing [`Handle`]s
837///
838/// This is like a browser tab, it can contain any number of regular
839/// and spawned [`Handle`]s.
840#[derive(Clone)]
841pub struct Window(RwData<InnerWindow>);
842
843struct InnerWindow {
844    index: usize,
845    nodes: Vec<Node>,
846    spawned: Vec<(SpawnId, Node)>,
847    buffers_area: RwArea,
848    master_area: RwArea,
849    new_additions: Arc<Mutex<Option<Vec<(usize, Node)>>>>,
850}
851
852impl Window {
853    /// Returns a new instance of [`Window`]
854    fn new<W: Widget>(
855        index: usize,
856        pa: &mut Pass,
857        ui: Ui,
858        widget: W,
859        new_additions: Arc<Mutex<Option<Vec<(usize, Node)>>>>,
860    ) -> (Self, Node) {
861        let widget = RwData::new(widget);
862        let path = if let Some(buffer) = widget.write_as::<Buffer>(pa) {
863            buffer.layout_order = get_layout_order();
864            buffer.path_set()
865        } else {
866            None
867        };
868
869        let area = ui.new_root(path.as_ref().map(|p| p.as_ref()));
870        let node = Node::new::<W>(widget, area.clone(), None);
871
872        new_additions
873            .lock()
874            .unwrap()
875            .get_or_insert_default()
876            .push((index, node.clone()));
877
878        let window = Self(RwData::new(InnerWindow {
879            index,
880            nodes: vec![node.clone()],
881            spawned: Vec::new(),
882            buffers_area: area.clone(),
883            master_area: area.clone(),
884            new_additions,
885        }));
886
887        (window, node)
888    }
889
890    /// Returns a new [`Window`] from raw elements
891    pub(crate) fn new_from_raw(
892        pa: &mut Pass,
893        index: usize,
894        master_area: RwArea,
895        nodes: Vec<Node>,
896        new_additions: Arc<Mutex<Option<Vec<(usize, Node)>>>>,
897    ) -> Self {
898        let master_area = master_area
899            .get_cluster_master(pa)
900            .unwrap_or(master_area.clone());
901
902        Self(RwData::new(InnerWindow {
903            index,
904            nodes,
905            spawned: Vec::new(),
906            buffers_area: master_area.clone(),
907            master_area,
908            new_additions,
909        }))
910    }
911
912    ////////// Widget addition/removal
913
914    /// Pushes a [`Widget`] around the "buffer area" of a `Window`
915    ///
916    /// When you push a `Widget`, it is placed on an edge of the
917    /// area, and a new parent area may be created to hold both
918    /// widgets. If created, that new area will be used for pushing
919    /// widgets in the future.
920    ///
921    /// This means that, if you push widget *B* to the bottom, then
922    /// you push widget *A* to the left, you will get this layout:
923    ///
924    /// ```text
925    /// ╭───┬───────────╮
926    /// │   │           │
927    /// │ A │  Buffers  │
928    /// │   │           │
929    /// ├───┴───────────┤
930    /// │       B       │
931    /// ╰───────────────╯
932    /// ```
933    ///
934    /// The `Buffers` region here represents a central area that all
935    /// `Window`s contain, where all the [`Buffer`]s are placed
936    /// alongside their [satellite `Widget`s]. When you push `Widget`s
937    /// to the `Window`, instead of to a [`Handle`], those widgets are
938    /// placed in the outer region, not being associated with any
939    /// particular `Buffer`.
940    ///
941    /// In this case, each `Window` will have a [`LogBook`] on the
942    /// left side as well as [`FooterWidgets`] on the bottom.
943    ///
944    /// [`Buffer`]: crate::buffer::Buffer
945    /// [`LogBook`]: https://docs.rs/duat/latest/duat/widgets/struct.LogBook.html
946    /// [`FooterWidgets`]: https://docs.rs/duat/latest/duat/widgets/struct.FooterWidgets.html
947    /// [`WindowOpened`]: crate::hook::WindowOpened
948    /// [satellite `Widget`s]: context::Handle::push_outer_widget
949    pub fn push_inner<W: Widget>(
950        &self,
951        pa: &mut Pass,
952        widget: W,
953        mut specs: PushSpecs,
954    ) -> Handle<W> {
955        let target = self.0.read(pa).buffers_area.clone();
956
957        specs.cluster = false;
958
959        context::windows()
960            .push_widget(pa, (&target, Some(false), specs), widget, None)
961            .unwrap()
962    }
963
964    /// Pushes a [`Widget`] to the edges of a `Window`
965    ///
966    /// When you push a `Widget`, it is placed on an edge of the
967    /// area, and a new parent area may be created to hold both
968    /// widgets. If created, that new area will be used for pushing
969    /// widgets in the future.
970    ///
971    /// This means that, if you push widget *B* to the bottom, then
972    /// you push widget *A* to the left, you will get this layout:
973    ///
974    /// ```text
975    /// ╭───┬───────────╮
976    /// │   │           │
977    /// │   │  Buffers  │
978    /// │ A │           │
979    /// │   ├───────────┤
980    /// │   │     B     │
981    /// ╰───┴───────────╯
982    /// ```
983    ///
984    /// The `Buffers` region here represents a central area that all
985    /// `Window`s contain, where all the [`Buffer`]s are placed
986    /// alongside their [satellite `Widget`s]. When you push `Widget`s
987    /// to the `Window`, instead of to a [`Handle`], those widgets are
988    /// placed in the outer region, not being associated with any
989    /// particular `Buffer`.
990    ///
991    /// In this case, each `Window` will have a [`LogBook`] on the
992    /// left side as well as [`FooterWidgets`] on the bottom.
993    ///
994    /// [`Buffer`]: crate::buffer::Buffer
995    /// [`LogBook`]: https://docs.rs/duat/latest/duat/widgets/struct.LogBook.html
996    /// [`FooterWidgets`]: https://docs.rs/duat/latest/duat/widgets/struct.FooterWidgets.html
997    /// [`WindowOpened`]: crate::hook::WindowOpened
998    /// [satellite `Widget`s]: context::Handle::push_outer_widget
999    pub fn push_outer<W: Widget>(
1000        &self,
1001        pa: &mut Pass,
1002        widget: W,
1003        mut specs: PushSpecs,
1004    ) -> Handle<W> {
1005        let target = self.0.read(pa).master_area.clone();
1006
1007        specs.cluster = false;
1008
1009        context::windows()
1010            .push_widget(pa, (&target, Some(false), specs), widget, None)
1011            .unwrap()
1012    }
1013
1014    /// Spawns a new static [`Widget`] on this `Window`
1015    ///
1016    /// This `Widget`, unlike all other kinds, does not follow changes
1017    /// in the layout by the resizing or closure of other `Widget`s.
1018    /// It instead stays in a single [`Coord`], its width and height
1019    /// being predefined.
1020    ///
1021    /// There is one circumstance in which this `Widget` will move,
1022    /// however: when the window resizes. In this circumstance, the
1023    /// `Widget` will be relocated as to be placed in relatively the
1024    /// same place on screen, in relation to the bottom right corner
1025    /// of the screen.
1026    ///
1027    /// [`Coord`]: super::Coord
1028    pub fn spawn<W: Widget>(&self, pa: &mut Pass, widget: W, specs: StaticSpawnSpecs) -> Handle<W> {
1029        context::windows()
1030            .spawn_static(pa, (specs, self.0.read(pa).index), widget)
1031            .unwrap()
1032    }
1033
1034    /// Adds a [`Widget`] to the list of widgets of this [`Window`]
1035    fn add(&self, pa: &mut Pass, node: Node, parent: Option<RwArea>, location: Location) {
1036        match location {
1037            Location::OnBuffers => {
1038                self.0.write(pa).nodes.push(node.clone());
1039                if let Some(parent) = &parent
1040                    && parent.is_master_of(pa, &self.0.read(pa).buffers_area)
1041                {
1042                    self.0.write(pa).buffers_area = parent.clone()
1043                }
1044            }
1045            Location::Regular => self.0.write(pa).nodes.push(node.clone()),
1046            Location::Spawned(id) => self.0.write(pa).spawned.push((id, node.clone())),
1047        }
1048
1049        if let Some(parent) = &parent
1050            && parent.is_master_of(pa, &self.0.read(pa).master_area)
1051        {
1052            self.0.write(pa).master_area = parent.clone()
1053        }
1054
1055        let inner = self.0.write(pa);
1056        inner
1057            .new_additions
1058            .lock()
1059            .unwrap()
1060            .get_or_insert_default()
1061            .push((inner.index, node.clone()));
1062    }
1063
1064    /// Closes the [`Handle`] and all related ones
1065    ///
1066    /// Returns `true` if this `Window` is supposed to be removed.
1067    fn close<W: Widget + ?Sized>(&self, pa: &mut Pass, handle: &Handle<W>) -> bool {
1068        let handle_eq = |node: &mut Node| node.handle() == handle;
1069
1070        let inner = self.0.write(pa);
1071
1072        let node = if let Some(node) = inner.nodes.extract_if(.., handle_eq).next() {
1073            node
1074        } else if let Some((_, node)) = inner.spawned.extract_if(.., |(_, n)| handle_eq(n)).next() {
1075            node
1076        } else {
1077            unreachable!("This isn't supposed to fail");
1078        };
1079
1080        node.handle().declare_closed(pa);
1081
1082        let (do_rm_window, rm_areas) = node.area().delete(pa);
1083        if do_rm_window {
1084            for handle in self.handles(pa).cloned().collect::<Vec<_>>() {
1085                handle.declare_closed(pa);
1086            }
1087            return true;
1088        }
1089
1090        let (mut nodes, mut spawned) = {
1091            let inner = self.0.write(pa);
1092            let nodes = std::mem::take(&mut inner.nodes);
1093            let spawned = std::mem::take(&mut inner.spawned);
1094            (nodes, spawned)
1095        };
1096
1097        nodes.retain(|node| {
1098            if rm_areas
1099                .iter()
1100                .any(|a| a.area_is_eq(pa, node.handle().area()))
1101            {
1102                node.handle().declare_closed(pa);
1103                false
1104            } else {
1105                true
1106            }
1107        });
1108        spawned.retain(|(_, node)| {
1109            if rm_areas
1110                .iter()
1111                .any(|a| a.area_is_eq(pa, node.handle().area()))
1112            {
1113                node.handle().declare_closed(pa);
1114                false
1115            } else {
1116                true
1117            }
1118        });
1119
1120        let inner = self.0.write(pa);
1121        inner.nodes = nodes;
1122        inner.spawned = spawned;
1123
1124        let buffers = self.buffers(pa);
1125        if buffers.len() == 1 {
1126            let handle = buffers.first().unwrap();
1127
1128            let master_area = handle
1129                .area()
1130                .get_cluster_master(pa)
1131                .unwrap_or(handle.area.clone());
1132
1133            self.0.write(pa).buffers_area = master_area;
1134        }
1135
1136        if self.buffers(pa).is_empty() {
1137            for handle in self.handles(pa).cloned().collect::<Vec<_>>() {
1138                handle.declare_closed(pa);
1139            }
1140            true
1141        } else {
1142            false
1143        }
1144    }
1145
1146    /// Takes all [`Node`]s related to a given [`Node`]
1147    fn take_with_related_nodes<W: Widget + ?Sized>(
1148        &self,
1149        pa: &mut Pass,
1150        handle: &Handle<W>,
1151    ) -> Vec<Node> {
1152        let related = handle.related();
1153
1154        let (related, inner) = pa.write_many((related, &self.0));
1155
1156        inner
1157            .nodes
1158            .extract_if(.., |node| {
1159                related.iter().any(|(handle, _)| handle == node.handle()) || node.handle() == handle
1160            })
1161            .collect()
1162    }
1163
1164    /// Inserts [`Buffer`] nodes orderly
1165    fn insert_nodes(&self, pa: &mut Pass, nodes: Vec<Node>) {
1166        self.0.write(pa).nodes.extend(nodes);
1167    }
1168
1169    ////////// Querying functions
1170
1171    /// An [`Iterator`] over the [`Node`]s in a [`Window`]
1172    pub(crate) fn nodes<'a>(&'a self, pa: &'a Pass) -> Nodes<'a> {
1173        let inner = self.0.read(pa);
1174
1175        let spawned = SpawnedNodes { iter: inner.spawned.iter() };
1176
1177        Nodes {
1178            iter: inner.nodes.iter().chain(spawned),
1179            len: inner.nodes.len() + inner.spawned.len(),
1180            taken: 0,
1181        }
1182    }
1183
1184    /// An [`Iterator`] over all [`Handle`]s in a `Window`
1185    ///
1186    /// Each `Handle` takes care of one [`Widget`], if you want to see
1187    /// which one exactly is being used, you can use the
1188    /// [`Handle::try_downcast`] and [`Handle::read_as`] methods.
1189    ///
1190    /// If you just want an iterator over the [`Buffer`]s, then check
1191    /// out [`Window::buffers`].
1192    pub fn handles<'a>(&'a self, pa: &'a Pass) -> impl Iterator<Item = &'a Handle<dyn Widget>> {
1193        self.nodes(pa).map(|node| node.handle())
1194    }
1195
1196    /// The [`Buffer`]s in a single `Window`
1197    ///
1198    /// They will be ordered by where the `"next-buffer"` [command]
1199    /// focus on. This usually follows the order by which they were
1200    /// created, but it can be altered if you use the `"swap"`
1201    /// command.
1202    ///
1203    /// If you want an [`Iterator`] over all [`Handle`]s, see
1204    /// [`Window::handles`].
1205    ///
1206    /// [command]: crate::cmd
1207    pub fn buffers(&self, pa: &Pass) -> Vec<Handle> {
1208        let inner = self.0.read(pa);
1209        let mut buffers: Vec<Handle> = inner
1210            .nodes
1211            .iter()
1212            .filter_map(|node| node.try_downcast())
1213            .collect();
1214
1215        buffers.sort_unstable_by_key(|buffer| buffer.read(pa).layout_order);
1216
1217        buffers.extend(
1218            inner
1219                .spawned
1220                .iter()
1221                .filter_map(|(_, node)| node.try_downcast()),
1222        );
1223
1224        buffers
1225    }
1226
1227    pub(crate) fn send_mouse_event(&self, pa: &mut Pass, mouse_event: UiMouseEvent) {
1228        let inner = self.0.read(pa);
1229        let node = inner
1230            .spawned
1231            .iter()
1232            .rev()
1233            .map(|(_, node)| node)
1234            .chain(&inner.nodes)
1235            .find(|node| {
1236                let tl = node.handle().area().top_left(pa);
1237                let br = node.handle().area().bottom_right(pa);
1238                (tl.x <= mouse_event.coord.x && tl.y <= mouse_event.coord.y)
1239                    && (mouse_event.coord.x < br.x && mouse_event.coord.y < br.y)
1240            })
1241            .cloned();
1242
1243        if let Some(node) = node {
1244            node.on_mouse_event(pa, mouse_event);
1245        }
1246    }
1247
1248    /// How many [`Widget`]s are in this [`Window`]
1249    pub(crate) fn len_widgets(&self, pa: &Pass) -> usize {
1250        let inner = self.0.read(pa);
1251        inner.nodes.len() + inner.spawned.len()
1252    }
1253}
1254
1255#[derive(Default)]
1256struct BufferHistory {
1257    current_i: usize,
1258    list: Vec<Handle>,
1259    last: Option<Handle>,
1260}
1261
1262impl BufferHistory {
1263    /// Returns the previous [`Handle`], if there is one
1264    fn jump_by(&mut self, current: Handle, by: i32) -> Option<Handle> {
1265        let new_i = self
1266            .current_i
1267            .saturating_add_signed(by as isize)
1268            .min(self.list.len());
1269        let new_handle = self.list.get(new_i)?.clone();
1270
1271        self.last = Some(current);
1272        self.current_i = new_i;
1273        Some(new_handle)
1274    }
1275
1276    /// Returns the last [`Handle`]
1277    ///
1278    /// Repeatedly calling this function will return the same two
1279    /// [`Handle`]s.
1280    fn last(&mut self, current: Handle) -> Option<Handle> {
1281        if let Some(last) = self.last.as_mut() {
1282            Some(std::mem::replace(last, current))
1283        } else if let Some(last) = self.list.get(self.current_i.checked_sub(1)?) {
1284            self.last = Some(current);
1285            Some(last.clone())
1286        } else {
1287            None
1288        }
1289    }
1290
1291    /// Inserts a new entry, but only if it is different from both of
1292    /// the surrounding entries
1293    fn insert(&mut self, current: Handle, handle: Handle) {
1294        if self
1295            .current_i
1296            .checked_sub(1)
1297            .is_none_or(|prev_i| self.list[prev_i] != handle)
1298            && self
1299                .list
1300                .get(self.current_i)
1301                .is_none_or(|other| *other != handle)
1302        {
1303            self.list.insert(self.current_i, handle);
1304            self.last = Some(current);
1305            self.current_i += 1;
1306        }
1307    }
1308
1309    /// Clears all instances of a given [`Handle`], signaling that it
1310    /// has been closed
1311    fn remove(&mut self, handle: &Handle) {
1312        let mut i = 0;
1313        self.list.retain(|other| {
1314            if other == handle {
1315                if i < self.current_i {
1316                    self.current_i -= 1;
1317                }
1318                false
1319            } else {
1320                i += 1;
1321                true
1322            }
1323        });
1324    }
1325}
1326
1327/// Returns a new layout order, which will be different from every
1328/// other
1329fn get_layout_order() -> usize {
1330    use std::sync::atomic::{AtomicUsize, Ordering};
1331
1332    static LAYOUT_ORDER: AtomicUsize = AtomicUsize::new(0);
1333    LAYOUT_ORDER.fetch_add(1, Ordering::Relaxed)
1334}
1335
1336enum Location {
1337    OnBuffers,
1338    Regular,
1339    Spawned(SpawnId),
1340}
1341
1342#[derive(Debug, Clone)]
1343pub(crate) struct Nodes<'a> {
1344    iter: Chain<std::slice::Iter<'a, Node>, SpawnedNodes<'a>>,
1345    len: usize,
1346    taken: usize,
1347}
1348
1349impl<'a> Iterator for Nodes<'a> {
1350    type Item = &'a Node;
1351
1352    fn next(&mut self) -> Option<Self::Item> {
1353        let next = self.iter.next();
1354        self.taken += next.is_some() as usize;
1355        next
1356    }
1357
1358    fn size_hint(&self) -> (usize, Option<usize>) {
1359        (self.len - self.taken, Some(self.len - self.taken))
1360    }
1361}
1362
1363impl<'a> DoubleEndedIterator for Nodes<'a> {
1364    fn next_back(&mut self) -> Option<Self::Item> {
1365        let next = self.iter.next_back();
1366        self.taken += next.is_some() as usize;
1367        next
1368    }
1369}
1370
1371impl ExactSizeIterator for Nodes<'_> {}
1372
1373#[derive(Debug, Clone)]
1374struct SpawnedNodes<'a> {
1375    iter: std::slice::Iter<'a, (SpawnId, Node)>,
1376}
1377
1378impl<'a> Iterator for SpawnedNodes<'a> {
1379    type Item = &'a Node;
1380
1381    fn next(&mut self) -> Option<Self::Item> {
1382        self.iter.next().map(|(_, node)| node)
1383    }
1384}
1385
1386impl DoubleEndedIterator for SpawnedNodes<'_> {
1387    fn next_back(&mut self) -> Option<Self::Item> {
1388        self.iter.next_back().map(|(_, node)| node)
1389    }
1390}