1mod builder;
2mod layout;
3
4use std::{
5 fmt::Debug,
6 marker::PhantomData,
7 sync::{Arc, mpsc},
8};
9
10use crossterm::event::KeyEvent;
11use layout::window_files;
12use parking_lot::RwLock;
13use serde::{Deserialize, Serialize};
14
15pub use self::{
16 builder::{FileBuilder, WindowBuilder},
17 layout::{FileId, Layout, MasterOnLeft, WindowFiles},
18};
19use crate::{
20 DuatError,
21 cache::load_cache,
22 cfg::{IterCfg, PrintCfg},
23 data::{RoData, RwData},
24 form::Painter,
25 text::{Item, Iter, Point, RevIter, Text},
26 widgets::{File, Node, Widget},
27};
28
29pub trait Ui: Sized + Send + Sync + 'static {
32 type MetaStatics: Default + Send + Sync;
33 type Area: Area<Ui = Self> + Clone + PartialEq + Send + Sync;
34
35 fn open(ms: &'static Self::MetaStatics, duat_tx: Sender, ui_rx: mpsc::Receiver<UiEvent>);
41
42 fn close(ms: &'static Self::MetaStatics);
46
47 fn load(ms: &'static Self::MetaStatics);
52
53 fn new_root(ms: &'static Self::MetaStatics, cache: <Self::Area as Area>::Cache) -> Self::Area;
62
63 fn switch_window(ms: &'static Self::MetaStatics, win: usize);
69
70 fn flush_layout(ms: &'static Self::MetaStatics);
76
77 fn unload(ms: &'static Self::MetaStatics);
85
86 fn remove_window(ms: &'static Self::MetaStatics, win: usize);
93}
94
95pub trait Area: Send + Sync + Sized {
100 type Ui: Ui<Area = Self>;
102 type ConstraintChangeErr: std::error::Error + DuatError;
103 type Cache: Default + Serialize + Deserialize<'static> + 'static;
104 type PrintInfo: Default + Clone + Send + Sync;
105
106 fn bisect(
145 &self,
146 specs: PushSpecs,
147 cluster: bool,
148 on_files: bool,
149 cache: Self::Cache,
150 _: DuatPermission,
151 ) -> (Self, Option<Self>);
152
153 fn delete(&self, _: DuatPermission) -> Option<Self>;
157
158 fn swap(&self, other: &Self, _: DuatPermission);
164
165 fn constrain_hor(&self, constraint: Constraint) -> Result<(), Self::ConstraintChangeErr>;
167
168 fn constrain_ver(&self, constraint: Constraint) -> Result<(), Self::ConstraintChangeErr>;
170
171 fn restore_constraints(&self) -> Result<(), Self::ConstraintChangeErr>;
173
174 fn request_width_to_fit(&self, text: &str) -> Result<(), Self::ConstraintChangeErr>;
177
178 fn scroll_around_point(&self, text: &Text, point: Point, cfg: PrintCfg);
183
184 fn set_as_active(&self);
190
191 fn print(&self, text: &mut Text, cfg: PrintCfg, painter: Painter);
195
196 fn print_with<'a>(
197 &self,
198 text: &mut Text,
199 cfg: PrintCfg,
200 painter: Painter,
201 f: impl FnMut(&Caret, &Item) + 'a,
202 );
203
204 fn set_print_info(&self, info: Self::PrintInfo);
208
209 fn print_iter<'a>(
220 &self,
221 iter: Iter<'a>,
222 cfg: IterCfg,
223 ) -> impl Iterator<Item = (Caret, Item)> + Clone + 'a
224 where
225 Self: Sized;
226
227 fn print_iter_from_top<'a>(
228 &self,
229 text: &'a Text,
230 cfg: IterCfg,
231 ) -> impl Iterator<Item = (Caret, Item)> + Clone + 'a
232 where
233 Self: Sized;
234
235 fn rev_print_iter<'a>(
246 &self,
247 iter: RevIter<'a>,
248 cfg: IterCfg,
249 ) -> impl Iterator<Item = (Caret, Item)> + Clone + 'a;
250
251 fn has_changed(&self) -> bool;
261
262 fn is_master_of(&self, other: &Self) -> bool;
267
268 fn get_cluster_master(&self) -> Option<Self>;
274
275 fn cache(&self) -> Option<Self::Cache>;
277
278 fn width(&self) -> u32;
280
281 fn height(&self) -> u32;
283
284 fn first_points(&self, text: &Text, cfg: PrintCfg) -> (Point, Option<Point>);
286
287 fn last_points(&self, text: &Text, cfg: PrintCfg) -> (Point, Option<Point>);
289
290 fn print_info(&self) -> Self::PrintInfo;
292
293 fn is_active(&self) -> bool;
297}
298
299pub struct Window<U: Ui> {
301 nodes: Vec<Node<U>>,
302 files_area: U::Area,
303 layout: Box<dyn Layout<U>>,
304}
305
306impl<U: Ui> Window<U> {
307 pub(crate) fn new<W: Widget<U>>(
309 ms: &'static U::MetaStatics,
310 widget: W,
311 checker: impl Fn() -> bool + Send + Sync + 'static,
312 layout: Box<dyn Layout<U>>,
313 ) -> (Self, Node<U>) {
314 let widget = RwData::<dyn Widget<U>>::new_unsized::<W>(Arc::new(RwLock::new(widget)));
315
316 let cache = if let Some(path) = widget.inspect_as(|file: &File| file.path())
317 && let Some(cache) = load_cache::<<U::Area as Area>::Cache>(path)
318 {
319 cache
320 } else {
321 <U::Area as Area>::Cache::default()
322 };
323
324 let area = U::new_root(ms, cache);
325
326 let node = Node::new::<W>(widget, area.clone(), checker);
327 node.update();
328
329 let window = Self {
330 nodes: vec![node.clone()],
331 files_area: area.clone(),
332 layout,
333 };
334
335 (window, node)
336 }
337
338 pub(crate) fn from_raw(
339 files_area: U::Area,
340 nodes: Vec<Node<U>>,
341 layout: Box<dyn Layout<U>>,
342 ) -> Self {
343 let files_area = files_area.get_cluster_master().unwrap_or(files_area);
344 Self { nodes, files_area, layout }
345 }
346
347 pub(crate) fn push<W: Widget<U>>(
349 &mut self,
350 widget: W,
351 area: &U::Area,
352 checker: impl Fn() -> bool + 'static + Send + Sync,
353 specs: PushSpecs,
354 cluster: bool,
355 ) -> (Node<U>, Option<U::Area>) {
356 let widget = RwData::<dyn Widget<U>>::new_unsized::<W>(Arc::new(RwLock::new(widget)));
357
358 let cache = if let Some(path) = widget.inspect_as::<File, String>(|file| file.path())
359 && let Some(cache) = load_cache::<<U::Area as Area>::Cache>(path)
360 {
361 cache
362 } else {
363 <U::Area as Area>::Cache::default()
364 };
365
366 let on_files = self.files_area.is_master_of(area);
367 let (child, parent) = area.bisect(specs, cluster, on_files, cache, DuatPermission::new());
368
369 self.nodes.push(Node::new::<W>(widget, child, checker));
370 (self.nodes.last().unwrap().clone(), parent)
371 }
372
373 pub(crate) fn push_file(
380 &mut self,
381 mut file: File,
382 checker: impl Fn() -> bool + 'static + Send + Sync,
383 ) -> crate::Result<(Node<U>, Option<U::Area>), ()> {
384 let window_files = window_files(&self.nodes);
385 file.layout_ordering = window_files.len();
386 let (id, specs) = self.layout.new_file(&file, window_files)?;
387
388 let (child, parent) = self.push(file, &id.0, checker, specs, false);
389
390 if let Some(parent) = &parent
391 && id.0 == self.files_area
392 {
393 self.files_area = parent.clone();
394 }
395
396 Ok((child, parent))
397 }
398
399 pub(crate) fn remove_file(&mut self, name: &str) {
401 let Some(node) = self
402 .nodes
403 .extract_if(.., |node| {
404 node.as_file()
405 .is_some_and(|(f, ..)| f.read().name() == name)
406 })
407 .next()
408 else {
409 return;
410 };
411
412 if let Some(parent) = node.area().delete(DuatPermission::new())
416 && parent == self.files_area
417 {
418 let files = self.file_nodes();
419 let (only_file, _) = files.first().unwrap();
420 self.files_area = only_file
421 .area()
422 .get_cluster_master()
423 .unwrap_or(only_file.area().clone())
424 }
425
426 if let Some(related) = node.related_widgets() {
427 let nodes = related.read();
428 for node in self.nodes.extract_if(.., |node| nodes.contains(node)) {
429 node.area().delete(DuatPermission::new());
430 }
431 }
432 }
433
434 pub(crate) fn take_file_and_related_nodes(&mut self, node: &Node<U>) -> Vec<Node<U>> {
436 if let Some(related) = node.related_widgets() {
437 let layout_ordering = node
438 .widget()
439 .inspect_as(|f: &File| f.layout_ordering)
440 .unwrap();
441
442 let nodes = related.read();
443 let nodes = self
444 .nodes
445 .extract_if(.., |n| nodes.contains(n) || n == node)
446 .collect();
447
448 for node in &self.nodes {
449 node.widget().mutate_as(|f: &mut File| {
450 if f.layout_ordering > layout_ordering {
451 f.layout_ordering -= 1;
452 }
453 });
454 }
455
456 nodes
457 } else {
458 Vec::new()
459 }
460 }
461
462 pub(crate) fn insert_file_nodes(&mut self, layout_ordering: usize, nodes: Vec<Node<U>>) {
463 if let Some(i) = self.nodes.iter().position(|node| {
464 node.widget()
465 .inspect_as(|f: &File| f.layout_ordering)
466 .is_some_and(|lo| lo >= layout_ordering)
467 }) {
468 for node in self.nodes[i..].iter() {
469 node.widget()
470 .mutate_as(|f: &mut File| f.layout_ordering += 1);
471 }
472 self.nodes.splice(i..i, nodes);
473 } else {
474 self.nodes.extend(nodes);
475 }
476 }
477
478 pub fn nodes(&self) -> impl ExactSizeIterator<Item = &Node<U>> + DoubleEndedIterator {
479 self.nodes.iter()
480 }
481
482 pub fn file_names(&self) -> Vec<String> {
487 window_files(&self.nodes)
488 .into_iter()
489 .map(|f| f.0.widget().inspect_as(|f: &File| f.name()).unwrap())
490 .collect()
491 }
492
493 pub fn file_paths(&self) -> Vec<String> {
494 window_files(&self.nodes)
495 .into_iter()
496 .map(|f| f.0.widget().inspect_as(|f: &File| f.path()).unwrap())
497 .collect()
498 }
499
500 pub fn file_nodes(&self) -> WindowFiles<U> {
501 window_files(&self.nodes)
502 }
503
504 pub fn len_widgets(&self) -> usize {
505 self.nodes.len()
506 }
507}
508
509#[derive(Debug, Clone, Copy, PartialEq, Eq)]
511pub enum Axis {
512 Horizontal,
513 Vertical,
514}
515
516impl Axis {
517 pub fn perp(&self) -> Self {
518 match self {
519 Axis::Horizontal => Axis::Vertical,
520 Axis::Vertical => Axis::Horizontal,
521 }
522 }
523}
524
525impl From<PushSpecs> for Axis {
526 fn from(value: PushSpecs) -> Self {
527 match value.side {
528 Side::Above | Side::Below => Axis::Vertical,
529 _ => Axis::Horizontal,
530 }
531 }
532}
533
534#[derive(Debug)]
535pub enum DuatEvent {
536 Key(KeyEvent),
537 Resize,
538 FormChange,
539 MetaMsg(Text),
540 ReloadConfig,
541 OpenFile(String),
542 CloseFile(String),
543 SwapFiles(String, String),
544 OpenWindow(String),
545 SwitchWindow(usize),
546 Quit,
547}
548
549pub struct Sender(&'static mpsc::Sender<DuatEvent>);
550
551impl Sender {
552 pub fn new(sender: &'static mpsc::Sender<DuatEvent>) -> Self {
553 Self(sender)
554 }
555
556 pub fn send_key(&self, key: KeyEvent) -> Result<(), mpsc::SendError<DuatEvent>> {
557 self.0.send(DuatEvent::Key(key))
558 }
559
560 pub fn send_reload_config(&self) -> Result<(), mpsc::SendError<DuatEvent>> {
561 self.0.send(DuatEvent::ReloadConfig)
562 }
563
564 pub fn send_resize(&self) -> Result<(), mpsc::SendError<DuatEvent>> {
565 self.0.send(DuatEvent::Resize)
566 }
567
568 pub(crate) fn send_form_changed(&self) -> Result<(), mpsc::SendError<DuatEvent>> {
569 self.0.send(DuatEvent::FormChange)
570 }
571}
572
573pub enum UiEvent {
574 ResumePrinting,
575 PausePrinting,
576 Quit,
577}
578
579pub struct RoWindow<'a, U: Ui>(&'a Window<U>);
580
581impl<U: Ui> RoWindow<'_, U> {
582 pub fn fold_files<B>(&self, init: B, mut f: impl FnMut(B, &File) -> B) -> B {
592 self.0.nodes.iter().fold(init, |accum, node| {
593 if let Some(file) = node.try_downcast::<File>() {
594 f(accum, &file.read())
595 } else {
596 accum
597 }
598 })
599 }
600
601 pub fn fold_widgets<B>(&self, init: B, mut f: impl FnMut(B, &dyn Widget<U>) -> B) -> B {
611 self.0.nodes.iter().fold(init, |accum, node| {
612 let f = &mut f;
613 node.raw_inspect(|widget| f(accum, widget))
614 })
615 }
616}
617
618pub struct RoWindows<U: Ui>(RoData<Vec<Window<U>>>);
619
620impl<U: Ui> RoWindows<U> {
621 pub fn new(windows: RoData<Vec<Window<U>>>) -> Self {
622 RoWindows(windows)
623 }
624
625 pub fn inspect_nth<B>(&self, index: usize, f: impl FnOnce(RoWindow<U>) -> B) -> Option<B> {
626 let windows = self.0.read();
627 windows.get(index).map(|window| f(RoWindow(window)))
628 }
629
630 pub fn try_inspect_nth<B>(&self, index: usize, f: impl FnOnce(RoWindow<U>) -> B) -> Option<B> {
631 self.0
632 .try_read()
633 .and_then(|windows| windows.get(index).map(|window| f(RoWindow(window))))
634 }
635}
636
637#[derive(Debug, Clone, Copy)]
659pub struct PushSpecs {
660 side: Side,
661 ver_con: Option<Constraint>,
662 hor_con: Option<Constraint>,
663}
664
665impl PushSpecs {
666 pub fn left() -> Self {
668 Self {
669 side: Side::Left,
670 ver_con: None,
671 hor_con: None,
672 }
673 }
674
675 pub fn right() -> Self {
677 Self {
678 side: Side::Right,
679 ver_con: None,
680 hor_con: None,
681 }
682 }
683
684 pub fn above() -> Self {
686 Self {
687 side: Side::Above,
688 ver_con: None,
689 hor_con: None,
690 }
691 }
692
693 pub fn below() -> Self {
695 Self {
696 side: Side::Below,
697 ver_con: None,
698 hor_con: None,
699 }
700 }
701
702 pub fn to_left(self) -> Self {
704 Self { side: Side::Left, ..self }
705 }
706
707 pub fn to_right(self) -> Self {
709 Self { side: Side::Right, ..self }
710 }
711
712 pub fn to_above(self) -> Self {
714 Self { side: Side::Above, ..self }
715 }
716
717 pub fn to_below(self) -> Self {
719 Self { side: Side::Below, ..self }
720 }
721
722 pub fn with_ver_len(self, len: f32) -> Self {
723 Self {
724 ver_con: Some(Constraint::Length(len)),
725 ..self
726 }
727 }
728
729 pub fn with_ver_min(self, min: f32) -> Self {
730 Self {
731 ver_con: Some(Constraint::Min(min)),
732 ..self
733 }
734 }
735
736 pub fn with_ver_max(self, max: f32) -> Self {
737 Self {
738 ver_con: Some(Constraint::Max(max)),
739 ..self
740 }
741 }
742
743 pub fn with_ver_ratio(self, den: u16, div: u16) -> Self {
744 Self {
745 ver_con: Some(Constraint::Ratio(den, div)),
746 ..self
747 }
748 }
749
750 pub fn with_hor_len(self, len: f32) -> Self {
751 Self {
752 hor_con: Some(Constraint::Length(len)),
753 ..self
754 }
755 }
756
757 pub fn with_hor_min(self, min: f32) -> Self {
758 Self {
759 hor_con: Some(Constraint::Min(min)),
760 ..self
761 }
762 }
763
764 pub fn with_hor_max(self, max: f32) -> Self {
765 Self {
766 hor_con: Some(Constraint::Max(max)),
767 ..self
768 }
769 }
770
771 pub fn with_hor_ratio(self, den: u16, div: u16) -> Self {
772 Self {
773 hor_con: Some(Constraint::Ratio(den, div)),
774 ..self
775 }
776 }
777
778 pub fn axis(&self) -> Axis {
779 match self.side {
780 Side::Above | Side::Below => Axis::Vertical,
781 Side::Right | Side::Left => Axis::Horizontal,
782 }
783 }
784
785 pub fn comes_earlier(&self) -> bool {
786 matches!(self.side, Side::Left | Side::Above)
787 }
788
789 pub fn ver_constraint(&self) -> Option<Constraint> {
790 self.ver_con
791 }
792
793 pub fn hor_constraint(&self) -> Option<Constraint> {
794 self.hor_con
795 }
796
797 pub fn constraint_on(&self, axis: Axis) -> Option<Constraint> {
798 match axis {
799 Axis::Horizontal => self.hor_con,
800 Axis::Vertical => self.ver_con,
801 }
802 }
803
804 pub fn is_resizable_on(&self, axis: Axis) -> bool {
805 let con = match axis {
806 Axis::Horizontal => self.hor_con,
807 Axis::Vertical => self.ver_con,
808 };
809 matches!(con, Some(Constraint::Min(..) | Constraint::Max(..)) | None)
810 }
811}
812
813#[derive(Debug, Clone, Copy, PartialEq)]
814pub enum Constraint {
815 Ratio(u16, u16),
816 Length(f32),
817 Min(f32),
818 Max(f32),
819}
820
821#[derive(Debug, Clone, Copy, PartialEq, Eq)]
824pub enum Side {
825 Above,
826 Right,
827 Below,
828 Left,
829}
830
831#[derive(Debug, Clone, Copy)]
832pub struct Caret {
833 pub x: u32,
834 pub len: u32,
835 pub wrap: bool,
836}
837
838impl Caret {
839 #[inline(always)]
840 pub fn new(x: u32, len: u32, wrap: bool) -> Self {
841 Self { x, len, wrap }
842 }
843}
844
845pub struct DuatPermission(PhantomData<()>);
848
849impl DuatPermission {
850 pub(crate) fn new() -> Self {
851 Self(PhantomData)
852 }
853}