1macro_rules! include {
2    ($module: ident) => {
3        mod $module;
4        pub use $module::*;
5    };
6}
7
8include!(backend);
9include!(widget);
10include!(animation);
11include!(shader);
12include!(content_processor);
13include!(style);
14
15mod type_aliases;
16use type_aliases::*;
17
18pub mod preludes;
19pub mod backends;
20pub mod content_processors;
21pub mod widgets;
22pub mod animations;
23pub mod shaders;
24pub mod event;
25
26use std::io;
27
28#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
29pub enum Content {
31    Styled(char, Style),
33    Clear,
35}
36
37#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
38pub struct ContextConfig {
41    pub frame_delay: Option<std::time::Duration>,
46    pub clear_buffer: bool,
48    pub custom_size: Option<Size>,
50    pub relative_printing: bool,
53    pub damaged_only: bool,
56    pub clear_paste: bool,
58    pub allow_screen_tearing: bool,
60}
61
62impl Default for ContextConfig {
63    fn default() -> Self {
64        Self {
65            frame_delay: None,
66            clear_buffer: true,
67            custom_size: None,
68            relative_printing: false,
69            damaged_only: true,
70            clear_paste: true,
71            allow_screen_tearing: false,
72        }
73    }
74}
75
76#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
77pub struct ContextSetupConfig {
80    pub raw_mode: bool,
82    pub alt_screen: bool,
84    pub hide_cursor: bool,
86    pub capture_mouse: bool,
88}
89
90impl Default for ContextSetupConfig {
91    fn default() -> Self {
92        Self {
93            raw_mode: true,
94            alt_screen: true,
95            hide_cursor: true,
96            capture_mouse: false,
97        }
98    }
99}
100
101#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
102pub struct DrawSummary {
103    pub duration: std::time::Duration,
105    pub count: usize,
107    pub drawn_buffer: usize,
109}
110
111#[derive(Debug, Clone)]
112pub struct Context<CT: ContentProcessorOutput, B: Backend<CT>, C: ContentProcessor<CT>, R: Widget> {
116    pub config: ContextConfig,
117    pub backend: B,
118    pub content_processor: C,
119    pub root: R,
120    pub event_state: event::EventState,
121    last_event_state: Option<event::EventState>,
122    filler: Content,
123    refresh_all: bool,
124    setup_config: ContextSetupConfig,
125    has_drawn_before: bool,
126    last_draw: Option<std::time::Instant>,
127    last_size: Option<Size>,
128    buffers: [Buffer; 2],
129    front_buffer: usize,
130    _phantom: std::marker::PhantomData<CT>,
131}
132
133impl<CT: ContentProcessorOutput, B: Backend<CT>, C: ContentProcessor<CT>, R: Widget> Context<CT, B, C, R> {
134    pub fn new(config: ContextConfig, setup_config: ContextSetupConfig, backend: B, content_processor: C, root: R) -> Self {
136        Self {
137            config,
138            backend,
139            content_processor,
140            root,
141            setup_config,
142            has_drawn_before: false,
143            last_draw: None,
144            last_size: None,
145            buffers: [
146                Buffer::new(),
147                Buffer::new(),
148            ],
149            front_buffer: 0,
150            filler: Content::Clear,
151            refresh_all: true,
152            event_state: event::EventState::new(),
153            last_event_state: None,
154            _phantom: std::marker::PhantomData,
155        }
156    }
157
158    pub fn setup(&mut self) -> Result<(), io::Error> {
160        self.set_state(true)?;
161
162        Ok(())
163    }
164
165    pub fn cleanup(&mut self) -> Result<(), io::Error> {
167        self.set_state(false)?;
168
169        if self.setup_config.alt_screen == false {
170            self.backend.clear(ClearType::FromCursorDown)?;
171        }
172
173        Ok(())
174    }
175
176    fn set_state(&mut self, enable: bool) -> Result<(), io::Error> {
177        if self.setup_config.capture_mouse {
178            self.backend.capture_mouse(enable)?;
179        }
180
181        if self.setup_config.alt_screen {
182            self.backend.alt_screen(enable)?;
183        }
184
185        if self.setup_config.raw_mode {
186            self.backend.raw_mode(enable)?;
187        }
188
189        if self.setup_config.hide_cursor {
190            self.backend.show_cursor(!enable)?;
191        }
192
193        Ok(())
194    }
195
196    pub fn last_size(&self) -> Option<Size> {
198        return self.last_size;
199    }
200
201    pub fn last_draw(&self) -> Option<std::time::Instant> {
203        return self.last_draw;
204    }
205
206    pub fn duration_since_last_draw(&self) -> Option<std::time::Duration> {
208        match self.last_draw() {
209            Some(s) => Some(std::time::Instant::now() - s),
210            None => None,
211        }
212    }
213
214    #[inline(always)]
215    fn should_draw(&self) -> bool {
216        let mut should_draw = true;
217
218        if let Some(frame_delay) = self.config.frame_delay {
219            match self.duration_since_last_draw() {
220                Some(s) => {
221                    if s < frame_delay {
222                        should_draw = false;
223                    }
224                },
225                None => (),
226            };
227        }
228
229        return should_draw;
230    }
231
232    #[inline(always)]
233    fn front_buffer_index(&self) -> usize {
234        return self.front_buffer;
235    }
236
237    #[inline(always)]
238    fn back_buffer_index(&self) -> usize {
239        return match self.front_buffer_index() {
240            0 => 1,
241            1 => 0,
242            _ => unreachable!(),
243        }
244    }
245
246    #[inline(always)]
247    fn swap_buffers(&mut self) {
248        self.front_buffer = self.back_buffer_index();
249    }
250
251    pub fn draw(&mut self) -> Result<Option<DrawSummary>, io::Error> {
257        let draw_start_time = std::time::Instant::now();
258
259        let mut draw_count: usize = 0;
260
261        if self.should_draw() == false {
262            return Ok(None);
263        }
264
265        self.swap_buffers();
266
267        let front_index = self.front_buffer_index();
268        let back_index = self.back_buffer_index();
269
270        if self.config.clear_buffer {
271            self.buffers[front_index].clear();
272        }
273
274        let offset = match self.config.relative_printing {
275            true => self.backend.cursor_position()?,
276            false => Position::new(0, 0),
277        };
278
279        let size = match self.config.custom_size {
280            Some(s) => s,
281            None => self.backend.terminal_size()?,
282        } - Size::new(offset.col as u16, offset.row as u16);
283
284        let mut canvas = Canvas::new(
285            Transform::new(
286                Position::zero(),
287                size,
288            ),
289            0,
290            &mut self.buffers[front_index],
291            self.filler.clone(),
292        );
293
294        canvas.transform.size = self.root.widget_info().size_info.correct_size(canvas.transform.size);
295
296        self.root.draw(&mut canvas, Some(&self.event_state_frame()));
297
298        if size != self.last_size.unwrap_or(size) {
299            self.refresh_all = true;
300        }
301
302        if self.config.allow_screen_tearing == false {
303            self.backend.begin_sync_update()?;
304        }
305
306        for row in 0..size.rows {
307            for col in 0..size.cols {
308                let position = Position::new(col as i16, row as i16);
309                let draw_position = position + offset;
310
311                let back_content = self.buffers[back_index].get(position);
312                let front_content = self.buffers[front_index].get(position);
313
314                let should_draw: bool = self.refresh_all
315                    || self.config.damaged_only == false
316                    || front_content != back_content;
317
318                if should_draw {
319                    self.backend.set_cursor_pos(draw_position)?;
320
321                    let content: Content = match self.buffers[front_index].get(position) {
322                        Some(s) => s.clone(),
323                        None => self.filler.clone(),
324                    };
325
326                    self.print(&content, &mut draw_count)?;
327                }
328            }
329        }
330
331        if self.config.allow_screen_tearing == false {
332            self.backend.end_sync_update()?;
333        }
334
335        self.backend.set_cursor_pos(offset)?;
336
337        self.backend.flush()?;
338
339        self.last_draw = Some(std::time::Instant::now());
341        self.last_size = Some(size);
342        self.last_event_state = Some(self.event_state.clone());
343
344        self.refresh_all = false;
346
347        if self.config.clear_paste {
348            self.event_state.terminal.paste = None;
349        }
350
351        self.has_drawn_before = true;
353        let draw_duration = std::time::Instant::now() - draw_start_time;
354        Ok(Some(DrawSummary {
355            duration: draw_duration,
356            count: draw_count,
357            drawn_buffer: front_index,
358        }))
359    }
360
361    #[inline(always)]
362    pub fn event_state_frame(&self) -> event::EventStateFrame {
363        return self.event_state.calculate_frame(self.last_event_state.clone().unwrap_or(self.event_state.clone()));
364    }
365
366    #[inline(always)]
367    pub fn get_last_event_state(&self) -> Option<event::EventState> {
368        return self.last_event_state.clone();
369    }
370
371    #[inline(always)]
372    fn print(&mut self, content: &Content, draw_count: &mut usize) -> Result<(), io::Error> {
373        self.backend.print(match content {
374            Content::Clear => CT::clear_output(),
375            Content::Styled(character, style) => {
376                self.content_processor.process(*character, style)
377            },
378        })?;
379
380        *draw_count += 1;
381
382        Ok(())
383    }
384
385    #[inline(always)]
386    pub fn set_filler(&mut self, content: Content) {
390        if self.filler != content {
391            self.refresh_all = true;
392        }
393
394        self.filler = content;
395    }
396
397    #[inline(always)]
398    pub fn get_filler(&self) -> &Content {
399        return &self.filler;
400    }
401}
402
403#[derive(Debug, Clone, Eq, PartialEq)]
404struct Buffer {
405    map: HashMap<Position, Content>,
406}
407
408impl Buffer {
409    pub fn new() -> Self {
410        Self {
411            map: HashMap::new(),
412        }
413    }
414
415    #[inline(always)]
416    pub fn set(&mut self, position: Position, content: Option<Content>) {
417        match content {
418            Some(s) => self.map.insert(position, s),
419            None => self.map.remove(&position),
420        };
421    }
422
423    #[inline(always)]
424    pub fn get(&self, position: Position) -> Option<&Content> {
425        return self.map.get(&position);
426    }
427
428    #[inline(always)]
429    pub fn clear(&mut self) {
430        self.map = HashMap::new(); }
432}
433
434pub struct Canvas {
439    pub transform: Transform,
441    buffer_pointer: *mut Buffer,
443    set_by_canvas: HashMap<Position, ()>,
445    transform_original: Transform,
447    depth: u32,
449    filler: Content,
451}
452
453impl Canvas {
454    fn new(
456        transform: Transform,
457        depth: u32,
458        buffer_pointer: *mut Buffer,
459        filler: Content,
460    ) -> Self {
461        return Self {
462            transform,
463            buffer_pointer,
464            set_by_canvas: HashMap::new(),
465            transform_original: transform,
466            depth,
467            filler,
468        };
469    }
470
471    #[inline(always)]
472    pub fn new_child(&self, transform: Transform) -> Self {
474        return Self::new(
475            transform.offset_by(self.transform.position),
476            self.depth + 1,
477            self.buffer_pointer,
478            self.filler.clone(),
479        );
480    }
481
482    #[inline(always)]
483    pub fn new_copy_child(&self) -> Self {
485        return self.new_child(Transform::new(Position::zero(), self.transform.size));
486    }
487
488    #[inline(always)]
489    pub fn depth(&self) -> u32 {
491        return self.depth;
492    }
493
494    #[inline(always)]
495    pub fn is_root(&self) -> bool {
497        return self.depth == 0;
498    }
499
500    #[inline(always)]
501    pub fn animate<A: Animation>(
503        &mut self,
504        animation: &mut A,
505        animation_data: &AnimationData,
506        custom_original: Option<Transform>,
507    ) {
508        self.animate_with_offset(animation, animation_data, custom_original, 0.0);
509    }
510
511    #[inline(always)]
512    pub fn animate_with_offset<A: Animation>(
514        &mut self,
515        animation: &mut A,
516        animation_data: &AnimationData,
517        custom_original: Option<Transform>,
518        offset: f64,
519    ) {
520        let original = custom_original.unwrap_or(self.original_transform());
521
522        self.transform = animate(animation, animation_data, original, offset);
523    }
524
525    #[inline(always)]
526    pub fn original_transform(&self) -> Transform {
528        return self.transform_original;
529    }
530
531    #[inline(always)]
532    pub fn is_visible(&self) -> bool {
536        if self.transform.size.cols == 0 || self.transform.size.rows == 0 {
537            return false;
538        }
539
540        return true;
541    }
542
543    #[inline(always)]
544    pub fn changed_at(&self, position: Position) -> bool {
547        return !(self.set_by_canvas.get(&position) == None);
548    }
549
550    #[inline(always)]
551    pub fn get(&self, position: Position) -> Option<&Content> {
553        if self.transform.zero_position().contains_point(position) == false {
554            return None;
555        }
556
557        let real_position = self.transform.position + position;
558
559        let buffer = unsafe { &*self.buffer_pointer };
560
561        return buffer.get(real_position);
562    }
563
564    #[inline(always)]
565    pub fn set(&mut self, position: Position, content: Option<Content>) {
567        if self.transform.zero_position().contains_point(position) == false {
568            return;
569        }
570
571        let real_position = self.transform.position + position;
572        let mut content = content;
573
574        let buffer = unsafe { &mut *self.buffer_pointer };
575
576        if let Some(Content::Styled(_, ref mut style)) = content {
577            let behind_content = buffer.get(real_position).unwrap_or(&self.filler);
578
579            *style = overlay_transparent(style, Some(behind_content), Some(&self.filler));
580        }
581
582        buffer.set(real_position, content);
583        self.set_by_canvas.insert(position, ());
584    }
585}
586
587#[inline(always)]
588fn overlay_transparent(front: &Style, back: Option<&Content>, filler: Option<&Content>) -> Style {
589    let mut style = front.clone();
590
591    macro_rules! style_apply {
592        ($fg_bg: ident, $opposite_fg_bg: ident, $pull_layer: expr, $back: ident) => {
593            if let Some(content) = $back {
594                match content {
595                    Content::Styled(_, b_style) => {
596                        style.$fg_bg = match $pull_layer {
597                            StylePullLayer::Foreground => b_style.fg,
598                            StylePullLayer::Background => b_style.bg,
599                            StylePullLayer::Same => b_style.$fg_bg,
600                            StylePullLayer::Any | StylePullLayer::AnyColor => {
601                                let color_only = $pull_layer == StylePullLayer::AnyColor;
602
603                                if (b_style.$fg_bg.is_final() && color_only == false)
604                                || (b_style.$fg_bg.is_color() && color_only) {
605                                    b_style.$fg_bg
606                                } else {
607                                    if (b_style.$opposite_fg_bg.is_final() && color_only == false)
608                                    || (b_style.$opposite_fg_bg.is_color() && color_only) {
609                                        b_style.$opposite_fg_bg
610                                    } else {
611                                        b_style.$fg_bg
612                                    }
613                                }
614                            },
615                        };
616                    },
617                    Content::Clear => style.$fg_bg = StyleGround::Clear,
618                };
619            }
620        };
621    }
622
623    macro_rules! apply {
624        ($fg_bg: ident, $opposite_fg_bg: ident) => {
625            if style.$fg_bg.is_final() == false {
626                if let StyleGround::Filler(pull_layer) = style.$fg_bg {
627                    style_apply!($fg_bg, $opposite_fg_bg, pull_layer, filler);
628                }
629
630                else if let StyleGround::Transparent(pull_layer) = style.$fg_bg {
631                    style_apply!($fg_bg, $opposite_fg_bg, pull_layer, back);
632                }
633            }
634        };
635    }
636
637    apply!(fg, bg);
638    apply!(bg, fg);
639
640    return style;
641}
642
643pub fn compute_refresh_area(
644    damaged: Option<Transform>,
645    prev_damaged: Option<Transform>,
646    has_drawn_before: bool,
647    full_size: Size,
648) -> Option<Transform> {
649    let last_damaged: Option<Transform> = match (prev_damaged, has_drawn_before) {
650        (None, false) => Some(Transform::new(Position::zero(), full_size)),
651        (l_d, _) => l_d,
652    };
653
654    return match (damaged, last_damaged) {
655        (Some(damaged), Some(last_damaged)) => Some(damaged.combined_area(last_damaged)),
656        (Some(damaged), None) => Some(damaged),
657        (None, Some(last_damaged)) => Some(last_damaged),
658        (None, None) => None,
659    };
660}
661
662#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
663pub struct Transform {
665    pub position: Position,
666    pub size: Size,
667}
668
669impl Transform {
670    pub fn new(position: Position, size: Size) -> Self {
671        Self {
672            position,
673            size,
674        }
675    }
676
677    #[inline(always)]
678    pub fn zero() -> Self {
679        Self::new(Position::zero(), Size::zero())
680    }
681
682    #[inline(always)]
683    pub fn zero_position(&self) -> Self {
685        return Self::new(Position::zero(), self.size);
686    }
687
688    #[inline(always)]
689    pub fn offset_by(&self, offset: Position) -> Self {
691        return Self::new(self.position + offset, self.size);
692    }
693
694    #[inline(always)]
695    pub fn contains_point(&self, position: Position) -> bool {
716        if position.col >= self.position.col
717        && position.row >= self.position.row {
718            if position.col < self.size.cols as i16 + self.position.col
719            && position.row < self.size.rows as i16 + self.position.row {
720                return true;
721            }
722        }
723
724        return false;
725    }
726
727    #[inline(always)]
728    pub fn expand_to_position(&self, position: Position) -> Self {
743        return self.combined_area(Self::new(position, Size::new(1, 1)));
744    }
745
746    #[inline(always)]
747    pub fn combined_area(&self, other: Self) -> Self {
763        let mut total = *self;
764
765        if other.position.row < total.position.row {
766            total.size.rows += (total.position.row - other.position.row) as u16;
767            total.position.row = other.position.row;
768        }
769
770        if other.position.col < total.position.col {
771            total.size.cols += (total.position.col - other.position.col) as u16;
772            total.position.col = other.position.col;
773        }
774
775        if other.size.rows as i16 + other.position.row >= total.size.rows as i16 + total.position.row {
776            total.size.rows = ((other.size.rows as i16 + other.position.row) - total.position.row) as u16;
777        }
778
779        if other.size.cols as i16 + other.position.col >= total.size.cols as i16 + total.position.col {
780            total.size.cols = ((other.size.cols as i16 + other.position.col) - total.position.col) as u16;
781        }
782
783        return total;
784    }
785
786    #[inline(always)]
787    pub fn apply_lerp(&self, b: Self, t: f64, f: fn(f64, f64, f64) -> f64) -> Self {
788        return Self::new(
789            Position {
790                col: f(self.position.col as f64, b.position.col as f64, t) as i16,
791                row: f(self.position.row as f64, b.position.row as f64, t) as i16,
792            },
793            Size {
794                cols: f(self.size.cols as f64, b.size.cols as f64, t) as u16,
795                rows: f(self.size.rows as f64, b.size.rows as f64, t) as u16,
796            },
797        );
798    }
799
800    #[inline(always)]
801    pub fn apply_quadratic_bezier(&self, control: Self, b: Self, t: f64, f: fn(f64, f64, f64, f64) -> f64) -> Self {
802        return Self::new(
803            Position {
804                col: f(self.position.col as f64, control.position.col as f64, b.position.col as f64, t) as i16,
805                row: f(self.position.row as f64, control.position.row as f64, b.position.row as f64, t) as i16,
806            },
807            Size {
808                cols: f(self.size.cols as f64, control.size.cols as f64, b.size.cols as f64, t) as u16,
809                rows: f(self.size.rows as f64, control.size.rows as f64, b.size.rows as f64, t) as u16,
810            },
811        );
812    }
813
814    #[inline(always)]
815    pub fn apply_cubic_bezier(&self, a_control: Self, b: Self, b_control: Self, t: f64, f: fn(f64, f64, f64, f64, f64) -> f64) -> Self {
816        return Self::new(
817            Position {
818                col: f(self.position.col as f64, a_control.position.col as f64, b.position.col as f64, b_control.position.col as f64, t) as i16,
819                row: f(self.position.row as f64, a_control.position.row as f64, b.position.row as f64, b_control.position.row as f64, t) as i16,
820            },
821            Size {
822                cols: f(self.size.cols as f64, a_control.size.cols as f64, b.size.cols as f64, b_control.size.cols as f64, t) as u16,
823                rows: f(self.size.rows as f64, a_control.size.rows as f64, b.size.rows as f64, b_control.size.rows as f64, t) as u16,
824            },
825        );
826    }
827}
828
829#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
830pub struct Size {
832    pub cols: u16,
833    pub rows: u16,
834}
835
836impl Size {
837    pub fn new(cols: u16, rows: u16) -> Self {
838        Self {
839            cols,
840            rows,
841        }
842    }
843
844    #[inline(always)]
845    pub fn zero() -> Self {
846        Self::new(0, 0)
847    }
848
849    #[inline(always)]
850    pub fn same(cols_rows: u16) -> Self {
851        Self::new(cols_rows, cols_rows)
852    }
853
854    #[inline(always)]
855    pub fn area(&self) -> u32 {
857        return (self.cols as u32) * (self.rows as u32);
858    }
859}
860
861#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
862pub struct Position {
864    pub col: i16,
865    pub row: i16,
866}
867
868impl Position {
869    pub fn new(col: i16, row: i16) -> Self {
870        Self {
871            col,
872            row,
873        }
874    }
875
876    #[inline(always)]
877    pub fn zero() -> Self {
878        Self::new(0, 0)
879    }
880
881    #[inline(always)]
882    pub fn same(col_row: i16) -> Self {
883        Self::new(col_row, col_row)
884    }
885}
886
887macro_rules! op_impl_core {
888    ($op: ident, $func: ident, $subfunc: ident) => {
889        impl std::ops::$op for Size {
890            type Output = Self;
891
892            fn $func(self, rhs: Self) -> Self::Output {
893                Self {
894                    cols: self.cols.$subfunc(rhs.cols),
895                    rows: self.rows.$subfunc(rhs.rows),
896                }
897            }
898        }
899
900        impl std::ops::$op for Position {
901            type Output = Self;
902
903            fn $func(self, rhs: Self) -> Self::Output {
904                Self {
905                    col: self.col.$subfunc(rhs.col),
906                    row: self.row.$subfunc(rhs.row),
907                }
908            }
909        }
910
911        impl std::ops::$op for Transform {
912            type Output = Self;
913
914            fn $func(self, rhs: Self) -> Self::Output {
915                Self {
916                    position: self.position.$func(rhs.position),
917                    size: self.size.$func(rhs.size),
918                }
919            }
920        }
921    };
922}
923
924macro_rules! op_impl {
925    ($op: ident, $func: ident) => {
926        op_impl_core!($op, $func, $func);
927    };
928    ($op: ident, $func: ident, $subfunc: ident) => {
929        op_impl_core!($op, $func, $subfunc);
930    };
931}
932
933op_impl!(Add, add, saturating_add);
934op_impl!(Sub, sub, saturating_sub);
935op_impl!(Mul, mul, saturating_mul);
936op_impl!(Div, div, saturating_div);
937op_impl!(Rem, rem);
938op_impl!(Shl, shl);
939op_impl!(Shr, shr);