1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#![deny(warnings)]
#![doc(test(attr(deny(warnings))))]
#![doc(test(attr(allow(dead_code))))]
#![doc(test(attr(allow(unused_variables))))]
use std::any::Any;
use std::cmp::{min, max};
use std::ops::Range;
use tuifw_screen_base::*;
use tuifw_screen_base::Screen as base_Screen;
use unicode_segmentation::UnicodeSegmentation;
pub struct Screen {
buf: Vec<(char, Color, Option<Color>, Attr)>,
out: Vec<(char, Color, Option<Color>, Attr)>,
size: Vector,
invalidated: Rect,
cursor: Option<Point>,
}
impl Screen {
pub fn new(size: Vector) -> Self {
let mut s = Screen {
buf: Vec::new(),
out: Vec::new(),
size: Vector::null(),
invalidated: Rect { tl: Point { x: 0, y: 0 }, size: Vector::null() },
cursor: None,
};
s.resize(size);
s
}
pub fn cursor(&self) -> Option<Point> { self.cursor }
fn resize(&mut self, out_size: Vector) {
self.buf.resize(out_size.rect_area() as usize, (' ', Color::White, None, Attr::empty()));
self.out.resize(out_size.rect_area() as usize, (' ', Color::White, None, Attr::empty()));
self.size = out_size;
self.invalidated = Rect { tl: Point { x: 0, y: 0 }, size: self.size };
}
}
impl base_Screen for Screen {
fn size(&self) -> Vector { self.size }
fn out(
&mut self,
p: Point,
fg: Color,
bg: Option<Color>,
attr: Attr,
text: &str,
hard: Range<i16>,
soft: Range<i16>
) -> Range<i16> {
assert!(p.y >= 0 && p.y < self.size().y);
assert!(hard.start >= 0 && hard.end > hard.start && hard.end <= self.size().x);
assert!(soft.start >= 0 && soft.end > soft.start && soft.end <= self.size().x);
let text_end = if soft.end <= p.x { return 0 .. 0 } else { soft.end.saturating_sub(p.x) };
let text_start = if soft.start <= p.x { 0 } else { soft.start.saturating_sub(p.x) };
let size = self.size;
let line = (p.y as u16 as usize) * (size.x as u16 as usize);
let line = &mut self.buf[line .. line + size.x as u16 as usize];
let mut before_hard_start = min(p.x, hard.start);
let mut before_text_start = 0i16;
let x0 = max(hard.start, p.x);
let mut x = x0;
for g in text.graphemes(true).map(|g| g.chars().next().unwrap()).take(text_end as u16 as usize) {
if x >= hard.end { break; }
let visible_1 = if before_text_start < text_start {
before_text_start += 1;
false
} else {
true
};
let visible_2 = if before_hard_start < hard.start {
before_hard_start += 1;
false
} else {
true
};
if visible_1 && visible_2 {
let col = &mut line[x as u16 as usize];
*col = (g, fg, bg, attr);
}
x += 1;
}
self.invalidated = self.invalidated
.union(Rect::from_tl_br(Point { x: x0, y: p.y }, Point { x, y: p.y + 1 }))
.unwrap().right().unwrap()
;
x0 .. x
}
fn update(&mut self, cursor: Option<Point>, _wait: bool) -> Result<Option<Event>, Box<dyn Any>> {
for y in self.invalidated.t() .. self.invalidated.b() {
let line = (y as u16 as usize) * (self.size.x as u16 as usize);
let s = line + self.invalidated.l() as u16 as usize;
let f = line + self.invalidated.r() as u16 as usize;
(&mut self.out[s .. f]).copy_from_slice(&self.buf[s .. f]);
}
self.invalidated.size = Vector::null();
self.cursor = cursor.and_then(|cursor| {
if (Rect { tl: Point { x: 0, y: 0 }, size: self.size() }).contains(cursor) {
Some(cursor)
} else {
None
}
});
Ok(None)
}
}