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
#![deny(warnings)]
use std::cmp::min;
use std::marker::PhantomData;
use std::ops::Range;
use tuifw_screen_base::*;
use tuifw_screen_base::Screen as base_Screen;
use unicode_segmentation::UnicodeSegmentation;

pub struct Screen<Error> {
    buf: Vec<(char, Color, Option<Color>, Attr)>,
    out: Vec<(char, Color, Option<Color>, Attr)>,
    size: Vector,
    invalidated: Rect,
    cursor: Option<Point>,
    phantom: PhantomData<Error>,
}

impl<Error> Screen<Error> {
    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,
            phantom: PhantomData,
        };
        s.resize(size);
        s
    }

    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<Error> base_Screen for Screen<Error> {
    type Error = Error;

    fn size(&self) -> Vector { self.size }

    fn out(&mut self, p: Point, fg: Color, bg: Option<Color>, attr: Attr, text: &str, range: Range<i16>) -> Range<i16> {
        assert!(range.start >= 0 && range.end >= range.start);
        let size = self.size;
        if p.y < 0 || p.y >= size.y {
            let n = text
                .graphemes(true)
                .take(range.end as u16 as usize)
                .count() as u16 as i16
            ;
            return min(range.start, n) .. n;
        }
        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 x = p.x;
        let invalidated_l = x;
        let mut skip = 0i16;
        let mut n = 0i16;
        for g in text.graphemes(true).map(|g| g.chars().next().unwrap()).take(range.end as u16 as usize) {
            n += 1;
            let skip = if skip < range.start {
                skip += 1;
                true
            } else {
                false
            };
            if !skip && x >= 0 && x < size.x {
                let col = &mut line[x as u16 as usize];
                *col = (g, fg, bg, attr);
                x += 1;
            } else {
                x = x.saturating_add(1);
            };
        }
        self.invalidated = self.invalidated
            .union(Rect::with_tl_br(Point { x: invalidated_l, y: p.y }, Point { x, y: p.y + 1 }))
            .unwrap().right().unwrap()
            .intersect(Rect { tl: Point { x: 0, y: 0 }, size: self.size })
        ;
        skip .. n
    }

    fn update(&mut self, cursor: Option<Point>, _wait: bool) -> Result<Option<Event>, Self::Error> {
        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)
    }
}