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);