tuifw_screen_test/
lib.rs

1#![feature(allocator_api)]
2
3#![deny(warnings)]
4#![doc(test(attr(deny(warnings))))]
5#![doc(test(attr(allow(dead_code))))]
6#![doc(test(attr(allow(unused_variables))))]
7
8#![no_std]
9
10extern crate alloc;
11
12use alloc::alloc::Global;
13use alloc::vec::Vec;
14use core::alloc::Allocator;
15use core::iter::repeat;
16use core::cmp::{min, max};
17use core::ops::Range;
18use tuifw_screen_base::*;
19use tuifw_screen_base::Screen as base_Screen;
20use unicode_width::UnicodeWidthChar;
21
22pub struct Screen<A: Allocator + Clone = Global> {
23    buf: Vec<(char, Fg, Bg), A>,
24    out: Vec<(char, Fg, Bg), A>,
25    size: Vector,
26    invalidated: Rect,
27    cursor: Option<Point>,
28    data: Vec<Range<i16>, A>,
29}
30
31impl Screen {
32    pub fn new(size: Vector) -> Self {
33        Self::new_in(size, Global)
34    }
35}
36
37impl<A: Allocator + Clone> Screen<A> {
38    pub fn new_in(size: Vector, alloc: A) -> Self {
39        assert!(size.x >= 0 && size.y >= 0);
40        let mut s = Screen {
41            data: Vec::new_in(alloc.clone()),
42            buf: Vec::new_in(alloc.clone()),
43            out: Vec::new_in(alloc),
44            size: Vector::null(),
45            invalidated: Rect { tl: Point { x: 0, y: 0 }, size: Vector::null() },
46            cursor: None,
47        };
48        s.resize(size);
49        s
50    }
51
52    pub fn cursor(&self) -> Option<Point> { self.cursor }
53
54    fn resize(&mut self, out_size: Vector) {
55        self.data.clear();
56        self.data.resize(usize::from(out_size.y as u16), 0 .. out_size. x);
57        self.buf.resize(out_size.rect_area() as usize, (' ', Fg::LightGray, Bg::None));
58        self.out.resize(out_size.rect_area() as usize, (' ', Fg::LightGray, Bg::None));
59        self.size = out_size;
60        self.invalidated = Rect { tl: Point { x: 0, y: 0 }, size: self.size };
61    }
62}
63
64impl<A: Allocator + Clone> base_Screen for Screen<A> {
65    fn size(&self) -> Vector { self.size }
66
67    fn out(
68        &mut self,
69        p: Point,
70        fg: Fg,
71        bg: Bg,
72        text: &str,
73        hard: Range<i16>,
74        soft: Range<i16>
75    ) -> Range<i16> {
76        assert!(p.y >= 0 && p.y < self.size().y);
77        assert!(hard.start >= 0 && hard.end > hard.start && hard.end <= self.size().x);
78        assert!(soft.start >= 0 && soft.end > soft.start && soft.end <= self.size().x);
79        let text_end = if soft.end <= p.x { return 0 .. 0 } else { soft.end.saturating_sub(p.x) };
80        let text_start = if soft.start <= p.x { 0 } else { soft.start.saturating_sub(p.x) };
81        let size = self.size;
82        let line = (p.y as u16 as usize) * (size.x as u16 as usize);
83        let line = &mut self.buf[line .. line + size.x as u16 as usize];
84        let mut before_hard_start = min(p.x, hard.start);
85        let mut before_text_start = 0i16;
86        let x0 = max(hard.start, p.x);
87        let mut x = x0;
88        let text = text.chars()
89            .filter(|&c| c != '\0' && c.width().is_some())
90            .flat_map(|c| repeat(c).take(c.width().unwrap()))
91        ;
92        for g in text.take(text_end as u16 as usize) {
93            if x >= hard.end { break; }
94            let visible_1 = if before_text_start < text_start {
95                before_text_start += 1;
96                false
97            } else {
98                true
99            };
100            let visible_2 = if before_hard_start < hard.start {
101                before_hard_start += 1;
102                false
103            } else {
104                true
105            };
106            if visible_1 && visible_2 {
107                let col = &mut line[x as u16 as usize];
108                *col = (g, fg, bg);
109            }
110            x += 1;
111        }
112        self.invalidated = self.invalidated
113            .union(Rect::from_tl_br(Point { x: x0, y: p.y }, Point { x, y: p.y + 1 }))
114            .unwrap().right().unwrap()
115        ;
116        x0 .. x
117    }
118
119    fn update(&mut self, cursor: Option<Point>, _wait: bool) -> Result<Option<Event>, Error> {
120        for y in self.invalidated.t() .. self.invalidated.b() {
121            let line = (y as u16 as usize) * (self.size.x as u16 as usize);
122            let s = line + self.invalidated.l() as u16 as usize;
123            let f = line + self.invalidated.r() as u16 as usize;
124            self.out[s .. f].copy_from_slice(&self.buf[s .. f]);
125        }
126        self.invalidated.size = Vector::null();
127        self.cursor = cursor.and_then(|cursor| {
128            if (Rect { tl: Point { x: 0, y: 0 }, size: self.size() }).contains(cursor) {
129                Some(cursor)
130            } else {
131                None
132            }
133        });
134        Ok(None)
135    }
136
137    fn line_invalidated_range(&self, line: i16) -> &Range<i16> { &self.data[usize::from(line as u16)] }
138
139    fn line_invalidated_range_mut(&mut self, line: i16) -> &mut Range<i16> { &mut self.data[usize::from(line as u16)] }
140}